Skip to main content

caixa_core/
manifest.rs

1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4use tatara_lisp::DeriveTataraDomain;
5
6use thiserror::Error;
7
8use crate::{
9    CaixaKind, Dep,
10    behavior::BehaviorSpec,
11    dep::DepError,
12    limits::LimitsSpec,
13    render::{
14        PathShapeViolation, is_computeunit_yaml_extension, is_git_repo_url, is_lisp_extension,
15        is_sandboxed_relative_path,
16    },
17    supervisor::SupervisorSpec,
18    upgrade::UpgradeFromEntry,
19};
20
21/// Top-level manifest for a caixa (a tatara-lisp package).
22///
23/// Authored as `caixa.lisp`:
24///
25/// ```lisp
26/// (defcaixa
27///   :nome        "pangea-tatara-aws"
28///   :versao      "0.1.0"
29///   :kind        Biblioteca
30///   :edicao      "2026"
31///   :descricao   "AWS provider caixa for tatara-lisp"
32///   :repositorio "github:pleme-io/pangea-tatara-aws"
33///   :licenca     "MIT"
34///   :autores     ("pleme-io")
35///   :etiquetas   ("iac" "aws" "pangea")
36///   :deps        ((:nome "caixa-teia"    :versao "^0.1")
37///                 (:nome "iac-forge-ir"  :versao "^0.5"))
38///   :deps-dev    ((:nome "tatara-check"  :versao "*"))
39///   :bibliotecas ("lib/pangea-tatara-aws.lisp"))
40/// ```
41///
42/// Because `Caixa` derives [`tatara_lisp::domain::TataraDomain`], the manifest
43/// is parsed directly by the tatara-lisp compiler — an ill-formed manifest is
44/// a compile error, not a runtime error.
45#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone, PartialEq)]
46#[serde(rename_all = "camelCase")]
47#[tatara(keyword = "defcaixa")]
48pub struct Caixa {
49    /// Package name — the canonical string used in `:deps`, the registry, and
50    /// the default lib/exe entry names.
51    pub nome: String,
52
53    /// Package version — a semver literal like `"0.1.0"`. Parsed lazily via
54    /// [`crate::CaixaVersion::parse`].
55    pub versao: String,
56
57    /// What this caixa produces. See [`CaixaKind`].
58    pub kind: CaixaKind,
59
60    /// Language edition — determines macro surface + compatibility flags.
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub edicao: Option<String>,
63
64    /// Free-form description shown in the registry listing.
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub descricao: Option<String>,
67
68    /// Homepage or repo URL.
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub repositorio: Option<String>,
71
72    /// SPDX license expression — `"MIT"`, `"Apache-2.0 OR MIT"`, etc.
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub licenca: Option<String>,
75
76    /// Authors — free-form strings.
77    #[serde(default)]
78    pub autores: Vec<String>,
79
80    /// Topical tags used for registry search.
81    #[serde(default)]
82    pub etiquetas: Vec<String>,
83
84    /// Runtime dependencies.
85    #[serde(default)]
86    pub deps: Vec<Dep>,
87
88    /// Development-only dependencies (tests, lint, bench).
89    #[serde(default)]
90    pub deps_dev: Vec<Dep>,
91
92    /// Paths to executable entry points (relative to the package root).
93    /// Required when `:kind Binario`.
94    #[serde(default)]
95    pub exe: Vec<String>,
96
97    /// Paths to library entry points (relative to the package root).
98    /// First entry is the canonical `lib/<nome>.lisp`; when omitted under
99    /// `:kind Biblioteca`, the layout check expects `lib/<nome>.lisp`.
100    #[serde(default)]
101    pub bibliotecas: Vec<String>,
102
103    /// Paths to service manifests (relative to the package root).
104    /// Required when `:kind Servico`.
105    #[serde(default)]
106    pub servicos: Vec<String>,
107
108    // ── M2 typed-substrate extensions per theory/ABSORPTION-ROADMAP.md ──
109    //
110    // All four are optional + default to "absent"; existing caixas
111    // round-trip unchanged. Each maps onto a prior-art primitive named
112    // in theory/INSPIRATIONS.md:
113    //
114    //   :limits        — Lunatic per-process limits (§III.1)
115    //   :behavior      — OTP gen_server callbacks  (§II.3)
116    //   :upgrade-from  — OTP appup migration       (§II.4)
117    //   :estrategia    — OTP supervisor strategy   (§II.2 + §III.2)
118    //   :children      — OTP supervisor children    (§II.2 + §III.2)
119    //
120    // The supervisor slots are flat on Caixa (vs nested under a
121    // SupervisorSpec sub-form) to keep tatara-lisp authoring at one
122    // level of nesting; SupervisorSpec exists for validation +
123    // composition convenience (`Caixa::supervisor_view()`).
124    /// Lunatic-style per-process resource limits. None = unbounded.
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub limits: Option<LimitsSpec>,
127
128    /// OTP-shaped behavior callbacks for Servico-kind caixas.
129    /// Authored as `(:on-init "..." :on-call "..." …)`.
130    #[serde(default, skip_serializing_if = "Option::is_none")]
131    pub behavior: Option<BehaviorSpec>,
132
133    /// OTP appup — declarative upgrade instructions per prior version.
134    /// Empty list = no hot-upgrade path declared (caller falls back to
135    /// `:Restart` strategy).
136    #[serde(default)]
137    pub upgrade_from: Vec<UpgradeFromEntry>,
138
139    /// OTP supervisor strategy. Required when `:kind Supervisor`;
140    /// ignored otherwise.
141    #[serde(default, skip_serializing_if = "Option::is_none")]
142    pub estrategia: Option<crate::supervisor::RestartStrategy>,
143
144    /// Max restarts before the supervisor itself fails. Defaults via
145    /// SupervisorSpec at validation time.
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub max_restarts: Option<u32>,
148
149    /// Sliding window for `max_restarts`. Authored as a duration
150    /// string (`"60s"`, `"5m"`).
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub restart_window: Option<String>,
153
154    /// Static children of a supervisor. Required for OneForOne /
155    /// OneForAll / RestForOne; must be empty for SimpleOneForOne.
156    #[serde(default)]
157    pub children: Vec<crate::supervisor::ChildSpec>,
158
159    // ── M3 Aplicacao slots (theory/MESH-COMPOSITION.md) ─────────────────
160    //
161    // Required when :kind Aplicacao; ignored otherwise.
162    // Composed into a typed AplicacaoSpec via Caixa::aplicacao_view().
163    /// Member Servicos that make up this Aplicacao. Each is a
164    /// caixa-name + version-constraint pair. Required for Aplicacao.
165    #[serde(default)]
166    pub membros: Vec<crate::aplicacao::Membro>,
167
168    /// WIT-typed inter-Servico contracts. Each `:de` and `:para`
169    /// must reference a name in `:membros`.
170    #[serde(default)]
171    pub contratos: Vec<crate::aplicacao::WitContract>,
172
173    /// Mesh-level policies (timeout, retries, circuit-breaker, mTLS,
174    /// rate-limit). Apply to every contrato unless overridden per-edge
175    /// in M4.
176    #[serde(default, skip_serializing_if = "Option::is_none")]
177    pub politicas: Option<crate::aplicacao::MeshPolicy>,
178
179    /// Placement strategy across the cluster fleet
180    /// (single-node | replicated | sharded).
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub placement: Option<crate::aplicacao::Placement>,
183
184    /// External entry point — gateway / ingress shape. Optional;
185    /// only for public Aplicacaos.
186    #[serde(default, skip_serializing_if = "Option::is_none")]
187    pub entrada: Option<crate::aplicacao::Entrada>,
188
189    // ── Acao slot (CANTEIRO §7.1-C) ──────────────────────────────────────
190    //
191    // Required when :kind Acao; ignored otherwise (mirrors the M2/
192    // supervisor-tree/M3 slot triads above — a declared-but-foreign `:ci`
193    // is a `LayoutError::CiOnNonAcao` build error, not a silent drop).
194    /// Typed CI run — a repo's CI run as a set of typed nodes + their
195    /// dependency edges. Required for `:kind Acao`; validated (not
196    /// rendered) by the `caixa-actions` renderer via
197    /// `canteiro_types::decompose`. See `caixa-actions`' crate docs for
198    /// the M0 validate-only contract.
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub ci: Option<canteiro_types::CiRun>,
201}
202
203/// Why reading a manifest into a [`Caixa`] failed.
204///
205/// Split from [`ManifestError`] (which reports a *parsed* manifest that is
206/// semantically wrong) because the two answer different questions, and the
207/// distinction is the whole point of this type: `ManifestError` means "your
208/// caixa is wrong", `LeituraError::DialetoEstrangeiro` means "this file is not
209/// a caixa".
210#[derive(Debug, thiserror::Error)]
211pub enum LeituraError {
212    /// The source is not readable as a `(defcaixa …)` package manifest — bad
213    /// syntax, a wrong head symbol, an unknown or mistyped slot.
214    ///
215    /// `#[source]`, not `#[error(transparent)]`. Transparent delegates
216    /// `source()` past the inner error to ITS source, which drops the
217    /// `LispError` off the cause chain — and `feira`'s
218    /// `load_caixa_parse_error_preserves_underlying_lisp_error_on_chain`
219    /// pins that a caller can `downcast_ref::<tatara_lisp::LispError>()`
220    /// through an anyhow context to read the typed payload. That pin caught
221    /// this exact regression when the variant first landed transparent.
222    #[error("{0}")]
223    Leitura(
224        #[source]
225        #[from]
226        tatara_lisp::LispError,
227    ),
228
229    /// The source IS a well-formed `(defcaixa …)` form, but of a different
230    /// declaration than this crate's.
231    ///
232    /// The variant that did not exist before, and whose absence is the defect.
233    /// A `(defcaixa :name "x" :ecosystem :go …)` used to reach the derive's
234    /// `parse_kwargs_strict` and come back as an unknown-keyword rejection —
235    /// byte-identical in shape to a typo in a real manifest. Measured over the
236    /// org checkout on 2026-07-31, that shape is the MAJORITY of the corpus, so
237    /// the confusing error was also the common one.
238    ///
239    /// Carrying the dialect means a consumer can branch on "not mine" without
240    /// re-parsing, and a census can count it. Every user-facing byte-string
241    /// (canonical keyword, one-line description, consuming crate) is a
242    /// projection of [`crate::dialeto::CaixaDialeto`] — the variant stores the
243    /// typed dialect and the `#[error]` template calls
244    /// [`CaixaDialeto::palavra_canonica`] /
245    /// [`CaixaDialeto::descricao`] / [`CaixaDialeto::consumidor`] on it, so
246    /// the three axes cannot silently diverge from the classification. Prior
247    /// to this closure the variant carried each accessor's return value as a
248    /// stored `&'static str` snapshot alongside `dialeto`, and the sole
249    /// constructor at [`Caixa::from_lisp`] filled all four fields — a caller
250    /// could construct `DialetoEstrangeiro { dialeto: Molde,
251    /// palavra_canonica: "defcaixa", … }` and every downstream consumer
252    /// (Display, ad-hoc audit, future JSON serialization) would silently
253    /// disagree with `dialeto.palavra_canonica() == "defmolde"`. The typed
254    /// enum owns the projections; the variant only carries the axis.
255    #[error(
256        "this is a `{palavra}` declaration ({desc}), read by \
257         {cons} — not a caixa-core package manifest. `defcaixa` is the \
258         tatara-lisp package manifest (`:nome :versao :kind :deps …`); the two \
259         are different declarations that shared one keyword until 2026-07-31",
260        palavra = dialeto.palavra_canonica(),
261        desc = dialeto.descricao(),
262        cons = dialeto.consumidor()
263    )]
264    DialetoEstrangeiro {
265        /// Which declaration this actually is. Sole authoritative axis;
266        /// every user-facing projection routes through
267        /// [`crate::dialeto::CaixaDialeto`]'s typed accessors so the four
268        /// axes cannot silently disagree.
269        dialeto: crate::dialeto::CaixaDialeto,
270    },
271
272    /// Not a manifest declaration at all.
273    #[error(transparent)]
274    Dialeto(#[from] crate::dialeto::DialetoError),
275}
276
277/// Substrate-canonical universal-axis per-[`Caixa`] `:licenca` SPDX-shaped
278/// license-expression fallback for the `Option<String>` `:licenca` slot —
279/// the `"MIT"` SPDX identifier every [`caixa-helm`]-rendered
280/// `lareira-<nome>` Helm chart's `README.md` `## License` section folds an
281/// author-omitted (`None`) `:licenca` slot through, extracted as a typed
282/// `pub const` so every substrate-side consumer that resolves "what license
283/// scalar does an author-omitted `:licenca` degrade onto?" reaches for
284/// exactly one substrate-primitive `&'static str`.
285///
286/// The `:licenca` fallback axis has one production consumer today — the
287/// [`caixa-helm`] `build_readme` fold at `caixa-helm/src/lib.rs`'s
288/// `caixa.licenca().unwrap_or(CAIXA_LICENCA_DEFAULT)` `README.md`
289/// `## License` section body — with three sibling caixa-core sites that
290/// cite the `"MIT"` fallback in prose (this crate's [`Caixa::licenca`]
291/// accessor's docstring, [`Self::validate_licenca`]'s docstring, and the
292/// [`ManifestError::LicencaEmpty`] `#[error]` template's user-facing text)
293/// all quoting the exact byte-string a future substrate-side rebrand of the
294/// fallback (a tightening to `"Apache-2.0"` as the substrate absorbs the
295/// wasm-component-model conventions the `wasi:*` WIT worlds already carry,
296/// a per-cluster license-default overlay the M4 CR materializer resolves
297/// per-CR, a promotion to the plain `Option<String>` byte-string into a
298/// richer `SpdxExpression` enum once the SPDX-expression parser lands per
299/// [`Self::validate_licenca`]'s docstring roadmap) would silently split
300/// against — the caixa-helm renderer would emit the new byte, the
301/// docstrings would still cite the prior byte, and every author who reads
302/// the accessor docstring before authoring would file a fresh
303/// `:licenca "MIT"` verbatim rather than defer to the substrate default,
304/// with the drift surfacing at chart-README-audit time far from the
305/// substrate rebrand commit.
306///
307/// Prior to this lift the sole production emitter (`build_readme`) carried
308/// an inline `"MIT"` byte literal at
309/// `caixa-helm/src/lib.rs:1018`'s `.unwrap_or("MIT")` fallback arm — one
310/// occurrence of the same load-bearing per-`Caixa` universal-axis
311/// SPDX-shaped license-expression convention as the four sibling caixa-core
312/// docstring citations, drift-prone by construction ahead of the second
313/// occurrence the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
314/// materializer's per-Aplicacao registry-annotation synthesis (the
315/// [`Self::validate_licenca`] roadmap already names the `Chart.yaml
316/// annotations["artifacthub.io/license"]` axis every registry-facing chart
317/// carries as the second consumer) will surface.
318///
319/// The `"MIT"` value pins the canonical CAIXA-SDLC §I license scaffold
320/// every `feira init`-emitted [`Self::template`] carries verbatim
321/// (`:licenca "MIT"`) and every substrate-side renderer fixture
322/// ([`caixa-helm`]'s `sample_caixa`, [`caixa-flux`]'s renderer fixtures,
323/// [`caixa-mesh`]'s renderer fixtures) seeds by construction, matching the
324/// pleme-io repo `LICENSE` header this workspace itself ships under. The
325/// alternatives an author declares explicitly (compound SPDX expressions
326/// like `"Apache-2.0 OR MIT"`, permissive-family peers like
327/// `"Apache-2.0"` / `"BSD-3-Clause"`, license-with-exception forms like
328/// `"Apache-2.0 WITH LLVM-exception"`) express deliberate license postures
329/// an author declares explicitly, never a posture an author-omitted slot
330/// should silently assume by default.
331///
332/// Lifted as a typed `pub const` so the substrate's chosen license
333/// fallback has exactly one source of truth on the `:licenca` fallback
334/// axis, on the same substrate-primitive lift discipline the peer
335/// per-`Caixa` load-bearing-scalar constants
336/// ([`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`],
337/// [`crate::version::DEFAULT_GIT_REMOTE`],
338/// [`crate::version::DEFAULT_PLEME_GIT_ORG`]) already carry on the sibling
339/// per-`Caixa` universal-axis publish-side convention surface, and the
340/// same discipline the sibling M2 per-supervisor default set carries
341/// end-to-end ([`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`],
342/// [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`],
343/// [`crate::supervisor::SUPERVISOR_RESTART_WINDOW_DEFAULT`],
344/// [`crate::supervisor::SUPERVISOR_CHILD_RESTART_DEFAULT`]) and the M3
345/// per-`:placement` default set already carries
346/// ([`crate::aplicacao::PLACEMENT_ESTRATEGIA_DEFAULT`]) on the paired
347/// M2 / M3 typed-slot-default axes. First typed default on the outer
348/// top-level [`Caixa`] universal-axis surface to converge onto the
349/// substrate-primitive-lift discipline the M2 / M3 typed-slot families
350/// already carry.
351pub const CAIXA_LICENCA_DEFAULT: &str = "MIT";
352
353impl Caixa {
354    /// Parse a `caixa.lisp` source string to a typed `Caixa`.
355    ///
356    /// Classifies the dialect **before** parsing. A `(defcaixa …)` of another
357    /// declaration is [`LeituraError::DialetoEstrangeiro`], naming what it is
358    /// and who reads it, instead of an unknown-keyword rejection that reads as
359    /// "your manifest is broken".
360    ///
361    /// The ordering is load-bearing. Handing a foreign dialect to the derive
362    /// first and interpreting the failure afterwards would mean guessing from
363    /// an error message, and the guess would be wrong for every file whose
364    /// first unknown slot happens to be one both schemas could plausibly carry.
365    pub fn from_lisp(src: &str) -> Result<Self, LeituraError> {
366        use tatara_lisp::domain::TataraDomain;
367        let forms = tatara_lisp::read(src).map_err(LeituraError::Leitura)?;
368        let first = forms.first().ok_or(crate::dialeto::DialetoError::Vazio)?;
369
370        // Route the foreign-dialect rejection gate through the lifted
371        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
372        // typed predicate rather than the pre-lift hand-rolled three-arm
373        // `match { Pacote => {}, Desconhecido => {}, foreign => Err(…) }`
374        // literal — the `defmolde` declaration-family partition (the two-
375        // arity closure of [`crate::dialeto::CaixaDialeto::Molde`] and
376        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two arms
377        // whose sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
378        // projection already collapses onto `"defmolde"` and whose sibling
379        // [`crate::dialeto::CaixaDialeto::consumidor`] projection already
380        // collapses onto `"pleme-doc-gen"`) resolves through one dispatch
381        // on the substrate primitive. `Pacote` (the tatara-lisp package
382        // manifest this derive can parse) and `Desconhecido` (deliberately
383        // falls through to the derive rather than short-circuiting: a
384        // `(defcaixa …)` matching neither schema is most likely a genuine
385        // package manifest with a typo in `:nome`, and the derive's
386        // diagnostic — which names the offending keyword and suggests the
387        // nearest slot — is far better than anything this classifier
388        // could say) both return `false` from `is_molde_family()` and fall
389        // through to the derive. Only the typed dialect flows into the
390        // error — the three user-facing projections (canonical keyword,
391        // description, consumer) are read at Display time through
392        // [`crate::dialeto::CaixaDialeto`]'s own accessors, so the
393        // variant cannot carry a snapshot that drifts from
394        // [`crate::dialeto::CaixaDialeto::palavra_canonica`] /
395        // `descricao` / `consumidor`. A future fifth dialect the
396        // [`crate::dialeto`] module doc's "third dialect" hazard
397        // actualises that belongs to the `defmolde` family lands one
398        // match arm at [`crate::dialeto::CaixaDialeto::is_molde_family`]
399        // and this gate picks up the new arm by construction — the pre-
400        // lift wildcard `foreign =>` was compile-time-anonymous and would
401        // silently absorb any hypothetical fifth `defcaixa`-family arm as
402        // foreign; routing the partition through the typed predicate
403        // closes both drift surfaces.
404        let dialeto = crate::dialeto::classify_form(first)?;
405        if dialeto.is_molde_family() {
406            return Err(LeituraError::DialetoEstrangeiro { dialeto });
407        }
408
409        Self::compile_from_sexp(first).map_err(LeituraError::Leitura)
410    }
411
412    /// Register `Caixa` with the global tatara-lisp domain registry so
413    /// `defcaixa` is dispatchable from any tatara-lisp binary that seeds
414    /// the registry (e.g. `tatara-check`).
415    ///
416    /// Returns the typed [`tatara_lisp::KeywordCollision`] on the second
417    /// (and every subsequent) call in the same process — one keyword,
418    /// one type, per process is a hard invariant of the upstream
419    /// registry, and a caller that hits it must fix its crate graph
420    /// rather than swallowing the error. Peer of the sibling per-crate
421    /// `register()` entry points at `caixa-flake/src/flake.rs`,
422    /// `caixa-fmt/src/lisp_config.rs`, `caixa-lacre/src/lock.rs`,
423    /// `caixa-lint/src/lisp_config.rs`, `caixa-resolver/src/lisp_config.rs`
424    /// — every substrate crate that owns a tatara-lisp keyword now
425    /// propagates the same typed error verbatim, so a downstream binary
426    /// that seeds the registry (`tatara-check`, the future LSP) reaches
427    /// for one shape at every call site.
428    ///
429    /// # Errors
430    ///
431    /// [`tatara_lisp::KeywordCollision`] when a peer type has already
432    /// claimed the `defcaixa` keyword in this process.
433    pub fn register() -> Result<(), tatara_lisp::KeywordCollision> {
434        tatara_lisp::domain::register::<Self>()
435    }
436
437    /// Substrate-canonical per-`Caixa` `:licenca` SPDX-expression scalar
438    /// accessor every consumer of the top-level manifest's license axis
439    /// keys off — returns the author-declared `:licenca` byte-string
440    /// verbatim as an `Option<&str>`, borrowed from the typed slot's own
441    /// `Option<String>` storage. `None` when the slot is absent (the
442    /// canonical "omit to defer to the caixa-helm renderer's `MIT`
443    /// fallback" shape [`Self::validate_licenca`] documents at
444    /// caixa-core/src/manifest.rs:1560; the peer [`caixa-helm`]
445    /// `build_readme` fold at caixa-helm/src/lib.rs:962 reads this
446    /// predicate too, so an authored-but-unset `:licenca` round-trips to
447    /// a rendered `lareira-<nome>` chart's `README.md` `## License`
448    /// section structurally identical to one that omits the slot).
449    ///
450    /// The `:licenca` slot carries the universal-axis SPDX-expression
451    /// license identifier every kind of caixa emits under (CAIXA-SDLC
452    /// §I — the author-facing surface every `defcaixa` form supplies) —
453    /// the typed slot's `Option<String>` accept-set (empty-string
454    /// rejected through [`ManifestError::LicencaEmpty`], SPDX-alphabet-
455    /// invalid rejected through [`ManifestError::LicencaInvalid`]) maps
456    /// onto the `lareira-<nome>` Helm chart's `README.md` `## License`
457    /// section (caixa-helm/src/lib.rs:962) and (through future
458    /// tightening documented at [`Self::validate_licenca`]) the
459    /// Chart.yaml `annotations["artifacthub.io/license"]` axis every
460    /// registry-facing chart carries. Every downstream consumer that
461    /// reads the license byte-string keys off this scalar (the
462    /// [`Self::validate_licenca`] empty-arm + SPDX-shape gate that
463    /// routes through `self.licenca.as_deref()`, the caixa-helm
464    /// `build_readme` `unwrap_or_else(|| "MIT".into())` fold that keys
465    /// the fallback off the `Option::is_none()` arm, every future
466    /// per-`Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
467    /// acknowledges).
468    ///
469    /// Prior to this lift the `.licenca` field was accessed inline at
470    /// two production sites — [`Self::validate_licenca`]'s
471    /// `self.licenca.as_deref()` empty-and-shape gate binding and the
472    /// caixa-helm `build_readme` `caixa.licenca.clone().unwrap_or_else(||
473    /// "MIT".into())` `README.md` `## License` fold — two open-coded
474    /// field-accesses that expressed no compile-time link back to the
475    /// typed slot. A future extension of the `:licenca` axis to a
476    /// richer author surface — a per-`:licenca` structured SPDX
477    /// expression parser + license-id allowlist (the future tightening
478    /// [`Self::validate_licenca`]'s docstring acknowledges), a
479    /// per-cluster license-default overlay the M4 CR materializer
480    /// resolves per-CR (the "cluster policy pins `Apache-2.0` for every
481    /// unlisted caixa" arm), a promotion of the plain
482    /// `Option<String>` byte-string to a richer `SpdxExpression` enum
483    /// once the SPDX-expression parser lands — would have had to be
484    /// threaded through both open-coded copies in lockstep or the
485    /// validate gate and the caixa-helm emit path would silently
486    /// disagree on which license a given [`Caixa`] resolves to (an
487    /// author's `:licenca "MIT OR Apache-2.0"` would satisfy validate
488    /// while the emit path silently rendered a stale `MIT` fallback,
489    /// or vice versa). Lifting the resolution to a typed method on the
490    /// substrate primitive means every downstream consumer of the
491    /// caixa's per-`Caixa` license surface reaches for exactly one
492    /// typed dispatch — the resolver's accept-set migrates as a unit
493    /// on any future axis addition.
494    ///
495    /// First `Option<&str>`-return top-level [`Caixa`] scalar accessor —
496    /// opens the "outer [`Caixa`] `Option<&str>` scalar" projection
497    /// pattern the sibling per-`Caixa` `:descricao` / `:repositorio` /
498    /// `:edicao` future lifts fold on. Same "one typed dispatch on the
499    /// substrate primitive, thin projections at each consumer"
500    /// discipline the peer per-`:placement` [`crate::aplicacao::Placement::shard_key`]
501    /// (7cd2a28) / [`crate::aplicacao::Placement::affinity`] (74ec2d3)
502    /// / per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
503    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
504    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
505    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
506    /// typed-slot atom axes, extended here to the outer top-level
507    /// `Caixa` universal-axis surface. Named `licenca()` to match the
508    /// storage field's name; the accessor's identity maps onto the
509    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
510    /// carries.
511    #[must_use]
512    pub const fn licenca(&self) -> Option<&str> {
513        match &self.licenca {
514            Some(s) => Some(s.as_str()),
515            None => None,
516        }
517    }
518
519    /// Substrate-canonical per-`Caixa` `:repositorio` git-repo-URL scalar
520    /// accessor every consumer of the top-level manifest's homepage /
521    /// source-of-truth axis keys off — returns the author-declared
522    /// `:repositorio` byte-string verbatim as an `Option<&str>`, borrowed
523    /// from the typed slot's own `Option<String>` storage. `None` when
524    /// the slot is absent (the canonical "omit to defer to the renderer's
525    /// per-target placeholder" shape — [`caixa-helm`]'s `ChartYaml.home`
526    /// carries the `Option<String>` through verbatim so an author-omitted
527    /// `:repositorio` renders a `Chart.yaml` without a `home:` field
528    /// (`skip_serializing_if = "Option::is_none"`), while [`caixa-flux`]'s
529    /// `ClusterBundleOpts::for_caixa` folds the omitted slot through a
530    /// `format!("https://github.com/{DEFAULT_PLEME_GIT_ORG}/{nome}")`
531    /// fallback derived from `caixa.nome`).
532    ///
533    /// The `:repositorio` slot carries the universal-axis git-repo-URL
534    /// homepage identifier every kind of caixa emits under (CAIXA-SDLC
535    /// §I — the author-facing surface every `defcaixa` form supplies) —
536    /// the typed slot's `Option<String>` accept-set (empty-string
537    /// rejected through [`ManifestError::RepositorioEmpty`], git-repo-URL-
538    /// shape-invalid rejected through [`ManifestError::RepositorioInvalid`]
539    /// past the shared [`crate::render::is_git_repo_url`] predicate the
540    /// peer per-`:deps :fonte :repo` axis also routes through) maps onto
541    /// four load-bearing downstream consumers:
542    ///
543    ///   - [`Self::validate_repositorio`]'s empty-arm + shape-predicate
544    ///     gate binding at caixa-core/src/manifest.rs:1456 — the
545    ///     universal-axis identity gate wired at caixa-build time.
546    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.home` fold at
547    ///     caixa-helm/src/lib.rs:840 — the rendered `lareira-<nome>`
548    ///     Helm chart's `Chart.yaml` `home:` field, which every registry
549    ///     that ingests the chart (ArtifactHub, chartmuseum,
550    ///     `helm search repo`) surfaces as the chart's canonical source-
551    ///     of-truth link.
552    ///   - [`caixa-helm`]'s `build_readme` `## Source` fold at
553    ///     caixa-helm/src/lib.rs:957 — the rendered `lareira-<nome>`
554    ///     chart's `README.md` header link back to the source repo,
555    ///     which every author who inspects the rendered chart bundle
556    ///     lands at.
557    ///   - [`caixa-flux`]'s `ClusterBundleOpts::for_caixa`
558    ///     `GitRepository.spec.url` fold at caixa-flux/src/lib.rs:2006 —
559    ///     the rendered `GitRepository` CR's `spec.url` field, which
560    ///     FluxCD's `source-controller` polls to reconcile the caixa's
561    ///     manifest bundle from git.
562    ///
563    /// Prior to this lift the `.repositorio` field was accessed inline
564    /// at four production sites — [`Self::validate_repositorio`]'s
565    /// `self.repositorio.as_deref()` empty-and-shape gate binding, the
566    /// caixa-helm `build_chart_yaml` `caixa.repositorio.clone()`
567    /// `Chart.yaml` `home:` field fold, the caixa-helm `build_readme`
568    /// `caixa.repositorio.clone().unwrap_or_else(|| caixa.nome.clone())`
569    /// `README.md` `## Source` fold, and the caixa-flux
570    /// `ClusterBundleOpts::for_caixa`
571    /// `caixa.repositorio.clone().unwrap_or_else(|| format!(...))`
572    /// `GitRepository.spec.url` fold — four open-coded field-accesses
573    /// that expressed no compile-time link back to the typed slot. A
574    /// future extension of the `:repositorio` axis to a richer author
575    /// surface — a per-`:repositorio` structured
576    /// [`crate::render::GitRepoUrl`]-shaped scheme+host+path parse
577    /// (the future tightening [`Self::validate_repositorio`]'s
578    /// docstring anticipates alongside the peer per-`:deps :fonte
579    /// :repo` axis), a per-cluster repo-mirror overlay the M4 CR
580    /// materializer resolves per-CR (the "cluster policy rewrites
581    /// `github:pleme-io/...` to `git.internal/mirror/pleme-io/...`"
582    /// arm the private-registry story acknowledges), a promotion of
583    /// the plain `Option<String>` byte-string to a richer
584    /// `RepoUrl` enum discriminated on scheme — would have had to be
585    /// threaded through all four open-coded copies in lockstep or the
586    /// validate gate and the three emit paths would silently disagree
587    /// on which URL a given [`Caixa`] resolves to (an author's
588    /// `:repositorio "github:pleme-io/checkout"` would satisfy validate
589    /// while one of the emit paths silently rendered a stale URL, or
590    /// vice versa). Lifting the resolution to a typed method on the
591    /// substrate primitive means every downstream consumer of the
592    /// caixa's per-`Caixa` repo-URL surface reaches for exactly one
593    /// typed dispatch — the resolver's accept-set migrates as a unit on
594    /// any future axis addition.
595    ///
596    /// Second outer top-level [`Caixa`] `Option<&str>`-return scalar
597    /// accessor — sibling of [`Self::licenca`] (6d5bc28), the accessor
598    /// that opened the "outer [`Caixa`] `Option<&str>` scalar"
599    /// projection pattern this lift folds on. Same "one typed dispatch
600    /// on the substrate primitive, thin projections at each consumer"
601    /// discipline the peer per-`:placement`
602    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
603    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
604    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
605    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
606    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
607    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
608    /// typed-slot atom axes, extended here to the second outer top-level
609    /// `Caixa` universal-axis surface. Named `repositorio()` to match
610    /// the storage field's name; the accessor's identity maps onto the
611    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
612    /// carries.
613    #[must_use]
614    pub const fn repositorio(&self) -> Option<&str> {
615        match &self.repositorio {
616            Some(s) => Some(s.as_str()),
617            None => None,
618        }
619    }
620
621    /// Substrate-canonical per-`Caixa` **resolved-git-repo-URL** composer —
622    /// returns the caixa's canonical git-source-of-truth URL as an owned
623    /// [`String`], author-declared `:repositorio` byte-string verbatim on
624    /// the `Some` arm and the substrate's canonical pleme-org github URL
625    /// fallback ([`crate::DEFAULT_PLEME_GIT_ORG`] and [`Self::nome`]
626    /// interpolated into `https://github.com/<org>/<nome>`) on the
627    /// `None` arm. Every substrate-side consumer that resolves
628    /// "which git URL does this caixa's source live at?" reaches for
629    /// exactly one typed dispatch on the substrate primitive — the raw
630    /// `caixa.repositorio().map(str::to_owned).unwrap_or_else(|| format!(
631    /// "https://github.com/{org}/{nome}", org = DEFAULT_PLEME_GIT_ORG,
632    /// nome = caixa.nome()))` open-coded composition every prior caller
633    /// re-derived collapses onto one canonical arm.
634    ///
635    /// Distinct from [`Self::repositorio`] (`Option<&str>`, exposes the
636    /// author-omitted / author-declared partition to the caller) — this
637    /// accessor is the **resolved** URL surface, folding the fallback in
638    /// at the substrate-primitive boundary. Every consumer that keys off
639    /// the `Option::is_none()` discriminator (a [`Chart.yaml`] `home:`
640    /// field emit that must omit the field entirely on an author-omitted
641    /// `:repositorio`, per the [`Self::repositorio`] docstring's
642    /// documented four-consumer list) reaches through the raw
643    /// [`Self::repositorio`] `Option<&str>` accessor by construction — the
644    /// resolved-URL composer sits alongside it as the second projection
645    /// on the same underlying `:repositorio` slot rather than replacing
646    /// the raw accessor.
647    ///
648    /// The fallback branch is the exact byte-image of the prior inline
649    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url` composer at
650    /// caixa-flux/src/lib.rs:2080 — pinned by the sibling caixa-flux
651    /// byte-parity test
652    /// `cluster_bundle_opts_for_caixa_git_url_routes_through_canonical_git_url_accessor`
653    /// against a future implementation of this method that reordered the
654    /// `format!` template arguments, migrated the `<org>` segment to a
655    /// different constant (the [`crate::DEFAULT_PLEME_GIT_ORG`] axis a
656    /// future substrate-side git-org migration may split off), or
657    /// silently absorbed the empty-string arm (a hypothetical
658    /// `Some("") → fallback` collapse the raw [`Self::repositorio`]
659    /// accessor's docstring explicitly rejects on the sibling raw
660    /// accessor).
661    ///
662    /// Peer of the sibling per-`&Caixa`-axis composed helpers
663    /// [`caixa-flux::cluster_bundle_for_caixa`] (06d52d7) on the sibling
664    /// substrate-side renderer surface — same "close the composed
665    /// substrate-primitive at one canonical arm on the single-`&Caixa`
666    /// dispatch, converge every prior open-coded caller onto the arm"
667    /// discipline extended onto the resolved-git-URL projection of the
668    /// per-`Caixa` `:repositorio` axis. Owns per-call [`String`]
669    /// allocation on both arms (the `Some` arm's `str::to_owned` and the
670    /// `None` arm's `format!`) — the by-value return matches every
671    /// downstream consumer's field-fill shape (the caixa-flux
672    /// `ClusterBundleOpts::git_url: String` field, every future
673    /// `Chart.yaml` `home:` fold's `Option<String>` field-fill on the
674    /// `Some` arm).
675    #[must_use]
676    pub fn canonical_git_url(&self) -> String {
677        self.repositorio().map_or_else(
678            || {
679                format!(
680                    "https://github.com/{org}/{nome}",
681                    org = crate::DEFAULT_PLEME_GIT_ORG,
682                    nome = self.nome(),
683                )
684            },
685            str::to_owned,
686        )
687    }
688
689    /// Substrate-canonical per-`Caixa` **resolved-publish-tag** composer —
690    /// returns the caixa's canonical Zig-style git-publish-tag as an owned
691    /// [`String`], derived by concatenating
692    /// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] with the typed
693    /// [`Self::versao`] byte-string on a single `format!` template.
694    /// Every substrate-side consumer that resolves "which git tag does this
695    /// caixa publish under?" reaches for exactly one typed dispatch on the
696    /// substrate primitive — the raw `format!("{prefix}{versao}", prefix =
697    /// caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao = caixa.versao())`
698    /// open-coded composition every prior caller re-derived collapses onto
699    /// one canonical arm.
700    ///
701    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
702    /// git-URL composer on the paired per-`Caixa` git-remote axis — same
703    /// "close the composed substrate-primitive at one canonical arm on the
704    /// single-`&Caixa` dispatch, converge every prior open-coded caller
705    /// onto the arm" discipline extended from the resolved-URL projection
706    /// of the per-`Caixa` `:repositorio` axis onto the resolved-tag
707    /// projection of the per-`Caixa` `:versao` axis. The two accessors
708    /// jointly close the pair of scalars every `FluxCD` `GitRepository` CR
709    /// keys off (`spec.url` via [`Self::canonical_git_url`],
710    /// `spec.ref.tag` via [`Self::publish_tag`]) at the substrate primitive
711    /// — a downstream consumer that reaches through both accessors reads
712    /// the complete published-git-identity of a caixa through two typed
713    /// dispatches, not four open-coded field accesses.
714    ///
715    /// The reader-side (`caixa-flux::cluster_bundle` /
716    /// `ClusterBundleOpts::for_caixa`'s `git_ref` field, every future
717    /// per-cluster snapshot bundle emitter, the future M4
718    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's tag-carrier
719    /// slot on the tatara `Process` intent) always resolves the tag under
720    /// the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] prefix — this
721    /// method encodes that reader-side convention. The writer-side
722    /// (`caixa-feira`'s `feira publish` `--prefix` clap flag) allows the
723    /// operator to override the prefix at publish time; the two surfaces
724    /// intentionally sit on the "canonical default + operator override"
725    /// pair the sibling [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] constant's
726    /// own docstring documents — a `feira publish --prefix release/`
727    /// override is the operator's explicit opt-out from the substrate
728    /// default, not a supported drift axis.
729    ///
730    /// The composition body is the exact byte-image of the prior inline
731    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_ref` composer at
732    /// caixa-flux/src/lib.rs:2105 — pinned by the sibling caixa-flux
733    /// byte-parity test
734    /// `cluster_bundle_opts_for_caixa_git_ref_routes_through_publish_tag_accessor`
735    /// against a future implementation of this method that reordered the
736    /// `format!` template arguments, migrated the `<prefix>` segment to a
737    /// different constant (the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] axis
738    /// a future Zig-style-tag rebrand may split off — the constant's own
739    /// docstring anticipates a substrate-side move to `release/<versao>`
740    /// or bare `<versao>` shapes once a sibling forge convention adopts a
741    /// slash-namespaced or bare-scalar form), interposed a canonicalization
742    /// pass on the `:versao` axis (a SemVer-2 build-metadata strip an OCI-
743    /// tag normalizer might apply once the M4 registry-alignment slot
744    /// lands), or silently absorbed an empty `:versao` arm (which cannot
745    /// occur past the [`Self::validate_versao`] gate but which a
746    /// hypothetical bypass on the accessor path must not silently paper
747    /// over).
748    ///
749    /// Owns per-call [`String`] allocation via the single `format!`
750    /// invocation — the by-value return matches every downstream
751    /// consumer's field-fill shape (the caixa-flux `GitRefSpec::Tag(String)`
752    /// variant's owned payload, every future `intent.aplicacao.tag: String`
753    /// field-fill on the M4 CR materializer's tag-carrier slot).
754    #[must_use]
755    pub fn publish_tag(&self) -> String {
756        format!(
757            "{prefix}{versao}",
758            prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
759            versao = self.versao(),
760        )
761    }
762
763    /// Substrate-canonical per-`Caixa` **resolved-Helm-chart-name** composer
764    /// — returns the caixa's canonical `lareira-<nome>` per-Servico Helm
765    /// chart identity as an owned [`String`], derived by dispatching through
766    /// the substrate-canonical [`crate::lareira_chart_name`] helper against
767    /// the typed [`Self::nome`] byte-string. Every substrate-side consumer
768    /// that resolves "which Helm chart identity does this caixa render
769    /// under?" reaches for exactly one typed dispatch on the substrate
770    /// primitive — the raw `caixa_core::lareira_chart_name(caixa.nome())`
771    /// two-step compose every prior caller re-derived collapses onto one
772    /// canonical arm on the single-`&Caixa` dispatch.
773    ///
774    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
775    /// git-URL composer + [`Self::publish_tag`] (07e05b8) resolved-publish-
776    /// tag composer on the paired per-`Caixa` published-artifact-identity
777    /// axis — same "close the composed substrate-primitive at one canonical
778    /// arm on the single-`&Caixa` dispatch, converge every prior open-coded
779    /// caller onto the arm" discipline extended from the resolved-URL /
780    /// resolved-tag projections of the `:repositorio` / `:versao` axes onto
781    /// the resolved-chart-name projection of the `:nome` axis. The three
782    /// accessors jointly close the triple of scalars every per-Servico
783    /// deploy artifact keys off (git source URL via
784    /// [`Self::canonical_git_url`], git source tag via
785    /// [`Self::publish_tag`], per-Servico Helm chart identity via
786    /// [`Self::lareira_chart_name`]) at the substrate primitive — a
787    /// downstream consumer that reaches through all three reads the
788    /// complete deploy-artifact identity of a caixa through three typed
789    /// dispatches, not six open-coded compositions across three renderer
790    /// crates.
791    ///
792    /// The reader-side (three production sites at the time of the lift —
793    /// [`caixa-helm::render_chart_for_servico_with`]'s `ChartDir.name`
794    /// composer at caixa-helm/src/lib.rs:778, the peer
795    /// [`caixa-flux::cluster_bundle`]'s per-CR `chart_name` binding at
796    /// caixa-flux/src/lib.rs:2219, and
797    /// [`caixa-tatara::process_for_aplicacao`]'s `release_name`
798    /// composer at caixa-tatara/src/lib.rs:227, plus every future
799    /// per-Servico OCI publish emitter the CAIXA-SDLC §II
800    /// `caixa-publish.yml` reusable workflow's `skopeo push` step keys
801    /// off, the future per-cluster snapshot bundle emitter, the future
802    /// M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
803    /// per-member chart-carrier slot on the tatara `Process` intent) —
804    /// always resolves the chart name under the canonical
805    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] prefix; this method encodes
806    /// that reader-side convention. The joint-length invariant the peer
807    /// [`Self::validate_nome_chart_name_budget`] gate enforces at
808    /// caixa-build time (author-declared `:nome` + fixed prefix ≤
809    /// [`crate::DNS_1123_LABEL_MAX_LEN`]) is verified on the input to
810    /// this composer by construction, so the produced `lareira-<nome>`
811    /// string is a valid Helm chart-name segment on every accept-set
812    /// input.
813    ///
814    /// The composition body is the exact byte-image of the prior inline
815    /// `caixa_core::lareira_chart_name(caixa.nome())` two-step form every
816    /// prior caller re-derived — pinned by the sibling caixa-helm /
817    /// caixa-flux / caixa-tatara byte-parity tests
818    /// `<crate>_lareira_chart_name_routes_through_caixa_accessor` against
819    /// a future implementation of this method that reordered the
820    /// composition arguments, migrated the `<prefix>` segment to a
821    /// different constant (the [`crate::LAREIRA_CHART_NAME_PREFIX`] axis a
822    /// future substrate-side chart-family rebrand may split off — the
823    /// constant's own docstring anticipates a substrate-side move once
824    /// the `lareira-` scoping intent outlives the family it names),
825    /// interposed a canonicalization pass on the `:nome` axis (a per-
826    /// registry namespace-qualification an M4 CR materializer might apply
827    /// per-CR — the "`pleme-io/checkout` vs `partner-org/checkout`
828    /// collision" arm the multi-tenant-registry story acknowledges), or
829    /// silently absorbed an empty `:nome` arm (which cannot occur past
830    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
831    /// on the accessor path must not silently paper over).
832    ///
833    /// Owns per-call [`String`] allocation via the single
834    /// [`crate::lareira_chart_name`] `format!` invocation — the by-value
835    /// return matches every downstream consumer's field-fill shape (the
836    /// caixa-helm `ChartDir.name: String` field, the caixa-flux per-CR
837    /// `chart_name: String` binding, the caixa-tatara
838    /// `AplicacaoIntent.release_name: Option<String>` field-fill on the
839    /// `Some` arm).
840    #[must_use]
841    pub fn lareira_chart_name(&self) -> String {
842        crate::lareira_chart_name(self.nome())
843    }
844
845    /// Substrate-canonical per-`Caixa` **resolved-OCI-chart-ref** composer
846    /// — returns the caixa's canonical `oci://<registry>/lareira-<nome>`
847    /// per-Servico Helm chart OCI artifact reference as an owned
848    /// [`String`], derived by dispatching through the substrate-canonical
849    /// [`crate::oci_chart_ref`] helper (which itself composes
850    /// [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied `registry` +
851    /// [`crate::lareira_chart_name`]-of-[`Self::nome`]) against the
852    /// caller-supplied `registry` and the typed [`Self::nome`] byte-string.
853    /// Every substrate-side consumer that resolves "which OCI chart
854    /// artifact does this caixa publish under, in this registry?" reaches
855    /// for exactly one typed dispatch on the substrate primitive — the raw
856    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step compose
857    /// every prior caller re-derived collapses onto one canonical arm on
858    /// the single-`(&Caixa, &str)` dispatch.
859    ///
860    /// Fourth member of the paired per-`Caixa` published-artifact-identity
861    /// axis alongside [`Self::canonical_git_url`] (124f864) /
862    /// [`Self::publish_tag`] (07e05b8) / [`Self::lareira_chart_name`]
863    /// (a8f0bee) — same "close the composed substrate-primitive at one
864    /// canonical arm on the single-`&Caixa` dispatch, converge every
865    /// prior open-coded caller onto the arm" discipline extended from the
866    /// resolved-URL / resolved-tag / resolved-chart-name projections of
867    /// the `:repositorio` / `:versao` / `:nome` axes onto the resolved-
868    /// OCI-ref projection over the paired `(registry, :nome)` inputs. The
869    /// four accessors jointly close the per-`Caixa` published-artifact-
870    /// identity surface every downstream consumer of a caixa's published
871    /// deploy artifacts keys off (git source URL via
872    /// [`Self::canonical_git_url`], git source tag via
873    /// [`Self::publish_tag`], per-Servico Helm chart identity via
874    /// [`Self::lareira_chart_name`], per-registry OCI chart artifact
875    /// reference via [`Self::oci_chart_ref`]) at the substrate primitive
876    /// — a downstream consumer that reaches through all four reads the
877    /// complete deploy-artifact identity of a caixa through four typed
878    /// dispatches, not eight open-coded compositions across four renderer
879    /// crates. The unique-signature dispatch (`(&Caixa, &str)` on this
880    /// method vs. `&Caixa` on the sibling three) reflects the extra input
881    /// axis this composer folds in: unlike the git-URL / git-tag / chart-
882    /// name axes (each derived purely from a `&Caixa`), the OCI-ref axis
883    /// pairs the caixa's per-`:nome` chart identity with the caller-
884    /// supplied per-registry authority segment, so the accessor threads
885    /// the registry byte-string through as a positional `&str`.
886    ///
887    /// The reader-side (one production site at the time of the lift —
888    /// [`caixa-tatara::process_for_aplicacao`]'s `derive_chart_ref` helper
889    /// at caixa-tatara/src/lib.rs:333 that composes the emitted
890    /// `AplicacaoIntent.chart_ref` scalar the tatara-reconciler feeds into
891    /// `helm install`, plus every future per-Servico OCI publish emitter
892    /// the CAIXA-SDLC §II `caixa-publish.yml` reusable workflow's
893    /// `skopeo push` step keys off, the future per-cluster snapshot bundle
894    /// emitter's per-CR `oci://…` field-fill on the M4 registry-alignment
895    /// slot, the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
896    /// materializer's per-member `chart_ref` slot on the tatara `Process`
897    /// intent, the `FluxCD` `HelmRelease` `spec.chart.spec.chart` field-fill
898    /// on the OCI-source path an M4 per-cluster registry-rewrite overlay
899    /// applies per-CR) — always resolves the OCI ref under the canonical
900    /// [`crate::OCI_SCHEME_PREFIX`] scheme prefix + the canonical
901    /// [`Self::lareira_chart_name`] chart-name segment; this method
902    /// encodes that reader-side convention.
903    ///
904    /// The composition body is the exact byte-image of the prior inline
905    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step form
906    /// every prior caller re-derived — pinned by the sibling caixa-tatara
907    /// byte-parity test
908    /// `derive_chart_ref_routes_through_caixa_oci_chart_ref_accessor`
909    /// against a future implementation of this method that reordered the
910    /// composition arguments, migrated the `<scheme>` segment to a
911    /// different constant (the [`crate::OCI_SCHEME_PREFIX`] axis a future
912    /// substrate-side registry-protocol rebrand may split off — the
913    /// constant's own docstring anticipates a substrate-side move once
914    /// Helm 3 / `FluxCD` introduce a successor scheme past `oci://`),
915    /// migrated the `<chart>` segment off the paired
916    /// [`crate::lareira_chart_name`] composer (a per-registry
917    /// namespace-qualification an M4 CR materializer might apply per-CR),
918    /// interposed a canonicalization pass on the `registry` axis (an OCI-
919    /// authority normalization once the M4 registry-alignment slot lands),
920    /// or silently absorbed an empty `:nome` arm (which cannot occur past
921    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
922    /// on the accessor path must not silently paper over).
923    ///
924    /// Owns per-call [`String`] allocation via the single
925    /// [`crate::oci_chart_ref`] `format!` invocation — the by-value return
926    /// matches every downstream consumer's field-fill shape (the caixa-
927    /// tatara `AplicacaoIntent.chart_ref: String` field-fill, every
928    /// future `intent.aplicacao.chart_ref: String` field-fill on the M4
929    /// CR materializer's chart-ref-carrier slot, every future
930    /// `HelmRelease.spec.chart.spec.chart: String` field-fill on the OCI-
931    /// source path).
932    #[must_use]
933    pub fn oci_chart_ref(&self, registry: &str) -> String {
934        crate::oci_chart_ref(registry, self.nome())
935    }
936
937    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
938    /// chart-description scalar accessor every consumer of the top-level
939    /// manifest's Chart.yaml `description:` axis keys off — returns the
940    /// author-declared `:descricao` byte-string verbatim as an
941    /// `Option<&str>`, borrowed from the typed slot's own
942    /// `Option<String>` storage. `None` when the slot is absent (the
943    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
944    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
945    /// omitted slot through a `format!("Generated chart for caixa Servico
946    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
947    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
948    /// and [`caixa-feira`]'s `render_flake` folds it through a
949    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
950    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
951    ///
952    /// The `:descricao` slot carries the universal-axis free-form-prose
953    /// chart-description identifier every kind of caixa emits under
954    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
955    /// supplies) — the typed slot's `Option<String>` accept-set
956    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
957    /// chart-description-shape-invalid rejected through
958    /// [`ManifestError::DescricaoInvalid`] past the shared
959    /// [`crate::render::is_chart_description_shape`] predicate the peer
960    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
961    /// load-bearing downstream consumers:
962    ///
963    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
964    ///     gate binding — the universal-axis identity gate wired at
965    ///     caixa-build time.
966    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
967    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
968    ///     chart's `Chart.yaml` `description:` field, which
969    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
970    ///     `WARNING [chart.metadata.description]: description is required`
971    ///     when absent) and which every registry that ingests the chart
972    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
973    ///     chart's canonical one-line prose descriptor.
974    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
975    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
976    ///     header directly beneath the `# <chart-name>` title, which
977    ///     every author who inspects the rendered chart bundle lands at.
978    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
979    ///     top-level fold — the emitted `flake.nix`'s `description`
980    ///     field, which every Nix consumer (`nix flake show`,
981    ///     `nix flake metadata`, downstream flake-registry ingestors)
982    ///     surfaces as the flake's canonical descriptor.
983    ///
984    /// Prior to this lift the `.descricao` field was accessed inline at
985    /// four production sites — [`Self::validate_descricao`]'s
986    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
987    /// caixa-helm `build_chart_yaml`
988    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
989    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
990    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
991    /// `README.md` header fold, and the caixa-feira `render_flake`
992    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
993    /// `description = ""` fold — four open-coded field-accesses that
994    /// expressed no compile-time link back to the typed slot. A future
995    /// extension of the `:descricao` axis to a richer author surface —
996    /// a per-`:descricao` locale-tagged multi-language descriptor map
997    /// (the "one caixa, N language-tagged prose descriptions" arm
998    /// author-tooling internationalization anticipates), a
999    /// per-registry-target length-and-shape overlay the M4 CR
1000    /// materializer resolves per-CR (the "ArtifactHub caps description
1001    /// at 512 bytes but the internal registry caps at 256" arm), a
1002    /// promotion of the plain `Option<String>` byte-string to a richer
1003    /// `ChartDescription` newtype guaranteeing the
1004    /// `is_chart_description_shape` predicate at the type level — would
1005    /// have had to be threaded through all four open-coded copies in
1006    /// lockstep or the validate gate and the three emit paths would
1007    /// silently disagree on which prose string a given [`Caixa`]
1008    /// resolves to (an author's
1009    /// `:descricao "Checkout flow orchestration."` would satisfy
1010    /// validate while one of the emit paths silently rendered a stale
1011    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
1012    /// resolution to a typed method on the substrate primitive means
1013    /// every downstream consumer of the caixa's per-`Caixa`
1014    /// chart-description surface reaches for exactly one typed dispatch
1015    /// — the resolver's accept-set migrates as a unit on any future
1016    /// axis addition.
1017    ///
1018    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
1019    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
1020    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
1021    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
1022    /// lift folds on. Same "one typed dispatch on the substrate
1023    /// primitive, thin projections at each consumer" discipline the
1024    /// peer per-`:placement`
1025    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1026    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1027    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1028    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1029    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1030    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1031    /// typed-slot atom axes, extended here to the third outer top-level
1032    /// `Caixa` universal-axis surface. Named `descricao()` to match the
1033    /// storage field's name; the accessor's identity maps onto the
1034    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1035    /// carries. The one remaining universal `Option<String>` slot
1036    /// (`:edicao`) folds on this pattern next.
1037    #[must_use]
1038    pub const fn descricao(&self) -> Option<&str> {
1039        match &self.descricao {
1040            Some(s) => Some(s.as_str()),
1041            None => None,
1042        }
1043    }
1044
1045    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
1046    /// accessor every consumer of the top-level manifest's tatara-lisp
1047    /// edition-selector axis keys off — returns the author-declared
1048    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
1049    /// the typed slot's own `Option<String>` storage. `None` when the
1050    /// slot is absent (the canonical "omit the slot to defer to the
1051    /// substrate's default edition" shape every existing
1052    /// [`caixa-resolver`] integration test fixture carries via
1053    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
1054    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
1055    /// arm by construction, so an author-omitted `:edicao` round-trips
1056    /// to a build without triggering the year-shape predicate).
1057    ///
1058    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
1059    /// decimal-year language-edition identifier every kind of caixa
1060    /// emits under (CAIXA-SDLC §I — the author-facing surface every
1061    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
1062    /// accept-set (empty-string rejected through
1063    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
1064    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
1065    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
1066    /// onto one load-bearing downstream consumer today
1067    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
1068    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
1069    /// future edition-aware substrate consumer the CAIXA-SDLC §I
1070    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
1071    /// selector every edition-aware build step keys off, the future
1072    /// per-edition compatibility-flag overlay the M4 CR materializer
1073    /// resolves per-CR, the peer [`Caixa::template`] canonical
1074    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
1075    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
1076    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
1077    /// carry `edicao: Some("2026".into())` by construction).
1078    ///
1079    /// Prior to this lift the `.edicao` field was accessed inline at
1080    /// one production site — [`Self::validate_edicao`]'s
1081    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
1082    /// open-coded field-access that expressed no compile-time link
1083    /// back to the typed slot. A future extension of the `:edicao`
1084    /// axis to a richer author surface — a per-`:edicao` known-
1085    /// edition allowlist (the future tightening
1086    /// [`Self::validate_edicao`]'s docstring acknowledges past the
1087    /// structural year-shape floor, rejecting year-shaped values that
1088    /// don't name a tatara-lisp edition the substrate actually
1089    /// understands — `"1999"` is year-shaped but no `1999` edition
1090    /// exists), a per-edition compatibility-flag overlay the M4 CR
1091    /// materializer resolves per-CR (the "edition `"2026"` enables
1092    /// macro-surface features the sibling `"2018"` gates behind a
1093    /// feature flag" arm the edition-selector story anticipates), a
1094    /// promotion of the plain `Option<String>` byte-string to a
1095    /// richer `CaixaEdition` enum discriminated on year once a sibling
1096    /// edition to `"2026"` lands — would have had to be threaded
1097    /// through the open-coded copy in lockstep with every future
1098    /// edition-aware consumer, or the validate gate and the future
1099    /// edition-aware consumer path would silently disagree on which
1100    /// edition a given [`Caixa`] resolves to (an author's
1101    /// `:edicao "2026"` would satisfy validate while a future
1102    /// edition-aware consumer silently defaulted to a stale edition,
1103    /// or vice versa). Lifting the resolution to a typed method on
1104    /// the substrate primitive means every downstream consumer of the
1105    /// caixa's per-`Caixa` edition surface reaches for exactly one
1106    /// typed dispatch — the resolver's accept-set migrates as a unit
1107    /// on any future axis addition.
1108    ///
1109    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
1110    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
1111    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
1112    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
1113    /// `Option<&str>` scalar" projection pattern this lift folds on.
1114    /// Same "one typed dispatch on the substrate primitive, thin
1115    /// projections at each consumer" discipline the peer per-`:placement`
1116    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1117    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1118    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1119    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1120    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1121    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1122    /// typed-slot atom axes, extended here to close the outer top-level
1123    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
1124    /// slot. Named `edicao()` to match the storage field's name; the
1125    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1126    /// vocabulary the slot's docstring already carries.
1127    #[must_use]
1128    pub const fn edicao(&self) -> Option<&str> {
1129        match &self.edicao {
1130            Some(s) => Some(s.as_str()),
1131            None => None,
1132        }
1133    }
1134
1135    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
1136    /// label caixa-identity scalar accessor every consumer of the top-
1137    /// level manifest's identity axis keys off — returns the author-
1138    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
1139    /// the typed slot's own `String` storage. Non-optional (`:nome` is
1140    /// a required-axis scalar every `defcaixa` form must supply; the
1141    /// [`Self::from_lisp`] derive rejects an omitted / non-string
1142    /// `:nome` at parse time, so a `Caixa` past parse definitionally
1143    /// carries a non-`None` `:nome`).
1144    ///
1145    /// The `:nome` slot carries the universal-axis DNS-1123-label
1146    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
1147    /// the primary identity axis every `defcaixa` form supplies
1148    /// alongside `:versao` / `:kind`; the substrate-wide identity every
1149    /// other typed surface that names a caixa reaches through — `:deps`
1150    /// entries, `:membros` entries, `:children` entries, the
1151    /// `lareira-<nome>` Helm chart name every per-Servico renderer
1152    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
1153    /// renderer emits) — the typed slot's `String` accept-set (empty
1154    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
1155    /// invalid rejected through [`ManifestError::NomeInvalid`] past
1156    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
1157    /// the peer name axes each land on, joint-length-with-`lareira-`-
1158    /// prefix rejected through
1159    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
1160    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
1161    /// load-bearing downstream consumer the substrate carries — the
1162    /// two universal-axis validate gates at caixa-build time
1163    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
1164    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
1165    /// derivation every per-Servico renderer keys off, the caixa-helm
1166    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
1167    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
1168    /// `HTTPRoute` per-Aplicacao name axes at
1169    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
1170    /// [`crate::pleme_program_selector`] /
1171    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
1172    /// derivations, and every future substrate renderer that emits an
1173    /// artifact keyed by the caixa's identity.
1174    ///
1175    /// Prior to this lift the `.nome` field was accessed inline at a
1176    /// dozen production sites across `caixa-core` (the two universal-
1177    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
1178    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
1179    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
1180    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
1181    /// entry `name:` fold, the `flux_kustomization_source_subtree`
1182    /// per-cluster subpath derivation), and `caixa-mesh` (the
1183    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
1184    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
1185    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
1186    /// insert) — a dozen open-coded field-accesses that expressed no
1187    /// compile-time link back to the typed slot. A future extension of
1188    /// the `:nome` axis to a richer author surface — a per-`:nome`
1189    /// structured `CaixaIdentity` newtype that carries the joint-
1190    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
1191    /// enforces at the type level (rather than as a validate-time
1192    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
1193    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
1194    /// `partner-org/checkout` collision" arm the multi-tenant-registry
1195    /// story acknowledges), a promotion of the plain `String` byte-
1196    /// string to a richer `CaixaNome` newtype discriminated on
1197    /// namespace prefix — would have had to be threaded through every
1198    /// open-coded copy in lockstep or the two validate gates and the
1199    /// dozen emit paths would silently disagree on which identity a
1200    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
1201    /// would satisfy validate while one of the emit paths silently
1202    /// rendered a drifted other identity, or vice versa). Lifting the
1203    /// resolution to a typed method on the substrate primitive means
1204    /// every downstream consumer of the caixa's per-`Caixa` identity
1205    /// surface reaches for exactly one typed dispatch — the resolver's
1206    /// accept-set migrates as a unit on any future axis addition.
1207    ///
1208    /// First outer top-level [`Caixa`] `&str`-return required-scalar
1209    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
1210    /// projection pattern the sibling per-`Caixa` `:versao` future lift
1211    /// folds on. Sibling in shape to the peer per-`:membros`
1212    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
1213    /// [`crate::aplicacao::WitContract::source`] /
1214    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
1215    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
1216    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
1217    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
1218    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
1219    /// per-sub-struct required-axis accessors carry on the sibling M3
1220    /// mesh-slot-atom scalar-value axes, extended here to open the
1221    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
1222    /// Named `nome()` to match the storage field's name; the accessor's
1223    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1224    /// slot's docstring already carries.
1225    #[must_use]
1226    pub const fn nome(&self) -> &str {
1227        self.nome.as_str()
1228    }
1229
1230    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
1231    /// pinned-version scalar accessor every consumer of the top-level
1232    /// manifest's version axis keys off — returns the author-declared
1233    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
1234    /// typed slot's own `String` storage. Non-optional (`:versao` is a
1235    /// required-axis scalar every `defcaixa` form must supply alongside
1236    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
1237    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
1238    /// parse definitionally carries a non-`None` `:versao`).
1239    ///
1240    /// The `:versao` slot carries the universal-axis SemVer-2
1241    /// concrete-version body every kind of caixa emits under
1242    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
1243    /// supplies alongside `:nome` / `:kind`; the substrate-wide
1244    /// pinned-version every downstream artifact-emitting consumer
1245    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
1246    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
1247    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
1248    /// prefix composes on top of, the programs.yaml entry's `versao:`
1249    /// value the `lareira-fleet-programs` aggregator carries onto each
1250    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
1251    /// tags every substrate-side `skopeo push` writes, the lacre
1252    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
1253    /// prior-version references peers in the exact same SemVer-2 shape).
1254    /// The typed slot's `String` accept-set (empty rejected through
1255    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
1256    /// through [`ManifestError::VersaoInvalid`] past
1257    /// [`semver::Version::parse`]) maps onto every load-bearing
1258    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
1259    /// universal-axis validate gate at caixa-build time, the
1260    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
1261    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
1262    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
1263    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
1264    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
1265    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
1266    /// tag derivation (`format!("{prefix}{versao}")`), and every future
1267    /// substrate renderer that emits an artifact keyed by the caixa's
1268    /// pinned version.
1269    ///
1270    /// Prior to this lift the `.versao` field was accessed inline at a
1271    /// dozen production sites across `caixa-core` (the universal-axis
1272    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
1273    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
1274    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
1275    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
1276    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
1277    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
1278    /// (the `feira publish` git-tag derivation + the `feira app graph` /
1279    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
1280    /// field-accesses that expressed no compile-time link back to the
1281    /// typed slot. A future extension of the `:versao` axis to a richer
1282    /// author surface — a per-`:versao` structured `CaixaVersion` at the
1283    /// storage layer (the substrate already carries a `CaixaVersion`
1284    /// newtype at [`crate::version::CaixaVersion`], deferred until the
1285    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
1286    /// a per-registry `:versao` immutability overlay the M4 CR
1287    /// materializer enforces per-CR, a promotion of the plain `String`
1288    /// byte-string to a richer `PinnedVersao` newtype discriminated on
1289    /// SemVer-2 pre-release / build-metadata presence — would have had
1290    /// to be threaded through every open-coded copy in lockstep or the
1291    /// validate gate and the dozen emit paths would silently disagree
1292    /// on which version a given [`Caixa`] resolves to (an author's
1293    /// `:versao "0.1.0"` would satisfy validate while one of the emit
1294    /// paths silently rendered a drifted other version, or vice versa).
1295    /// Lifting the resolution to a typed method on the substrate
1296    /// primitive means every downstream consumer of the caixa's
1297    /// per-`Caixa` pinned-version surface reaches for exactly one typed
1298    /// dispatch — the resolver's accept-set migrates as a unit on any
1299    /// future axis addition.
1300    ///
1301    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
1302    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
1303    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
1304    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
1305    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
1306    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
1307    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
1308    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
1309    /// on the sibling per-typed-slot version-carrier axes, extended here
1310    /// to close the second outer top-level [`Caixa`] required-`&str`-
1311    /// carrying axis so the two universal-axis identity-carrying
1312    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
1313    /// share the same "one typed dispatch per axis" discipline. Named
1314    /// `versao()` to match the storage field's name; the accessor's
1315    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1316    /// slot's docstring already carries.
1317    #[must_use]
1318    pub const fn versao(&self) -> &str {
1319        self.versao.as_str()
1320    }
1321
1322    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
1323    /// closed-set-enum discriminant accessor every consumer of the top-
1324    /// level manifest's kind axis keys off — returns the author-declared
1325    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
1326    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
1327    /// (`:kind` is a required-axis discriminant every `defcaixa` form
1328    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
1329    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
1330    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
1331    /// variant).
1332    ///
1333    /// The `:kind` slot carries the universal-axis closed-set typed-
1334    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
1335    /// §I — the primary shape gate every renderer / verifier /
1336    /// operator branches on; the five variants `Biblioteca` /
1337    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
1338    /// the caixa surface into disjoint runtime contracts) — the typed
1339    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
1340    /// values through the derive-macro's symbol-arm gate, exhaustively
1341    /// matched at every downstream dispatch site) maps onto every
1342    /// load-bearing downstream consumer the substrate carries:
1343    ///
1344    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
1345    ///     predicate — the canonical two-line
1346    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
1347    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
1348    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
1349    ///     ComputeUnit` CR materializer) runs at its entry-point,
1350    ///     alongside the [`crate::render::KindMismatch`] error carrier's
1351    ///     `actual:` field the diagnostic surfaces to name the offending
1352    ///     caixa's variant.
1353    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
1354    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
1355    ///     `_view` composers that fold the flat mesh-slot / supervisor-
1356    ///     slot columns into their typed sub-spec only when the kind
1357    ///     matches (returns `None` otherwise); the future per-Servico
1358    ///     M2-view composer (`servico_view`) will follow the same shape.
1359    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
1360    ///     coherence gate — the `!self.kind.requires_exe()` /
1361    ///     `!self.kind.requires_servicos()` predicates that fence
1362    ///     each code-surface slot from the wrong owning kind.
1363    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
1364    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
1365    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
1366    ///     coherence error carriers (`SupervisorOwnsCode` /
1367    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
1368    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
1369    ///     / `ForeignCodeSlot`) which each name the offending caixa's
1370    ///     variant in their `kind:` field.
1371    ///
1372    /// Prior to this lift the `.kind` field was accessed inline at
1373    /// twenty-plus production sites across `caixa-core` (the
1374    /// [`crate::render::require_kind`] entry-gate predicate + the
1375    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
1376    /// composers, the `declared_foreign_code_slots` per-slot kind-
1377    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
1378    /// kind ↔ code-surface predicates + four error carriers) — a score
1379    /// of open-coded field-accesses that expressed no compile-time link
1380    /// back to the typed slot. A future extension of the `:kind` axis
1381    /// to a richer author surface — a per-`:kind` sub-variant discriminant
1382    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
1383    /// variant across the wasm-component / legacy-container / native-
1384    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
1385    /// kind-overlay the M4 CR materializer resolves per-CR (the
1386    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
1387    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
1388    /// enum to a richer `KindWithRuntime` discriminated on the
1389    /// component-model world axis — would have had to be threaded
1390    /// through every open-coded copy in lockstep or the entry gate,
1391    /// the view composers, and the layout invariants would silently
1392    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
1393    /// the resolution to a typed method on the substrate primitive
1394    /// means every downstream consumer of the caixa's per-`Caixa`
1395    /// kind surface reaches for exactly one typed dispatch — the
1396    /// resolver's accept-set migrates as a unit on any future axis
1397    /// addition.
1398    ///
1399    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
1400    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
1401    /// required-discriminant" projection pattern. Sibling in shape to
1402    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
1403    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
1404    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
1405    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
1406    /// on the sibling nested-spec typed-slot discriminator axes,
1407    /// extended here to the outer top-level [`Caixa`] universal-axis
1408    /// surface. Named `kind()` to match the storage field's name;
1409    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
1410    /// vocabulary the slot's docstring already carries.
1411    #[must_use]
1412    pub const fn kind(&self) -> CaixaKind {
1413        self.kind
1414    }
1415
1416    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
1417    /// maintainer-name-list slice-accessor every consumer of the top-
1418    /// level manifest's maintainer axis keys off — returns the author-
1419    /// declared `:autores` list verbatim as a `&[String]` slice-view over
1420    /// the same backing buffer the raw `self.autores.as_slice()` field
1421    /// access borrows from. Empty-list-carrying (`:autores` is a default-
1422    /// empty axis every `defcaixa` form supplies with an empty `()` when
1423    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
1424    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1425    /// parse definitionally carries a `Vec<String>` slot — possibly
1426    /// empty — and the returned `&[String]` degenerates to an empty
1427    /// slice on that arm without any silent `None` collapse).
1428    ///
1429    /// The `:autores` slot carries the universal-axis maintainer-name
1430    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1431    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1432    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1433    /// every downstream registry-facing artifact emits under) — the
1434    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1435    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1436    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1437    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1438    /// onto every load-bearing downstream consumer the substrate carries
1439    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1440    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1441    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1442    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1443    /// name, email: None }` record, every future per-`Caixa` registry-
1444    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1445    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1446    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1447    /// the future per-cluster author-notification overlay the M4 CR
1448    /// materializer resolves per-CR).
1449    ///
1450    /// Prior to this lift the `.autores` field was accessed inline at
1451    /// two production sites — [`Self::validate_autores`]'s `for autor
1452    /// in &self.autores` walk that gates every entry through
1453    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1454    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1455    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1456    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1457    /// two open-coded field-accesses that expressed no compile-time link
1458    /// back to the typed slot. A future extension of the `:autores` axis
1459    /// to a richer author surface — a per-`:autores` structured
1460    /// `Maintainer { name, email, url }` at the storage layer once the
1461    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1462    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1463    /// enforces per-CR (the "cluster policy demands every author declare
1464    /// an on-file `mailto:` contact" arm), a promotion of the plain
1465    /// `Vec<String>` byte-string list to a richer
1466    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1467    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1468    /// predicate already resolves through — would have had to be
1469    /// threaded through both open-coded copies in lockstep or the
1470    /// validate gate and the caixa-helm emit path would silently
1471    /// disagree on which authors a given [`Caixa`] resolves to (an
1472    /// author's `:autores ("alice" "bob")` would satisfy validate while
1473    /// the caixa-helm emit path silently rendered a drifted other
1474    /// maintainer list, or vice versa). Lifting the resolution to a
1475    /// typed method on the substrate primitive means every downstream
1476    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1477    /// for exactly one typed dispatch — the resolver's accept-set
1478    /// migrates as a unit on any future axis addition.
1479    ///
1480    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1481    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1482    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1483    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1484    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1485    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1486    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1487    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1488    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1489    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1490    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1491    /// per-M3 typed-slot list axes, extended here to the outer top-level
1492    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1493    /// `&Vec<String>`) because every downstream consumer of the author
1494    /// list treats it as a read-only sequence — the slice-view is the
1495    /// narrowest borrow that supports every present + roadmapped consumer
1496    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1497    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1498    /// reaches for (the storage-side `Vec` remains reachable through the
1499    /// `pub autores` field for the mutation-carrying serde round-trip and
1500    /// per-test fixture-mutation paths). Named `autores()` to match the
1501    /// storage field's name; the accessor's identity maps onto the
1502    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1503    /// carries.
1504    #[must_use]
1505    pub const fn autores(&self) -> &[String] {
1506        self.autores.as_slice()
1507    }
1508
1509    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1510    /// registry-search-tag-list slice-accessor every consumer of the
1511    /// top-level manifest's topical-tag axis keys off — returns the
1512    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1513    /// slice-view over the same backing buffer the raw
1514    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1515    /// list-carrying (`:etiquetas` is a default-empty axis every
1516    /// `defcaixa` form supplies with an empty `()` when unset; the
1517    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1518    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1519    /// definitionally carries a `Vec<String>` slot — possibly empty —
1520    /// and the returned `&[String]` degenerates to an empty slice on
1521    /// that arm without any silent `None` collapse).
1522    ///
1523    /// The `:etiquetas` slot carries the universal-axis topical-tag
1524    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1525    /// author-facing surface every `defcaixa` form supplies alongside
1526    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1527    /// search-facing axis every downstream registry-facing artifact
1528    /// emits under) — the typed slot's `Vec<String>` accept-set
1529    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1530    /// non-chart-keyword-shape rejected through
1531    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1532    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1533    /// every load-bearing downstream consumer the substrate carries —
1534    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1535    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1536    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1537    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1538    /// `Chart.yaml` `keywords:` array (chained with the
1539    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1540    /// dedup'd through a `BTreeSet` at emit time), every future per-
1541    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1542    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1543    /// annotation, the future per-cluster tag-notification overlay the
1544    /// M4 CR materializer resolves per-CR).
1545    ///
1546    /// Prior to this lift the `.etiquetas` field was accessed inline at
1547    /// two production sites — [`Self::validate_etiquetas`]'s `for
1548    /// etiqueta in &self.etiquetas` walk that gates every entry through
1549    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1550    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1551    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1552    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1553    /// two open-coded field-accesses that expressed no compile-time
1554    /// link back to the typed slot. A future extension of the
1555    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1556    /// structured `ChartKeyword { name, uri, category }` at the storage
1557    /// layer once the substrate absorbs `artifacthub.io/keywords`
1558    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1559    /// CR materializer enforces per-CR (the "cluster policy demands
1560    /// every tag come from a substrate-approved taxonomy" arm), a
1561    /// promotion of the plain `Vec<String>` byte-string list to a
1562    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1563    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1564    /// already resolves through — would have had to be threaded through
1565    /// both open-coded copies in lockstep or the validate gate and the
1566    /// caixa-helm emit path would silently disagree on which tags a
1567    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1568    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1569    /// path silently rendered a drifted other keyword list, or vice
1570    /// versa). Lifting the resolution to a typed method on the
1571    /// substrate primitive means every downstream consumer of the
1572    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1573    /// typed dispatch — the resolver's accept-set migrates as a unit
1574    /// on any future axis addition.
1575    ///
1576    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1577    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1578    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1579    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1580    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1581    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1582    /// fold onto the same pattern in future lifts. Sibling in shape to
1583    /// the peer per-`:supervisor`
1584    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1585    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1586    /// (a6e18d7), per-`:membros`
1587    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1588    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1589    /// (0dcc926), and per-`:upgrade-from :instructions`
1590    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1591    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1592    /// typed-slot list axes, extended here to the outer top-level
1593    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1594    /// `&Vec<String>`) because every downstream consumer of the tag
1595    /// list treats it as a read-only sequence — the slice-view is the
1596    /// narrowest borrow that supports every present + roadmapped
1597    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1598    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1599    /// the typed view reaches for (the storage-side `Vec` remains
1600    /// reachable through the `pub etiquetas` field for the mutation-
1601    /// carrying serde round-trip and per-test fixture-mutation paths).
1602    /// Named `etiquetas()` to match the storage field's name; the
1603    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1604    /// vocabulary the slot's docstring already carries.
1605    #[must_use]
1606    pub const fn etiquetas(&self) -> &[String] {
1607        self.etiquetas.as_slice()
1608    }
1609
1610    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1611    /// library-source-path-list slice-accessor every consumer of the
1612    /// top-level manifest's Biblioteca-source axis keys off — returns
1613    /// the author-declared `:bibliotecas` list verbatim as a
1614    /// `&[String]` slice-view over the same backing buffer the raw
1615    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1616    /// list-carrying (`:bibliotecas` is a default-empty axis every
1617    /// `defcaixa` form supplies with an empty `()` when unset; the
1618    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1619    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1620    /// parse definitionally carries a `Vec<String>` slot — possibly
1621    /// empty — and the returned `&[String]` degenerates to an empty
1622    /// slice on that arm without any silent `None` collapse).
1623    ///
1624    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1625    /// entry-path list every `:kind Biblioteca` caixa emits under
1626    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1627    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1628    /// substrate-wide library-carrier axis every downstream
1629    /// authoring-facing consumer keys off) — the typed slot's
1630    /// `Vec<String>` accept-set (empty-per-entry rejected through
1631    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1632    /// non-sandboxed-relative-shape rejected through
1633    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1634    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1635    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1636    /// maps onto every load-bearing downstream consumer the substrate
1637    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1638    /// empty-check + per-entry file-exists loop at
1639    /// caixa-core/src/layout.rs that gates each entry through
1640    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1641    /// [`Self::validate_code_paths`] per-slot shape gate at
1642    /// caixa-core/src/manifest.rs that walks each entry through the
1643    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1644    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1645    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1646    /// declared library file for lexical / structural errors before
1647    /// downstream `importar` resolution, every future per-`Caixa`
1648    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1649    /// (the future `tatara-lispc` compilation entry the docstring at
1650    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1651    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1652    /// the future `caixa-lsp` per-library semantic-token stream the
1653    /// caixa-lsp docstring roadmaps).
1654    ///
1655    /// Prior to this lift the `.bibliotecas` field was accessed inline
1656    /// at three production sites — [`crate::LayoutInvariants`]'s
1657    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1658    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1659    /// declared library path through the on-disk-existence check,
1660    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1661    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1662    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1663    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1664    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1665    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1666    /// coded field-accesses that expressed no compile-time link back
1667    /// to the typed slot. A future extension of the `:bibliotecas`
1668    /// axis to a richer library surface — a per-`:bibliotecas`
1669    /// structured `BibliotecaEntry { path, edition, exports }` at the
1670    /// storage layer once the substrate absorbs the per-library
1671    /// language-edition + explicit-exports tuple the tatara-lisp
1672    /// module-system roadmap acknowledges, a per-registry
1673    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1674    /// per-CR (the "cluster policy demands every biblioteca declare
1675    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1676    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1677    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1678    /// [`crate::render::is_sandboxed_relative_path`] +
1679    /// [`crate::render::is_lisp_extension`] predicates already resolve
1680    /// through — would have had to be threaded through all three
1681    /// open-coded copies in lockstep or the layout gate, the shape
1682    /// validator, and the `feira build` phase-1 parse walk would
1683    /// silently disagree on which library paths a given [`Caixa`]
1684    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1685    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1686    /// silently parsed a drifted other list, or vice versa). Lifting
1687    /// the resolution to a typed method on the substrate primitive
1688    /// means every downstream consumer of the caixa's per-`Caixa`
1689    /// library-source surface reaches for exactly one typed dispatch
1690    /// — the resolver's accept-set migrates as a unit on any future
1691    /// axis addition.
1692    ///
1693    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1694    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1695    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1696    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1697    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1698    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1699    /// `:children` / `:membros` / `:contratos`) fold onto the same
1700    /// pattern in future lifts. Sibling in shape to the peer
1701    /// per-`:supervisor`
1702    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1703    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1704    /// (a6e18d7), per-`:membros`
1705    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1706    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1707    /// (0dcc926), and per-`:upgrade-from :instructions`
1708    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1709    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1710    /// typed-slot list axes, extended here to the outer top-level
1711    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1712    /// `&Vec<String>`) because every downstream consumer of the
1713    /// library-source list treats it as a read-only sequence — the
1714    /// slice-view is the narrowest borrow that supports every
1715    /// present + roadmapped consumer (`.iter()`, `.len()`,
1716    /// `.is_empty()`) without leaking the backing `Vec`'s
1717    /// grow/push/reserve surface no consumer of the typed view
1718    /// reaches for (the storage-side `Vec` remains reachable through
1719    /// the `pub bibliotecas` field for the mutation-carrying serde
1720    /// round-trip and per-test fixture-mutation paths). Named
1721    /// `bibliotecas()` to match the storage field's name; the
1722    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1723    /// vocabulary the slot's docstring already carries.
1724    #[must_use]
1725    pub const fn bibliotecas(&self) -> &[String] {
1726        self.bibliotecas.as_slice()
1727    }
1728
1729    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1730    /// nix-built-executable-entry-path-list slice-accessor every consumer
1731    /// of the top-level manifest's Binario-executable axis keys off —
1732    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1733    /// slice-view over the same backing buffer the raw
1734    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1735    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1736    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1737    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1738    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1739    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1740    /// degenerates to an empty slice on that arm without any silent
1741    /// `None` collapse).
1742    ///
1743    /// The `:exe` slot carries the universal-axis nix-built executable
1744    /// entry-path list every `:kind Binario` caixa emits under
1745    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1746    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1747    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1748    /// downstream flake-build-facing consumer keys off) — the typed
1749    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1750    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1751    /// non-sandboxed-relative-shape rejected through
1752    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1753    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1754    /// directory paths rejected past the layout's
1755    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1756    /// onto every load-bearing downstream consumer the substrate carries
1757    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1758    /// per-entry file-exists + `exe/`-directory-fence loop at
1759    /// caixa-core/src/layout.rs that gates each entry through
1760    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1761    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1762    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1763    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1764    /// that fences code-surface slots off from the two no-code kinds,
1765    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1766    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1767    /// fences the `:exe` code surface off from every non-Binario code-
1768    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1769    /// that walks each entry through the sandbox-relative / cross-entry
1770    /// duplicate gates, every future per-`Caixa` executable-facing
1771    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1772    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1773    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1774    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1775    /// future `feira nix` per-executable Binario-target emit path).
1776    ///
1777    /// Prior to this lift the `.exe` field was accessed inline at three
1778    /// production sites — the compound-code-path `has_code =
1779    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1780    /// !caixa.servicos.is_empty()` OR-fold on the
1781    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1782    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1783    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1784    /// gate, the per-entry `for p in &caixa.exe`
1785    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1786    /// [`Self::declared_foreign_code_slots`]'s
1787    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1788    /// open-coded field-accesses that expressed no compile-time link
1789    /// back to the typed slot. A future extension of the `:exe` axis
1790    /// to a richer executable surface — a per-`:exe` structured
1791    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1792    /// layer once the substrate absorbs the per-executable
1793    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1794    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1795    /// the M4 CR materializer enforces per-CR (the "cluster policy
1796    /// demands every Binario declare an explicit `:wrapper`" arm), a
1797    /// promotion of the plain `Vec<String>` byte-string list to a
1798    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1799    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1800    /// fence already resolves through — would have had to be threaded
1801    /// through all four open-coded copies in lockstep or the layout
1802    /// gate, the shape validator, and the `feira nix` emit path would
1803    /// silently disagree on which executable paths a given [`Caixa`]
1804    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1805    /// satisfy layout while `feira nix` silently packaged a drifted
1806    /// other list, or vice versa). Lifting the resolution to a typed
1807    /// method on the substrate primitive means every downstream
1808    /// consumer of the caixa's per-`Caixa` executable-source surface
1809    /// reaches for exactly one typed dispatch — the resolver's accept-
1810    /// set migrates as a unit on any future axis addition.
1811    ///
1812    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1813    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1814    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1815    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1816    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1817    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1818    /// future lift closes onto (per the trio of code-surface list slots
1819    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1820    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1821    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1822    /// last unlifted code-surface slot). Sibling in shape to the peer
1823    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1824    /// (bc92bce), per-`:placement`
1825    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1826    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1827    /// (6c77e36), per-`:contratos`
1828    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1829    /// per-`:upgrade-from :instructions`
1830    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1831    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1832    /// typed-slot list axes, extended here to the outer top-level
1833    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1834    /// `&Vec<String>`) because every downstream consumer of the
1835    /// executable-source list treats it as a read-only sequence — the
1836    /// slice-view is the narrowest borrow that supports every
1837    /// present + roadmapped consumer (`.iter()`, `.len()`,
1838    /// `.is_empty()`) without leaking the backing `Vec`'s
1839    /// grow/push/reserve surface no consumer of the typed view
1840    /// reaches for (the storage-side `Vec` remains reachable through
1841    /// the `pub exe` field for the mutation-carrying serde
1842    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1843    /// to match the storage field's name; the accessor's identity
1844    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1845    /// docstring already carries.
1846    #[must_use]
1847    pub const fn exe(&self) -> &[String] {
1848        self.exe.as_slice()
1849    }
1850
1851    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1852    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1853    /// of the top-level manifest's Servico-component axis keys off —
1854    /// returns the author-declared `:servicos` list verbatim as a
1855    /// `&[String]` slice-view over the same backing buffer the raw
1856    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1857    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1858    /// form supplies with an empty `()` when unset; the
1859    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1860    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1861    /// definitionally carries a `Vec<String>` slot — possibly empty —
1862    /// and the returned `&[String]` degenerates to an empty slice on
1863    /// that arm without any silent `None` collapse).
1864    ///
1865    /// The `:servicos` slot carries the universal-axis
1866    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1867    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1868    /// author-facing surface every `defcaixa` form supplies alongside
1869    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1870    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1871    /// Servico-facing renderer keys off) — the typed slot's
1872    /// `Vec<String>` accept-set (empty-per-entry rejected through
1873    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1874    /// non-sandboxed-relative-shape rejected through
1875    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1876    /// extension rejected through
1877    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1878    /// entry duplicate rejected through
1879    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1880    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1881    /// renderer entry-points, out-of-`servicos/`-directory paths
1882    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1883    /// `starts_with` fence) maps onto every load-bearing downstream
1884    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1885    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1886    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1887    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1888    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1889    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1890    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1891    /// that fences code-surface slots off from the two no-code kinds,
1892    /// [`Self::declared_foreign_code_slots`]'s
1893    /// `!self.servicos.is_empty()` arm on the
1894    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1895    /// `:servicos` code surface off from every non-Servico code-running
1896    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1897    /// walks each entry through the sandbox-relative / `.computeunit.
1898    /// yaml`-extension / cross-entry duplicate gates, the
1899    /// [`crate::require_single_servico`] V0 singularity gate every
1900    /// per-Servico renderer entry-point runs through
1901    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1902    /// `feira deploy` per-verb `first_servico_path` walk at
1903    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1904    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1905    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1906    /// per-Servico OCI packager, the future M4
1907    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1908    /// per-Servico OTel collector-config emit).
1909    ///
1910    /// Prior to this lift the `.servicos` field was accessed inline at
1911    /// five production sites — the compound-code-path `has_code =
1912    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1913    /// !caixa.servicos.is_empty()` OR-fold on the
1914    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1915    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1916    /// `caixa.servicos.is_empty()`
1917    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1918    /// per-entry `for p in &caixa.servicos`
1919    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1920    /// [`Self::declared_foreign_code_slots`]'s
1921    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1922    /// and the [`crate::require_single_servico`] V0 count gate's
1923    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1924    /// projection (both the accept-arm predicate and the
1925    /// diagnostic-carrying `ServicoCountMismatch { count }`
1926    /// projection) — five open-coded field-accesses across three
1927    /// crates that expressed no compile-time link back to the typed
1928    /// slot. A future extension of the `:servicos` axis to a richer
1929    /// component surface — a per-`:servicos` structured
1930    /// `ServicoEntry { path, world, capabilities }` at the storage
1931    /// layer once the substrate absorbs the per-component WIT-world +
1932    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1933    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1934    /// materializer enforces per-CR (the "cluster policy demands every
1935    /// Servico declare an explicit `:world`" arm), a promotion of the
1936    /// plain `Vec<String>` byte-string list to a richer
1937    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1938    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1939    /// `starts_with(servicos_dir)` fence and the
1940    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1941    /// already resolve through, a promotion of the V0 singleton
1942    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1943    /// component-model multi-world boundary — would have had to be
1944    /// threaded through all five open-coded copies in lockstep or the
1945    /// layout gate, the shape validator, the V0 count gate, and the
1946    /// `feira chart` / `feira deploy` entry-point walks would silently
1947    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1948    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1949    /// yaml")` would satisfy layout while `feira chart` silently
1950    /// packaged a drifted other list, or vice versa). Lifting the
1951    /// resolution to a typed method on the substrate primitive means
1952    /// every downstream consumer of the caixa's per-`Caixa`
1953    /// ComputeUnit-CR-source surface reaches for exactly one typed
1954    /// dispatch — the resolver's accept-set migrates as a unit on any
1955    /// future axis addition.
1956    ///
1957    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1958    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1959    /// projection pattern [`Self::autores`] (b5d813f) opened,
1960    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1961    /// (8a36c23) closed the universal-axis text-tag family of, and
1962    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1963    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1964    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1965    /// a substrate-canonical slice accessor, the trio of code-surface
1966    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1967    /// tuple carries is complete on the typed dispatch surface (the
1968    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1969    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1970    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1971    /// per-element accessor swap in isolation — a future companion lift
1972    /// promotes the tuple's element type to `&[String]` and threads the
1973    /// triple of typed dispatches through as a unit). Sibling in shape
1974    /// to the peer per-`:supervisor`
1975    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1976    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1977    /// (a6e18d7), per-`:membros`
1978    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1979    /// per-`:contratos`
1980    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1981    /// per-`:upgrade-from :instructions`
1982    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1983    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1984    /// typed-slot list axes, extended here to the outer top-level
1985    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1986    /// `&Vec<String>`) because every downstream consumer of the
1987    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1988    /// the slice-view is the narrowest borrow that supports every
1989    /// present + roadmapped consumer (`.iter()`, `.len()`,
1990    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1991    /// grow/push/reserve surface no consumer of the typed view reaches
1992    /// for (the storage-side `Vec` remains reachable through the
1993    /// `pub servicos` field for the mutation-carrying serde round-trip
1994    /// and per-test fixture-mutation paths, and for the
1995    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1996    /// homogeneous-element-type shape carries the raw field access
1997    /// until the trio-closure lift promotes the tuple as a unit).
1998    /// Named `servicos()` to match the storage field's name; the
1999    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
2000    /// vocabulary the slot's docstring already carries.
2001    #[must_use]
2002    pub const fn servicos(&self) -> &[String] {
2003        self.servicos.as_slice()
2004    }
2005
2006    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
2007    /// runtime-dependency-declaration-list slice-accessor every consumer
2008    /// of the top-level manifest's runtime-dep-graph axis keys off —
2009    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
2010    /// slice-view over the same backing buffer the raw
2011    /// `self.deps.as_slice()` field access borrows from. Empty-list-
2012    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
2013    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
2014    /// derive folds an omitted `:deps` through `#[serde(default)]` to
2015    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
2016    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
2017    /// degenerates to an empty slice on that arm without any silent
2018    /// `None` collapse).
2019    ///
2020    /// The `:deps` slot carries the universal-axis runtime dependency
2021    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
2022    /// facing surface every `defcaixa` form supplies alongside `:nome` /
2023    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
2024    /// every downstream resolver-facing artifact emits under) — the
2025    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
2026    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
2027    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
2028    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
2029    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
2030    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
2031    /// maps onto every load-bearing downstream consumer the substrate
2032    /// carries — the [`Self::validate_deps`] per-entry
2033    /// [`Dep::validate`] + within-list dedup walk at
2034    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
2035    /// cross-list self-reference gate at caixa-core/src/layout.rs that
2036    /// checks each entry against the caixa's own `:nome`, the
2037    /// caixa-resolver `for dep in &root.deps` closure walk at
2038    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
2039    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
2040    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
2041    /// caixa-crd/src/conversion.rs that materializes each entry into the
2042    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
2043    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
2044    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
2045    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
2046    /// closure emit walk the caixa-resolver docstring roadmaps).
2047    ///
2048    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2049    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2050    /// sibling `:deps-dev` future lift closes on. Peer of the closed
2051    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
2052    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
2053    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
2054    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
2055    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
2056    /// pattern onto a novel element-type axis (`Dep` composite vs the
2057    /// prior sibling family's `String` scalar). Sibling in shape to the
2058    /// peer per-`:supervisor`
2059    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
2060    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
2061    /// (a6e18d7), per-`:membros`
2062    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
2063    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
2064    /// (0dcc926), and per-`:upgrade-from :instructions`
2065    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2066    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2067    /// typed-slot list axes, extended here to the outer top-level
2068    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
2069    /// (not `&Vec<Dep>`) because every downstream consumer of the
2070    /// runtime-dep list treats it as a read-only sequence — the slice-
2071    /// view is the narrowest borrow that supports every present +
2072    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2073    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2074    /// of the typed view reaches for (the storage-side `Vec` remains
2075    /// reachable through the `pub deps` field for the mutation-carrying
2076    /// serde round-trip and per-test fixture-mutation paths). Named
2077    /// `deps()` to match the storage field's name; the accessor's
2078    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
2079    /// slot's docstring already carries.
2080    #[must_use]
2081    pub const fn deps(&self) -> &[Dep] {
2082        self.deps.as_slice()
2083    }
2084
2085    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
2086    /// development-only-dependency-declaration-list slice-accessor every
2087    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
2088    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
2089    /// slice-view over the same backing buffer the raw
2090    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
2091    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
2092    /// form supplies with an empty `()` when unset; the
2093    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
2094    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
2095    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
2096    /// the returned `&[Dep]` degenerates to an empty slice on that arm
2097    /// without any silent `None` collapse).
2098    ///
2099    /// The `:deps-dev` slot carries the universal-axis dev-only
2100    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
2101    /// the author-facing sibling of `:deps` that every `defcaixa` form
2102    /// supplies to declare tests / lint / bench closures the runtime
2103    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
2104    /// axis every downstream test-facing artifact emits under, matching
2105    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
2106    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
2107    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
2108    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
2109    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
2110    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
2111    /// within-list duplicate `:nome` rejected through
2112    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
2113    /// load-bearing downstream consumer the substrate carries — the
2114    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
2115    /// dedup walk at caixa-core/src/manifest.rs, the
2116    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
2117    /// gate at caixa-core/src/layout.rs that checks each entry against
2118    /// the caixa's own `:nome`, the caixa-resolver
2119    /// `for dep in &root.deps_dev` closure walk at
2120    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
2121    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
2122    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
2123    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
2124    /// overlay the M4 CR materializer resolves per-CR, the future
2125    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
2126    /// roadmaps).
2127    ///
2128    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2129    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2130    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
2131    /// jointly close the two-list dep-graph surface every downstream
2132    /// resolver-facing consumer keys off (runtime `:deps` +
2133    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
2134    /// pair the [`Self::validate_deps`] gate already walks in canonical
2135    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
2136    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
2137    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
2138    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
2139    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
2140    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
2141    /// dev-dep composite-element axis (`Dep` composite, matching the
2142    /// [`Self::deps`] element type). Sibling in shape to the peer
2143    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
2144    /// (bc92bce), per-`:placement`
2145    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
2146    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
2147    /// (6c77e36), per-`:contratos`
2148    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
2149    /// per-`:upgrade-from :instructions`
2150    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2151    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2152    /// typed-slot list axes, folded here to the outer top-level
2153    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
2154    /// (not `&Vec<Dep>`) because every downstream consumer of the
2155    /// dev-dep list treats it as a read-only sequence — the slice-view
2156    /// is the narrowest borrow that supports every present +
2157    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2158    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2159    /// of the typed view reaches for (the storage-side `Vec` remains
2160    /// reachable through the `pub deps_dev` field for the mutation-
2161    /// carrying serde round-trip and per-test fixture-mutation paths).
2162    /// Named `deps_dev()` to match the storage field's `snake_case` name;
2163    /// the kebab-case author-surface tag `:deps-dev` is the same axis
2164    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
2165    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
2166    /// docstring already carries.
2167    #[must_use]
2168    pub const fn deps_dev(&self) -> &[Dep] {
2169        self.deps_dev.as_slice()
2170    }
2171
2172    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
2173    /// every consumer that walks one of the two dep-list axes keyed on a
2174    /// [`crate::dep::DepList`] discriminant reaches for — routes the
2175    /// `(list: DepList) -> &[Dep]` projection through one typed method on
2176    /// the substrate primitive rather than the prior open-coded
2177    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
2178    /// inline dispatch every per-axis walker would otherwise carry.
2179    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
2180    /// `&[Dep]` slice-view over the same backing buffer the sibling
2181    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
2182    /// accessors borrow from, preserving the empty-list-carrying invariant
2183    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
2184    /// are default-empty axes every `defcaixa` form supplies with an empty
2185    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
2186    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
2187    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
2188    /// returned `&[Dep]` degenerates to an empty slice on either arm
2189    /// without any silent `None` collapse).
2190    ///
2191    /// The [`crate::dep::DepList`] closed-set typed enum is the
2192    /// substrate's canonical discriminator for the "runtime-closure
2193    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
2194    /// consumer dispatches on — the compiler-checked exhaustiveness on
2195    /// the enum's `match` arms is the build-time guarantee that no future
2196    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
2197    /// that a future third dep-list axis (a `:deps-build` build-only
2198    /// closure once the substrate grows cross-artifact heterogeneous
2199    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
2200    /// consumer. Prior to this the read side carried two per-slot
2201    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
2202    /// typed dispatch that a per-axis walker could parametrise on, so
2203    /// every per-list walker (the [`Self::validate_deps`] per-list
2204    /// [`crate::render::insert_first_seen`] dedup walk, a future
2205    /// `feira app graph` per-list dep summary, a future M4 per-cluster
2206    /// dev-closure-audit overlay the CR materializer resolves per-CR)
2207    /// open-coded the same two-block "run over `:deps`, then run over
2208    /// `:deps-dev`" pattern — a silent duplication that a future third
2209    /// dep-list axis would have had to grow a third block at every site.
2210    ///
2211    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
2212    /// (359fba5) — closes the two-side dispatch symmetry on the outer
2213    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
2214    /// side, `deps_of` on the read side, both keyed on the same
2215    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
2216    /// the substrate primitive, thin projections at each consumer"
2217    /// discipline the sibling per-slot read accessors ([`Self::nome`]
2218    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
2219    /// the outer-[`Caixa`] typed-dispatch read surface.
2220    ///
2221    /// Declared `pub const fn` — every operator in the body is already
2222    /// `const`-callable (the [`crate::dep::DepList`] enum is a plain
2223    /// closed-set `#[derive(Copy)]` discriminator so the `match` arms
2224    /// are const-evaluable, and each arm forwards through the sibling
2225    /// `pub const fn` [`Self::deps`] / [`Self::deps_dev`] per-slot
2226    /// slice accessor). Pinned load-bearing by the paired
2227    /// [`caixa_deps_of_is_const_fn`][pin] wrapper test (a
2228    /// `const fn deps_of_via_const_fn(c: &Caixa, l: DepList) -> &[Dep]`
2229    /// that forwards through this accessor) — any future accidental
2230    /// downgrade to non-`const` fails the wrapper at caixa-core build
2231    /// time with E0015 (`cannot call non-const method`), strictly
2232    /// stronger than a runtime `assert!` and side-stepping the
2233    /// destructor-in-const restriction the `Caixa` fixture's owning
2234    /// carriers rule out on the direct-`const _: () = assert!(…)`
2235    /// residence. Peer of the sibling per-`Dep` outer-accessor
2236    /// family's parallel `const`-eval-surface pass and of the outer-
2237    /// `Caixa` slice-return accessor family's earlier pass (231a968)
2238    /// — same "one canonical dispatch per axis, `const`-eval posture
2239    /// pinned at the substrate primitive, thin projections at each
2240    /// consumer" discipline extended onto the outer-`Caixa`
2241    /// typed-dispatch read surface on the [`DepList`]-keyed dep-list
2242    /// axis.
2243    ///
2244    /// [DepList]: crate::dep::DepList
2245    /// [pin]: tests::caixa_deps_of_is_const_fn
2246    #[must_use]
2247    pub const fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
2248        match list {
2249            crate::dep::DepList::Prod => self.deps(),
2250            crate::dep::DepList::Dev => self.deps_dev(),
2251        }
2252    }
2253
2254    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
2255    /// consumer that appends to one of the two dep-list axes keys off
2256    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
2257    /// method on the substrate primitive rather than the prior
2258    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
2259    /// else { &mut caixa.deps }` inline dispatch + open-coded
2260    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
2261    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
2262    /// a within-list name collision — the same `list: &'static str`
2263    /// diagnostic shape [`Self::validate_deps`]'s per-list
2264    /// [`crate::render::insert_first_seen`] walk raises on the peer
2265    /// parse-time within-list dedup axis, so a future author reading a
2266    /// `feira add` refusal and a `feira build` refusal reaches for the
2267    /// same corrective surface without switching diagnostic idioms.
2268    ///
2269    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
2270    /// closed-set typed carrier for the "runtime-closure `:deps` vs
2271    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
2272    /// dispatches on — the compiler-checked exhaustiveness on the
2273    /// enum's `match` arms is the build-time guarantee that no future
2274    /// per-list mutation-site regresses to a bare-`bool`-flag
2275    /// (`is_dev: bool`) inline dispatch that a future third
2276    /// dep-list axis (a `:deps-build` build-only closure once the
2277    /// substrate grows cross-artifact heterogeneous dep-graphs, per
2278    /// CAIXA-SDLC §I) would silently split at every consumer.
2279    ///
2280    /// Same "one typed dispatch on the substrate primitive, thin
2281    /// projections at each consumer" discipline the sibling per-slot
2282    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
2283    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
2284    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
2285    /// the substrate's first typed-mutation dispatch on the top-level
2286    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
2287    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
2288    /// diagnostic path routed no through-line back to the typed slot,
2289    /// so a future extension of either dep-list axis to a richer author
2290    /// surface (a per-cluster override the operator pins through a
2291    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
2292    /// roadmap acknowledges, an M4
2293    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
2294    /// admission-webhook that normalized the list at admission time)
2295    /// would have had to be threaded through the `feira add` mutation
2296    /// site in lockstep with every read consumer or one path would
2297    /// silently disagree with the other on which list a given dep lands
2298    /// in. Lifting the resolution rule to a typed method on the
2299    /// substrate primitive means every downstream dep-list-mutating
2300    /// consumer of the top-level manifest reaches for exactly one typed
2301    /// dispatch — the resolver's accept-set migrates as a unit on any
2302    /// future axis addition.
2303    ///
2304    /// # Errors
2305    ///
2306    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
2307    /// when another entry in the same list already carries the same
2308    /// `:nome` — the mutation is refused and the caller can surface the
2309    /// typed diagnostic to the author (the `feira add` verb routes the
2310    /// error through `anyhow::Error::from`, which preserves the
2311    /// canonical `#[error(...)]`-templated diagnostic body).
2312    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
2313        let target = match list {
2314            crate::dep::DepList::Prod => &mut self.deps,
2315            crate::dep::DepList::Dev => &mut self.deps_dev,
2316        };
2317        if target.iter().any(|d| d.nome() == dep.nome()) {
2318            return Err(DepError::DuplicateNome {
2319                nome: dep.nome().to_string(),
2320                list: list.as_str(),
2321            });
2322        }
2323        target.push(dep);
2324        Ok(())
2325    }
2326
2327    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
2328    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
2329    /// composite-reference accessor every consumer of the top-level
2330    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
2331    /// off — returns the author-declared `:limits` typed composite
2332    /// verbatim as an `Option<&LimitsSpec>` reference over the same
2333    /// backing storage the raw `self.limits.as_ref()` field access
2334    /// borrows from, with `None` naming the "no `:limits` block
2335    /// authored — every per-axis Lunatic-sandbox cap defers to the
2336    /// wasm-engine-default arm named on the per-axis
2337    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
2338    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
2339    /// docstrings" partition every downstream Servico-M2-overlay
2340    /// emitter treats as "emit nothing" and the sibling
2341    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
2342    /// treats as "skip the per-axis
2343    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
2344    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
2345    ///
2346    /// The outer `:limits` slot carries the M2 Servico-runtime typed
2347    /// composite — the load-bearing container of every Lunatic-shaped
2348    /// per-process wasm32-sandbox cap axis every long-running wasm
2349    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
2350    /// Lunatic per-process linear-memory / fuel / wall-clock /
2351    /// millicore cap primitives translated onto pleme-io's typed
2352    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
2353    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2354    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2355    /// chart both fan on). Every per-`:limits` axis threads through a
2356    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
2357    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
2358    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
2359    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
2360    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
2361    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
2362    /// consumer that reaches for a limits axis first passes through
2363    /// this outer accessor onto the composite and then dispatches
2364    /// onto the per-axis accessor — the two-level dispatch means
2365    /// every per-`:limits` reader now routes through a typed dispatch
2366    /// on the substrate primitive at both altitudes.
2367    ///
2368    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
2369    /// was accessed inline at three production sites — the
2370    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
2371    /// `if let Some(l) = &caixa.limits { … }` traversal head
2372    /// (caixa-core/src/layout.rs:882, which drives the per-axis
2373    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
2374    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
2375    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
2376    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
2377    /// [`LimitsSpec::validate`] fans onto), the
2378    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
2379    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
2380    /// head (caixa-core/src/render.rs:18504, which drives the
2381    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
2382    /// projection every `caixa-helm` / `caixa-flux` Servico values-
2383    /// block emitter fans on), and the
2384    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2385    /// set enumerator's `self.limits.is_some()` presence probe
2386    /// (caixa-core/src/manifest.rs:1788, which drives the
2387    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
2388    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2389    /// gate reads) — three open-coded outer-field accesses that
2390    /// expressed no compile-time link back to the typed slot at the
2391    /// [`Caixa`] altitude. A future extension of the `:limits` outer
2392    /// axis to a richer author surface (a multi-`:limits` list the M4
2393    /// CR materializer resolves per-CR at admission time so a Servico
2394    /// can expose a compute-heavy + IO-heavy limits pair, a per-
2395    /// cluster `:limits-overrides` slot the operator pins so a
2396    /// cluster-specific policy can tighten a caixa-declared cap
2397    /// without re-authoring the `caixa.lisp`, a promotion of the
2398    /// plain `Option<LimitsSpec>` to a richer
2399    /// `{static, dynamic}` partition once the wasm-engine's runtime-
2400    /// resolved dynamic-cap surface lands) would have had to be
2401    /// threaded through all three open-coded copies in lockstep or
2402    /// one consumer would silently disagree with the peers on which
2403    /// limits composite a given Caixa resolves to — the layout gate's
2404    /// per-axis bracket-dispatch seed reading the raw slot while the
2405    /// peer `servico_m2_overlay` emitter read an operator-resolved
2406    /// slot would silently split the build-time sandbox-shape gate
2407    /// from the runtime `ComputeUnit` CR emission gate, a three-
2408    /// consumer split at the layout gate, the M2 overlay emitter, and
2409    /// the declared-slot enumerator far from the source `caixa.lisp`
2410    /// with no field naming the limits-drift root cause. Lifting the
2411    /// resolution rule to a typed method on the substrate primitive
2412    /// means every downstream consumer of the caixa's per-`Caixa`
2413    /// Lunatic-sandboxing outer-composite surface reaches for exactly
2414    /// one typed dispatch — the resolver's accept-set migrates as a
2415    /// unit on any future axis addition.
2416    ///
2417    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
2418    /// composite-reference accessor — opens the outer-`Caixa`
2419    /// `Option<&Composite>` composite-reference projection pattern the
2420    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
2421    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
2422    /// [`crate::aplicacao::Placement`] / `:entrada`
2423    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
2424    /// fold on. Peer of the M3 mesh-slot outer-composite family the
2425    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2426    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2427    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2428    /// accessors already close on the outer [`crate::AplicacaoSpec`]
2429    /// altitude — extends that "one typed dispatch on the substrate
2430    /// primitive, thin projections at each consumer" discipline onto
2431    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2432    /// runtime slot family's outer-composite axis. Returns
2433    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2434    /// clone) because every downstream consumer of the limits
2435    /// composite treats it as a read-only per-axis dispatch source —
2436    /// the reference-view is the narrowest borrow that supports every
2437    /// present + roadmapped consumer (per-axis accessor dispatch,
2438    /// `.is_empty()`-gated overlay projection, presence-probe early
2439    /// return on the "author-omitted `:limits` ⇒ engine-default
2440    /// applies" partition) without cloning the composite through
2441    /// every consumer's fast path. The `Option` half of the return-
2442    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2443    /// engine-default applies" partition (not a default composite the
2444    /// downstream must reject on emptiness) — the accessor projects
2445    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2446    /// reference-return unchanged. Named `limits()` to match the
2447    /// storage field's name verbatim and the tatara-lisp author-
2448    /// surface term (`:limits`) the field's own docstring already
2449    /// carries.
2450    #[must_use]
2451    pub const fn limits(&self) -> Option<&LimitsSpec> {
2452        self.limits.as_ref()
2453    }
2454
2455    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2456    /// composite OTP-`gen_server`-shaped callback-table optional-
2457    /// composite-reference accessor every consumer of the top-level
2458    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2459    /// keys off — returns the author-declared `:behavior` typed
2460    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2461    /// the same backing storage the raw `self.behavior.as_ref()` field
2462    /// access borrows from, with `None` naming the "no `:behavior`
2463    /// block authored — every per-callback OTP-shaped hook defers to
2464    /// the wasm-engine's runtime default arm named on the per-axis
2465    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2466    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2467    /// [`BehaviorSpec::on_state_change`] /
2468    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2469    /// partition every downstream Servico-M2-overlay emitter treats as
2470    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2471    /// per-`:behavior` shape gate treats as "skip the per-arm
2472    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2473    /// per-callback on-disk `MissingEntry` existence check".
2474    ///
2475    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2476    /// composite — the load-bearing container of every OTP-shaped
2477    /// per-Servico lifecycle-callback path axis every long-running wasm
2478    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2479    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2480    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2481    /// translated onto pleme-io's typed `:behavior :on-init` /
2482    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2483    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2484    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2485    /// chart both fan on). Every per-`:behavior` axis threads through a
2486    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2487    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2488    /// Every downstream consumer that reaches for a behavior axis
2489    /// first passes through this outer accessor onto the composite
2490    /// and then dispatches onto the per-callback accessor — the
2491    /// two-level dispatch means every per-`:behavior` reader now
2492    /// routes through a typed dispatch on the substrate primitive at
2493    /// both altitudes.
2494    ///
2495    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2496    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2497    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2498    /// keys the "per-version `:state-change` instruction must have a
2499    /// `:on-state-change` callback" precondition off this accessor's
2500    /// composite (the callback-side counterpart to the
2501    /// `:upgrade-from :instructions :state-change :script` refusal at
2502    /// the appup-side). Threading that gate's traversal input through
2503    /// this accessor closes the cross-slot invariant on the substrate
2504    /// primitive, not on the raw field.
2505    ///
2506    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2507    /// composite was accessed inline at four production sites — the
2508    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2509    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2510    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2511    /// `BehaviorError` refusal cascade + the per-callback on-disk
2512    /// [`crate::LayoutError::MissingEntry`] existence check under
2513    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2514    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2515    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2516    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2517    /// drives the `:state-change` ↔ `:on-state-change` precondition
2518    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2519    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2520    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2521    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2522    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2523    /// Servico values-block emitter fans on), and the
2524    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2525    /// set enumerator's `self.behavior.is_some()` presence probe
2526    /// (caixa-core/src/manifest.rs:1919, which drives the
2527    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2528    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2529    /// gate reads) — four open-coded outer-field accesses that
2530    /// expressed no compile-time link back to the typed slot at the
2531    /// [`Caixa`] altitude. A future extension of the `:behavior`
2532    /// outer axis to a richer author surface (a per-callback overlay
2533    /// resolver the operator materializes at admission time so a
2534    /// cluster-specific policy can inject a per-callback tracing
2535    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2536    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2537    /// dynamic}` partition once a runtime-resolved behavior-swap
2538    /// surface lands, the M4 per-callback middleware chain the
2539    /// caixa-operator's per-Servico admission webhook keys off) would
2540    /// have had to be threaded through all four open-coded copies in
2541    /// lockstep or one consumer would silently disagree with the
2542    /// peers on which behavior composite a given Caixa resolves to —
2543    /// the layout gate's per-callback existence-check seed reading
2544    /// the raw slot while the peer `servico_m2_overlay` emitter read
2545    /// an operator-resolved slot would silently split the build-time
2546    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2547    /// gate from the cross-slot `:state-change` composition gate from
2548    /// the M2 declared-slot enumerator, a four-consumer split far
2549    /// from the source `caixa.lisp` with no field naming the
2550    /// behavior-drift root cause. Lifting the resolution rule to a
2551    /// typed method on the substrate primitive means every downstream
2552    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2553    /// composite surface reaches for exactly one typed dispatch — the
2554    /// resolver's accept-set migrates as a unit on any future axis
2555    /// addition.
2556    ///
2557    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2558    /// composite-reference accessor — sibling to the opening
2559    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2560    /// `Option<&Composite>` composite-reference sub-family, extends
2561    /// the "one typed dispatch on the substrate primitive, thin
2562    /// projections at each consumer" discipline onto the second of
2563    /// the three M2 Servico-runtime slots. The remaining
2564    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2565    /// altitude — the M3 mesh-slot family (`:politicas`,
2566    /// `:placement`, `:entrada` — already closed on the inner
2567    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2568    /// d32111c) — remain the future sibling lifts on the outer
2569    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2570    /// the owning composite by copy or clone) because every
2571    /// downstream consumer of the behavior composite treats it as a
2572    /// read-only per-callback dispatch source — the reference-view is
2573    /// the narrowest borrow that supports every present + roadmapped
2574    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2575    /// overlay projection, presence-probe early return on the
2576    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2577    /// partition, cross-slot `:state-change` composition input)
2578    /// without cloning the composite through every consumer's fast
2579    /// path. The `Option` half of the return-type preserves the
2580    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2581    /// applies" partition (not a default composite the downstream
2582    /// must reject on emptiness) — the accessor projects the raw
2583    /// `Option<BehaviorSpec>` slot's presence bit through the
2584    /// reference-return unchanged. Named `behavior()` to match the
2585    /// storage field's name verbatim and the tatara-lisp author-
2586    /// surface term (`:behavior`) the field's own docstring already
2587    /// carries.
2588    #[must_use]
2589    pub const fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2590        self.behavior.as_ref()
2591    }
2592
2593    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2594    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2595    /// reference accessor every consumer of the top-level manifest's
2596    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2597    /// reader keys off — returns the author-declared `:politicas` typed
2598    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2599    /// same backing storage the raw `self.politicas.as_ref()` field
2600    /// access borrows from, with `None` naming the "no `:politicas`
2601    /// block authored — every per-axis mesh-policy scalar defers to the
2602    /// cluster-default arm named on the per-axis
2603    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2604    /// [`crate::aplicacao::MeshPolicy::retries`] /
2605    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2606    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2607    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2608    /// docstrings" partition every downstream caixa-mesh /
2609    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2610    /// "emit no per-`:politicas` overlay" and the sibling
2611    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2612    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2613    /// arm.
2614    ///
2615    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2616    /// Aplicacao typed composite — the load-bearing container of every
2617    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2618    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2619    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2620    /// composite; §V — the "no infinite blocking" per-call deadline +
2621    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2622    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2623    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2624    /// threads through a lifted per-slot accessor on the
2625    /// [`crate::aplicacao::MeshPolicy`] type: the
2626    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2627    /// mTLS-enforcement toggle, the
2628    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2629    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2630    /// (7073d0f) Gateway-API per-call deadline, the
2631    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2632    /// Envoy-outlier-detection composite. Every downstream consumer
2633    /// that reaches for a mesh-policy axis first passes through this
2634    /// outer accessor onto the composite and then dispatches onto the
2635    /// per-axis accessor — the two-level dispatch means every per-
2636    /// `:politicas` reader now routes through a typed dispatch on the
2637    /// substrate primitive at both altitudes.
2638    ///
2639    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2640    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2641    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2642    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2643    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2644    /// composite whether or not the author declared the outer slot.
2645    /// The outer accessor preserves the "author-omitted vs authored-
2646    /// empty" partition the inner accessor's `is_empty()`-gated
2647    /// renderer overlay collapses — routing the presence bit through
2648    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2649    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2650    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2651    ///
2652    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2653    /// composite was accessed inline at two production sites — the
2654    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2655    /// `self.politicas.clone().unwrap_or_default()` traversal head
2656    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2657    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2658    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2659    /// then observes), and the [`Self::declared_mesh_slots`] M3
2660    /// declared-slot-set enumerator's `self.politicas.is_some()`
2661    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2662    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2663    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2664    /// coherence gate reads) — two open-coded outer-field accesses
2665    /// that expressed no compile-time link back to the typed slot at
2666    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2667    /// outer axis to a richer author surface (a per-cluster
2668    /// `:politicas-overrides` slot the operator materializes at
2669    /// admission time so a cluster-specific policy can tighten the
2670    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2671    /// promotion of the plain `Option<MeshPolicy>` to a richer
2672    /// `{static, dynamic}` partition once the M4 per-edge
2673    /// contrato-scoped policy-override surface lands, the M5 traffic-
2674    /// shaping composition the caixa-operator's per-Aplicacao mesh
2675    /// admission webhook keys off) would have had to be threaded
2676    /// through both open-coded copies in lockstep or the Aplicacao-
2677    /// composition seed's default-fold arm would silently disagree
2678    /// with the M3 declared-slot enumerator on which policy composite
2679    /// a given Caixa resolves to — the seed reading an operator-
2680    /// resolved slot while the enumerator's presence probe read the
2681    /// raw slot would silently split the build-time mesh-artifact
2682    /// emission gate from the M3 declared-slot enumerator's kind-
2683    /// coherence gate, a two-consumer split far from the source
2684    /// `caixa.lisp` with no field naming the policy-drift root cause.
2685    /// Lifting the resolution rule to a typed method on the substrate
2686    /// primitive means every downstream consumer of the caixa's per-
2687    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2688    /// reaches for exactly one typed dispatch — the resolver's
2689    /// accept-set migrates as a unit on any future axis addition.
2690    ///
2691    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2692    /// composite-reference accessor — sibling to the opening
2693    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2694    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2695    /// reference sub-family, extends the "one typed dispatch on the
2696    /// substrate primitive, thin projections at each consumer"
2697    /// discipline onto the first of the three M3 mesh-slot axes.
2698    /// Peer of the closed inner mesh-slot outer-composite family the
2699    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2700    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2701    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2702    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2703    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2704    /// mesh-slot arm of the composite-reference family the remaining
2705    /// two axes (`:placement`, `:entrada`) fold onto in future
2706    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2707    /// composite by copy or clone) because every downstream consumer
2708    /// of the mesh-policy composite treats it as a read-only per-axis
2709    /// dispatch source — the reference-view is the narrowest borrow
2710    /// that supports every present + roadmapped consumer (per-axis
2711    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2712    /// presence-probe early return on the "author-omitted `:politicas`
2713    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2714    /// seed's default-fold arm) without cloning the composite through
2715    /// every consumer's fast path. The `Option` half of the return-
2716    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2717    /// cluster-default applies" partition (not a default composite
2718    /// the downstream must reject on emptiness) — the accessor
2719    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2720    /// through the reference-return unchanged. Named `politicas()` to
2721    /// match the storage field's name verbatim and the tatara-lisp
2722    /// author-surface term (`:politicas`) the field's own docstring
2723    /// already carries.
2724    #[must_use]
2725    pub const fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2726        self.politicas.as_ref()
2727    }
2728
2729    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2730    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2731    /// reference accessor every consumer of the top-level manifest's
2732    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2733    /// reader keys off — returns the author-declared `:placement` typed
2734    /// composite verbatim as an `Option<&Placement>` reference over the
2735    /// same backing storage the raw `self.placement.as_ref()` field
2736    /// access borrows from, with `None` naming the "no `:placement`
2737    /// block authored — every per-axis placement scalar defers to the
2738    /// cluster-default arm named on the per-axis
2739    /// [`crate::aplicacao::Placement::estrategia`] /
2740    /// [`crate::aplicacao::Placement::clusters`] /
2741    /// [`crate::aplicacao::Placement::affinity`] /
2742    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2743    /// docstrings" partition every downstream caixa-mesh /
2744    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2745    /// "emit no per-`:placement` overlay" and the sibling
2746    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2747    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2748    ///
2749    /// The outer `:placement` slot carries the M3 mesh-slot per-
2750    /// Aplicacao typed distribution composite — the load-bearing
2751    /// container of every where-does-this-Aplicacao-run axis every
2752    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2753    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2754    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2755    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2756    /// Aplicacao's typed distribution composite; §V CSE invariants —
2757    /// "distribution is a first-class typed composite, not a runtime
2758    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2759    /// typed inter-Servico contrato-edge overlay the per-cluster
2760    /// mesh renderer keys off). Every per-`:placement` axis threads
2761    /// through a lifted per-slot accessor on the
2762    /// [`crate::aplicacao::Placement`] type: the
2763    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2764    /// MESH-COMPOSITION distribution-strategy scalar, the
2765    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2766    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2767    /// M3-Adaptive-compression-hint optional-scalar, and the
2768    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2769    /// sharding extractor-expression optional-scalar. Every downstream
2770    /// consumer that reaches for a placement axis first passes through
2771    /// this outer accessor onto the composite and then dispatches onto
2772    /// the per-axis accessor — the two-level dispatch means every per-
2773    /// `:placement` reader now routes through a typed dispatch on the
2774    /// substrate primitive at both altitudes.
2775    ///
2776    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2777    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2778    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2779    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2780    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2781    /// whether or not the author declared the outer slot. The outer
2782    /// accessor preserves the "author-omitted vs authored-empty" partition
2783    /// the inner accessor collapses at the cluster-default fold —
2784    /// routing the presence bit through this accessor keeps the
2785    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2786    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2787    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2788    /// dispatch.
2789    ///
2790    /// Prior to this lift the `.placement` `Option<Placement>`
2791    /// composite was accessed inline at two production sites — the
2792    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2793    /// `self.placement.clone().unwrap_or_default()` traversal head
2794    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2795    /// the [`crate::aplicacao::Placement::default`] cluster-default
2796    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2797    /// then observes), and the [`Self::declared_mesh_slots`] M3
2798    /// declared-slot-set enumerator's `self.placement.is_some()`
2799    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2800    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2801    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2802    /// coherence gate reads) — two open-coded outer-field accesses
2803    /// that expressed no compile-time link back to the typed slot at
2804    /// the [`Caixa`] altitude. A future extension of the `:placement`
2805    /// outer axis to a richer author surface (a per-cluster
2806    /// `:placement-overrides` slot the operator materializes at
2807    /// admission time so a cluster-specific placement can tighten the
2808    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2809    /// per-tenant placement-alias table the M4
2810    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2811    /// per-CR at admission time, a promotion of the plain
2812    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2813    /// once Orleans-style virtual-actor dynamic placement comes into
2814    /// typed scope) would have had to be threaded through both open-
2815    /// coded copies in lockstep or the Aplicacao-composition seed's
2816    /// default-fold arm would silently disagree with the M3 declared-
2817    /// slot enumerator on which distribution composite a given Caixa
2818    /// resolves to — the seed reading an operator-resolved slot while
2819    /// the enumerator's presence probe read the raw slot would
2820    /// silently split the build-time distribution-artifact emission
2821    /// gate from the M3 declared-slot enumerator's kind-coherence
2822    /// gate, a two-consumer split far from the source `caixa.lisp`
2823    /// with no field naming the distribution-drift root cause.
2824    /// Lifting the resolution rule to a typed method on the substrate
2825    /// primitive means every downstream consumer of the caixa's per-
2826    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2827    /// reaches for exactly one typed dispatch — the resolver's
2828    /// accept-set migrates as a unit on any future axis addition.
2829    ///
2830    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2831    /// composite-reference accessor — sibling to the opening
2832    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2833    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2834    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2835    /// composite-reference sub-family, folds on the "one typed
2836    /// dispatch on the substrate primitive, thin projections at each
2837    /// consumer" discipline extended onto the second of the three M3
2838    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2839    /// composite family the sibling
2840    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2841    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2842    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2843    /// accessor pins already close on the inner
2844    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2845    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2846    /// [`Self::politicas`] opened, extending the discipline onto the
2847    /// second of the three M3 mesh-slot axes. The remaining M3
2848    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2849    /// discipline in the final sibling lift, closing the outer top-
2850    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2851    /// Returns `Option<&Placement>` (not the owning composite by copy
2852    /// or clone) because every downstream consumer of the placement
2853    /// composite treats it as a read-only per-axis dispatch source —
2854    /// the reference-view is the narrowest borrow that supports every
2855    /// present + roadmapped consumer (per-axis accessor dispatch,
2856    /// serde composite-serialization on the programs.yaml overlay,
2857    /// presence-probe early return on the "author-omitted `:placement`
2858    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2859    /// seed's default-fold arm) without cloning the composite through
2860    /// every consumer's fast path. The `Option` half of the return-
2861    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2862    /// cluster-default applies" partition (not a default composite
2863    /// the downstream must reject on emptiness) — the accessor
2864    /// projects the raw `Option<Placement>` slot's presence bit
2865    /// through the reference-return unchanged. Named `placement()` to
2866    /// match the storage field's name verbatim and the tatara-lisp
2867    /// author-surface term (`:placement`) the field's own docstring
2868    /// already carries.
2869    #[must_use]
2870    pub const fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2871        self.placement.as_ref()
2872    }
2873
2874    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2875    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2876    /// composite-reference accessor every consumer of the top-level
2877    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2878    /// composite reader keys off — returns the author-declared
2879    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2880    /// reference over the same backing storage the raw
2881    /// `self.entrada.as_ref()` field access borrows from, with `None`
2882    /// naming the "no `:entrada` block authored — this Aplicacao is
2883    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2884    /// partition every downstream caixa-mesh Gateway-API artifact
2885    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2886    /// backend for this Aplicacao" and the sibling
2887    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2888    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2889    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2890    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2891    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2892    /// the same `Option<&Entrada>` presence bit unchanged).
2893    ///
2894    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2895    /// Aplicacao typed external-gateway composite — the load-bearing
2896    /// container of every how-does-the-outside-world-reach-this-
2897    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2898    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2899    /// external-entry composite; §V CSE invariants — "the external
2900    /// gateway is a first-class typed composite, not a per-Servico
2901    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2902    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2903    /// API renderer keys off). Every per-`:entrada` axis threads
2904    /// through a lifted per-slot accessor on the
2905    /// [`crate::aplicacao::Entrada`] type: the
2906    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2907    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2908    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2909    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2910    /// backend `trigger.service.port` scalar, and the
2911    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2912    /// resolver every HTTPRoute-aware renderer consumes. Every
2913    /// downstream consumer that reaches for an entry axis first passes
2914    /// through this outer accessor onto the composite and then
2915    /// dispatches onto the per-axis accessor — the two-level dispatch
2916    /// means every per-`:entrada` reader now routes through a typed
2917    /// dispatch on the substrate primitive at both altitudes.
2918    ///
2919    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2920    /// seed: the Aplicacao-view builder forwards the outer `Option`
2921    /// arm verbatim (no default fold — `:entrada` is inherently
2922    /// optional; a cluster-internal Aplicacao has no external gateway
2923    /// at all, not "an external gateway that defaults to nothing"), so
2924    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2925    /// `Option<&Entrada>`-return accessor observes the same presence
2926    /// bit whether or not the author declared the outer slot. Routing
2927    /// the presence bit through this accessor keeps the
2928    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2929    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2930    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2931    /// hostname/backend/path emission dispatch.
2932    ///
2933    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2934    /// was accessed inline at two production sites — the
2935    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2936    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2937    /// which drives the forward onto the peer inner
2938    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2939    /// Gateway-API fan-out then observes), and the
2940    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2941    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2942    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2943    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2944    /// kind-coherence gate reads) — two open-coded outer-field
2945    /// accesses that expressed no compile-time link back to the typed
2946    /// slot at the [`Caixa`] altitude. A future extension of the
2947    /// `:entrada` outer axis to a richer author surface (a per-cluster
2948    /// `:entrada-overrides` slot the operator materializes at admission
2949    /// time so a cluster-specific hostname can pin the caixa-declared
2950    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2951    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2952    /// CR materializer resolves per-CR at admission time, a promotion
2953    /// of the plain `Option<Entrada>` to a richer
2954    /// `{public, private, internal}` partition once Cilium-identity-
2955    /// scoped internal gateways come into typed scope) would have had
2956    /// to be threaded through both open-coded copies in lockstep or the
2957    /// Aplicacao-composition seed's forward arm would silently
2958    /// disagree with the M3 declared-slot enumerator on which external-
2959    /// gateway composite a given Caixa resolves to — the seed reading
2960    /// an operator-resolved slot while the enumerator's presence probe
2961    /// read the raw slot would silently split the build-time gateway-
2962    /// artifact emission gate from the M3 declared-slot enumerator's
2963    /// kind-coherence gate, a two-consumer split far from the source
2964    /// `caixa.lisp` with no field naming the entry-drift root cause.
2965    /// Lifting the resolution rule to a typed method on the substrate
2966    /// primitive means every downstream consumer of the caixa's per-
2967    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2968    /// surface reaches for exactly one typed dispatch — the resolver's
2969    /// accept-set migrates as a unit on any future axis addition.
2970    ///
2971    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2972    /// return composite-reference accessor — closes the outer-`Caixa`
2973    /// `Option<&Composite>` composite-reference sub-family opened by
2974    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2975    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2976    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2977    /// folds on the "one typed dispatch on the substrate primitive,
2978    /// thin projections at each consumer" discipline extended onto the
2979    /// third and final M3 mesh-slot axis. Peer of the closed inner
2980    /// mesh-slot outer-composite family the sibling
2981    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2982    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2983    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2984    /// accessor pins already close on the inner
2985    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2986    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2987    /// altitudes of the outer-composite reference-return discipline
2988    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2989    /// slot presence) now carry the full five-arm accept-set behind a
2990    /// typed dispatch on the substrate primitive. Returns
2991    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2992    /// because every downstream consumer of the entrada composite
2993    /// treats it as a read-only per-axis dispatch source — the
2994    /// reference-view is the narrowest borrow that supports every
2995    /// present + roadmapped consumer (per-axis accessor dispatch,
2996    /// serde composite-serialization on the programs.yaml overlay,
2997    /// presence-probe early return on the "author-omitted `:entrada`
2998    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2999    /// seed's forward arm) without cloning the composite through every
3000    /// consumer's fast path. The `Option` half of the return-type
3001    /// preserves the load-bearing "author-omitted `:entrada` ⇒
3002    /// cluster-internal Aplicacao" partition (not a default composite
3003    /// the downstream must reject on emptiness — a cluster-internal
3004    /// Aplicacao has no external gateway at all, not "a default gateway
3005    /// that emits nothing"); the accessor projects the raw
3006    /// `Option<Entrada>` slot's presence bit through the reference-
3007    /// return unchanged. Named `entrada()` to match the storage field's
3008    /// name verbatim and the tatara-lisp author-surface term
3009    /// (`:entrada`) the field's own docstring already carries.
3010    #[must_use]
3011    pub const fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
3012        self.entrada.as_ref()
3013    }
3014
3015    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
3016    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
3017    /// an `Option<&CiRun>`, borrowed from the typed slot's own
3018    /// `Option<CiRun>` storage. `None` when the slot is absent (every
3019    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
3020    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
3021    /// not silently accepted).
3022    ///
3023    /// Named `ci()` to match the storage field's name and the
3024    /// tatara-lisp author surface (`:ci`); mirrors the sibling
3025    /// `Option<&Composite>` accessors on this same `Caixa` altitude
3026    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
3027    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
3028    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
3029    /// at every consumer.
3030    #[must_use]
3031    pub const fn ci(&self) -> Option<&canteiro_types::CiRun> {
3032        self.ci.as_ref()
3033    }
3034
3035    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
3036    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
3037    /// accessor every consumer of the top-level manifest's per-Supervisor
3038    /// restart-strategy axis keys off — returns the author-declared
3039    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
3040    /// `Copy`-projected from the typed slot's own
3041    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
3042    /// (`:estrategia` is a flat-spread supervisor-only slot every
3043    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
3044    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3045    /// still omit to defer to [`RestartStrategy::default`] —
3046    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
3047    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
3048    /// [`SupervisorSpec::default`]-inherited strategy without any silent
3049    /// promotion to a fresh explicit variant at the accessor boundary).
3050    ///
3051    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
3052    /// restart-strategy discriminant every substrate-side per-Supervisor
3053    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
3054    /// closed-set `one_for_one | one_for_all | rest_for_one |
3055    /// simple_one_for_one` algebra translated onto pleme-io's typed
3056    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
3057    /// slot algebra the operator's hierarchical reconciliation scheduler
3058    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
3059    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
3060    /// supervisor slots are flat on Caixa (vs nested under a
3061    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
3062    /// level of nesting"), so the accessor's altitude is the outer
3063    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
3064    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
3065    /// (eafb619) accessor keys off. The two typed axes — the outer
3066    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
3067    /// (author-omitted arm carried as `None`) and the inner post-
3068    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
3069    /// (`Option` collapsed through the [`Self::supervisor_view`]
3070    /// `unwrap_or_default()` fold) — now share one accessor discipline for
3071    /// the shared substrate concept "the author-declared OTP-shaped
3072    /// sibling-restart-strategy variant that partitions the downstream
3073    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
3074    /// `None` arm is the pre-composition presence bit every declared-slot
3075    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
3076    /// inner-altitude non-`Option` `RestartStrategy` is the post-
3077    /// composition partition-dispatch input every strategy-arm consumer
3078    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
3079    /// Supervisor sibling-restart branch, the future M4
3080    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
3081    /// webhook) fans on.
3082    ///
3083    /// Prior to this lift the `.estrategia` field was accessed inline at
3084    /// two production sites in `caixa-core/src/manifest.rs` — the
3085    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
3086    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
3087    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3088    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
3089    /// `SupervisorSpec` construction site at `estrategia:
3090    /// self.estrategia.unwrap_or_default()` (which composes the flat-
3091    /// spread outer author-surface `Option<RestartStrategy>` onto the
3092    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
3093    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
3094    /// coded field-accesses that expressed no compile-time link back to
3095    /// the typed slot. A future extension of the outer `:estrategia` axis
3096    /// to a richer author surface (a per-cluster strategy override the
3097    /// operator pins through a future `:estrategia-overrides` overlay the
3098    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
3099    /// a per-tenant strategy-alias table the M4 CR materializer resolves
3100    /// per-CR, a per-Supervisor dynamic strategy derivation the future
3101    /// adaptive-supervision engine computes from child-failure-history
3102    /// topology, a per-child-cohort strategy split the future
3103    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
3104    /// absorption roadmap acknowledges, a promotion of the plain
3105    /// `Option<RestartStrategy>` to a richer
3106    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
3107    /// operator-resolved overlay lands) would have had to be threaded
3108    /// through both open-coded copies in lockstep or the enumerator's
3109    /// presence probe and the composition site's `unwrap_or_default()`
3110    /// fold would silently disagree on which strategy a given [`Caixa`]
3111    /// resolves to (an author's `:estrategia OneForAll` would satisfy
3112    /// the enumerator's presence probe while the composition site
3113    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
3114    /// the resolution rule to a typed method on the substrate primitive
3115    /// means every downstream consumer of the caixa's per-`Caixa` outer-
3116    /// altitude sibling-restart-strategy surface reaches for exactly one
3117    /// typed dispatch — the resolver's accept-set migrates as a unit on
3118    /// any future axis addition.
3119    ///
3120    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3121    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3122    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
3123    /// projection pattern the sibling per-`Caixa` `:max-restarts`
3124    /// `Option<u32>` and (through the future duration-newtype landing)
3125    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
3126    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
3127    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
3128    /// the post-composition [`SupervisorSpec`] altitude — same "one
3129    /// typed dispatch on the substrate primitive, thin projections at
3130    /// each consumer" discipline extended onto the pre-composition outer
3131    /// author-surface [`Caixa`] altitude for the same OTP-shaped
3132    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
3133    /// `Option<&Composite>` composite-reference family the sibling
3134    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3135    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3136    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
3137    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
3138    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
3139    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
3140    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
3141    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
3142    /// pins on the inner-altitude per-`:placement` composite. Named
3143    /// `estrategia()` to match the storage field's name and the
3144    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
3145    /// / per-[`crate::aplicacao::Placement`] peer
3146    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
3147    /// verbatim; the accessor's identity name maps onto the canonical
3148    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
3149    /// docstring already carries.
3150    #[must_use]
3151    pub const fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
3152        self.estrategia
3153    }
3154
3155    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
3156    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
3157    /// scalar accessor every consumer of the top-level manifest's per-
3158    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
3159    /// returns the author-declared `:max-restarts` typed `Option<u32>`
3160    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
3161    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
3162    /// accessor returns by value; no borrow of `&self` past the call).
3163    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
3164    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
3165    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3166    /// still omit to defer to the [`Self::supervisor_view`]
3167    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
3168    ///
3169    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
3170    /// `MaxIntensity` restart-budget count that pairs with the sibling
3171    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
3172    /// restart-intensity ratio the supervisor trips its own escalation on
3173    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
3174    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
3175    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
3176    /// reconciliation scheduler fans on). The slot is *flat-spread* on
3177    /// the outer top-level `Caixa` (per the field-shape docstring at
3178    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
3179    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
3180    /// accessor's altitude is the outer [`Caixa`] surface rather than the
3181    /// composed [`SupervisorSpec`] altitude the sibling
3182    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
3183    /// off. The two typed axes — the outer author-surface `Option<u32>`
3184    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
3185    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
3186    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
3187    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
3188    /// shared substrate concept "the author-declared OTP-shaped
3189    /// restart-budget count every downstream per-Supervisor consumer's
3190    /// restart-intensity budget-vs-count comparator fans on".
3191    ///
3192    /// Prior to this lift the `.max_restarts` field was accessed inline
3193    /// at two production sites in `caixa-core/src/manifest.rs` — the
3194    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
3195    /// presence-probe arm at `if self.max_restarts.is_some()` (which
3196    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3197    /// kind-coherence gate's per-slot label push) and the
3198    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
3199    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
3200    /// flat-spread outer author-surface `Option<u32>` onto the inner
3201    /// post-composition [`SupervisorSpec`] `u32` field the
3202    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
3203    /// coded field-accesses that expressed no compile-time link back to
3204    /// the typed slot. A future extension of the outer `:max-restarts`
3205    /// axis to a richer author surface (a per-cluster restart-budget
3206    /// override the operator pins through a future `:max-restarts-overrides`
3207    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
3208    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
3209    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
3210    /// budget derivation the future adaptive-supervision engine computes
3211    /// from child-failure-history topology, a promotion of the plain
3212    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
3213    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
3214    /// per-child-cohort roadmap lands) would have had to be threaded
3215    /// through both open-coded copies in lockstep or the enumerator's
3216    /// presence probe and the composition site's `unwrap_or(5)` fold
3217    /// would silently disagree on which restart-budget a given [`Caixa`]
3218    /// resolves to (an author's `:max-restarts 10` would satisfy the
3219    /// enumerator's presence probe while the composition site silently
3220    /// composed the OTP-canonical `5`, or vice versa). Lifting the
3221    /// resolution rule to a typed method on the substrate primitive means
3222    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
3223    /// restart-budget-count surface reaches for exactly one typed dispatch
3224    /// — the resolver's accept-set migrates as a unit on any future axis
3225    /// addition.
3226    ///
3227    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3228    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3229    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
3230    /// projection pattern the sibling per-`Caixa`
3231    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
3232    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
3233    /// Peer of the inner-altitude
3234    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
3235    /// on the post-composition [`SupervisorSpec`] altitude — same "one
3236    /// typed dispatch on the substrate primitive, thin projections at
3237    /// each consumer" discipline extended onto the pre-composition outer
3238    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
3239    /// shaped restart-budget-count axis. Named `max_restarts()` to match
3240    /// the storage field's name and the per-[`SupervisorSpec`] peer
3241    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
3242    /// discipline verbatim; the accessor's identity maps onto the
3243    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
3244    /// field's docstring already carries.
3245    #[must_use]
3246    pub const fn max_restarts(&self) -> Option<u32> {
3247        self.max_restarts
3248    }
3249
3250    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
3251    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
3252    /// denominator raw-duration-string scalar accessor every consumer of
3253    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
3254    /// window axis keys off — returns the author-declared `:restart-window`
3255    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
3256    /// from the typed slot's own `Option<String>` storage. `None` when
3257    /// the slot is absent (the canonical "never reset — every restart
3258    /// across the supervisor's lifetime counts against the sibling
3259    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
3260    /// `defcaixa` carries by `#[serde(default)]` and every
3261    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
3262    /// [`Self::supervisor_view`] `restart_window: None` composition
3263    /// through the [`crate::supervisor::duration_codec::parse`] soft-
3264    /// swallow `.and_then(|s| … .ok())` fold).
3265    ///
3266    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
3267    /// shaped `Period` sliding-observation-interval duration string that
3268    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
3269    /// budget count to form the `MaxIntensity / Period` restart-intensity
3270    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
3271    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
3272    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
3273    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
3274    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
3275    /// holds an `Option<Duration>` routed through the shared
3276    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
3277    /// — so the outer altitude's accessor returns `Option<&str>` (raw
3278    /// authoring surface) while the inner altitude's
3279    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
3280    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
3281    /// is closed by the sibling [`Self::validate_restart_window`] gate
3282    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
3283    /// the offending value; the view-construction path
3284    /// [`Self::supervisor_view`] soft-swallows the same parse error to
3285    /// `None` to keep the view best-effort.
3286    ///
3287    /// Prior to this lift the `.restart_window` field was accessed inline
3288    /// at three production sites in `caixa-core/src/manifest.rs` — the
3289    /// [`Self::declared_supervisor_slots`]
3290    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
3291    /// `if self.restart_window.is_some()` (which drives the
3292    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3293    /// coherence gate's per-slot label push), the
3294    /// [`Self::validate_restart_window`] `let Some(s) =
3295    /// self.restart_window.as_deref()` empty-and-shape gate binding
3296    /// (which folds the raw string through the shared
3297    /// [`crate::supervisor::duration_codec::parse`] to surface
3298    /// [`ManifestError::RestartWindowMalformed`] naming the offending
3299    /// value), and the [`Self::supervisor_view`] `self.restart_window
3300    /// .as_deref().and_then(…)` view-construction fold (which composes
3301    /// the flat-spread outer author-surface `Option<String>` onto the
3302    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
3303    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
3304    /// three open-coded field-accesses that expressed no compile-time
3305    /// link back to the typed slot. A future extension of the outer
3306    /// `:restart-window` axis to a richer author surface (a per-cluster
3307    /// window override, a per-tenant window-alias table, a per-Supervisor
3308    /// dynamic window derivation the future adaptive-supervision engine
3309    /// computes from child-failure-history topology, a promotion of the
3310    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
3311    /// once the future author-surface parser lands at the [`Caixa`]
3312    /// altitude and the raw-string form is retired) would have had to be
3313    /// threaded through every open-coded copy in lockstep or the three
3314    /// consumers would silently disagree on which raw string a given
3315    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
3316    /// method on the substrate primitive means every downstream consumer
3317    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
3318    /// string surface reaches for exactly one typed dispatch — the
3319    /// resolver's accept-set migrates as a unit on any future axis
3320    /// addition.
3321    ///
3322    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
3323    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
3324    /// spread projection pattern the sibling per-`Caixa`
3325    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
3326    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
3327    /// the sub-family onto the sibling `Option<&str>` raw-duration-
3328    /// string arm (the outer altitude's raw-string form; the inner
3329    /// altitude's parsed [`Duration`] form is the peer
3330    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
3331    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
3332    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
3333    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
3334    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
3335    /// sub-family already carries — same "one typed dispatch on the
3336    /// substrate primitive, thin projections at each consumer"
3337    /// discipline extended onto the M2 supervisor-tree flat-spread
3338    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
3339    /// to match the storage field's name and the per-[`SupervisorSpec`]
3340    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
3341    /// method-name discipline verbatim; the accessor's identity maps
3342    /// onto the canonical OTP-shape supervision vocabulary the
3343    /// `:restart-window` field's docstring already carries.
3344    #[must_use]
3345    pub const fn restart_window(&self) -> Option<&str> {
3346        match &self.restart_window {
3347            Some(s) => Some(s.as_str()),
3348            None => None,
3349        }
3350    }
3351
3352    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
3353    /// outer-composite OTP-appup-shaped per-prior-version migration-
3354    /// entry-list slice accessor every consumer of the top-level
3355    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
3356    /// slice-view keys off — returns the author-declared `:upgrade-from`
3357    /// typed `Vec<UpgradeFromEntry>` verbatim as a
3358    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
3359    /// the raw `self.upgrade_from.as_slice()` field access borrows
3360    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
3361    /// arm every `defcaixa` without an `:upgrade-from` block carries;
3362    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
3363    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
3364    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
3365    /// possibly empty — and the returned `&[UpgradeFromEntry]`
3366    /// degenerates to an empty slice on that arm without any silent
3367    /// `None` collapse).
3368    ///
3369    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
3370    /// migration block — the load-bearing container of every per-
3371    /// prior-`:versao` migration-instruction list the wasm-operator
3372    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
3373    /// `.appup` per-prior-version `LoadModule | StateChange |
3374    /// SoftPurge | Purge | Restart` instruction algebra translated
3375    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
3376    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
3377    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
3378    /// threads through a lifted per-entry accessor on the
3379    /// [`UpgradeFromEntry`] type: the
3380    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
3381    /// version scalar accessor and the
3382    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
3383    /// return per-entry instruction-list accessor (0137e5a). Every
3384    /// downstream consumer of the hot-upgrade path first passes
3385    /// through this outer accessor onto the slice and then dispatches
3386    /// per-entry through the inner accessors — the two-level dispatch
3387    /// means every per-`:upgrade-from` reader now routes through a
3388    /// typed dispatch on the substrate primitive at both altitudes.
3389    ///
3390    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
3391    /// slot was accessed inline at production sites across three
3392    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
3393    /// enumerator's `self.upgrade_from.is_empty()` presence probe
3394    /// (caixa-core/src/manifest.rs, which drives the
3395    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
3396    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
3397    /// gate reads), the [`crate::StandardLayout::verify`] per-
3398    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
3399    /// layout.rs, which fans onto the
3400    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
3401    /// cross-entry duplicate gate, the
3402    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
3403    /// SemVer-precedence cross-slot gate, the
3404    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
3405    /// `:state-change` ↔ `:on-state-change` cross-slot composition
3406    /// gate, and the per-instruction script-path existence-probe walk
3407    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
3408    /// resolve every declared migration script against the layout
3409    /// root), and the [`crate::render::servico_m2_overlay`] per-
3410    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
3411    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
3412    /// projection (caixa-core/src/render.rs, which drives the
3413    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
3414    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
3415    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
3416    /// A future extension of the outer `:upgrade-from` axis (a per-
3417    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
3418    /// resolves at admission time so a cluster-specific migration
3419    /// policy can tighten a caixa-declared step without re-authoring
3420    /// the `caixa.lisp`, promotion of the plain
3421    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
3422    /// partition once runtime-resolved hot-upgrade instructions land,
3423    /// per-entry priority annotation once multi-strategy fan-out
3424    /// lands) would have had to be threaded through all six open-
3425    /// coded copies in lockstep or one consumer would silently
3426    /// disagree with the peers on which upgrade slice a given Caixa
3427    /// resolves to — a six-consumer split at the enumerator, the
3428    /// three-stage validate pass, the script-path probe walk, and the
3429    /// M2 overlay emitter, far from the source `caixa.lisp` with no
3430    /// field naming the upgrade-drift root cause. Lifting the
3431    /// resolution rule to a typed method on the substrate primitive
3432    /// means every downstream consumer of the caixa's per-`Caixa`
3433    /// OTP-appup outer-slice surface reaches for exactly one typed
3434    /// dispatch — the resolver's accept-set migrates as a unit on any
3435    /// future axis addition.
3436    ///
3437    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3438    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3439    /// outer-`Caixa` `&[Composite]` composite-slice projection
3440    /// pattern the sibling `:children`
3441    /// [`crate::supervisor::ChildSpec`] / `:membros`
3442    /// [`crate::aplicacao::Membro`] / `:contratos`
3443    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3444    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3445    /// `Option<&Composite>` composite-reference family the sibling
3446    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3447    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3448    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3449    /// `Option<&Composite>` altitude, extended here to the outer-
3450    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3451    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3452    /// (0137e5a) — same "one typed dispatch on the substrate
3453    /// primitive, thin projections at each consumer" discipline
3454    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3455    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3456    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3457    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3458    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3459    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3460    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3461    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3462    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3463    /// slice" projection pattern onto the sibling M2 typed-composite-
3464    /// element axis (`UpgradeFromEntry` composite, matching the
3465    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3466    /// different altitude).
3467    ///
3468    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3469    /// because every downstream consumer of the hot-upgrade list
3470    /// treats it as a read-only sequence — the slice-view is the
3471    /// narrowest borrow that supports every present + roadmapped
3472    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3473    /// serialization through
3474    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3475    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3476    /// the typed view reaches for (the storage-side `Vec` remains
3477    /// reachable through the `pub upgrade_from` field for the
3478    /// mutation-carrying serde round-trip and per-test fixture-
3479    /// mutation paths). Named `upgrade_from()` to match the storage
3480    /// field's `snake_case` name; the kebab-case author-surface tag
3481    /// `:upgrade-from` is the same axis after tatara-lisp's
3482    /// kebab↔snake fold and the accessor's identity maps onto the
3483    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3484    /// already carries.
3485    #[must_use]
3486    pub const fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3487        self.upgrade_from.as_slice()
3488    }
3489
3490    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3491    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3492    /// slice accessor every consumer of the top-level manifest's per-
3493    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3494    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3495    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3496    /// the same backing buffer the raw `self.children.as_slice()` field
3497    /// access borrows from. Empty-slice-carrying (the "no static children
3498    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3499    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3500    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3501    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3502    /// on those arms without any silent `None` collapse).
3503    ///
3504    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3505    /// static-child list — the load-bearing container of every per-
3506    /// child `{caixa, versao, restart}` triple the wasm-operator's
3507    /// hierarchical reconciler dispatches on at supervisor-tree
3508    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3509    /// static-child list translated onto pleme-io's typed
3510    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3511    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3512    /// dispatch fans on). Every per-child axis threads through a lifted
3513    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3514    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3515    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3516    /// version-requirement scalar accessor, and the
3517    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3518    /// per-child post-exit restart-decision-policy discriminant
3519    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3520    /// tree path first passes through this outer accessor onto the
3521    /// slice and then dispatches per-child through the inner accessors
3522    /// — the two-level dispatch means every per-`:children` reader now
3523    /// routes through a typed dispatch on the substrate primitive at
3524    /// both altitudes.
3525    ///
3526    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3527    /// accessed inline at three production sites across two files —
3528    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3529    /// declared-slot enumerator's `!self.children.is_empty()` presence
3530    /// probe (caixa-core/src/manifest.rs, which drives the
3531    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3532    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3533    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3534    /// per-supervisor typed-view composer's `self.children.clone()`
3535    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3536    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3537    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3538    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3539    /// `:children :caixa` self-parent refusal probe's
3540    /// `&caixa.children`-borrowed
3541    /// [`crate::supervisor::validate_no_self_supervision`] input
3542    /// (caixa-core/src/layout.rs, which pins the "no child names the
3543    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3544    /// extension of the outer `:children` axis (a per-cluster
3545    /// `:children-overrides` overlay the wasm-engine operator resolves
3546    /// at admission time so a cluster-specific child-set can tighten
3547    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3548    /// promotion of the plain `Vec<ChildSpec>` to a richer
3549    /// `{static, dynamic}` partition once Erlang/OTP's
3550    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3551    /// axis, per-child priority annotation once multi-strategy fan-out
3552    /// lands) would have had to be threaded through all three open-
3553    /// coded copies in lockstep or one consumer would silently
3554    /// disagree with the peers on which child slice a given Caixa
3555    /// resolves to — the enumerator's presence probe reading the raw
3556    /// slot while the peer view-composer's fold-in path read an
3557    /// operator-resolved slot would silently split the paired
3558    /// declared-slot enumerator and typed-view composition, and the
3559    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3560    /// refusal probe reading a third borrow would silently drift the
3561    /// cross-slot coherence gate's traversal input from the two peers,
3562    /// a three-consumer split at the enumerator, the view composer,
3563    /// and the self-parent gate far from the source `caixa.lisp` with
3564    /// no field naming the child-set-drift root cause. Lifting the
3565    /// resolution rule to a typed method on the substrate primitive
3566    /// means every downstream consumer of the caixa's per-`Caixa`
3567    /// OTP-supervisor outer-slice surface reaches for exactly one
3568    /// typed dispatch — the resolver's accept-set migrates as a unit
3569    /// on any future axis addition.
3570    ///
3571    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3572    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3573    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3574    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3575    /// at the outer altitude of the closed inner-`SupervisorSpec`
3576    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3577    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3578    /// borrow-shared" outer-accessor discipline extended onto the
3579    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3580    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3581    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3582    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3583    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3584    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3585    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3586    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3587    /// M2 typed-composite-element axis
3588    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3589    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3590    /// different altitude).
3591    ///
3592    /// Returns `&[crate::supervisor::ChildSpec]` (not
3593    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3594    /// child list treats it as a read-only sequence — the slice-view
3595    /// is the narrowest borrow that supports every present +
3596    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3597    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3598    /// input, `serde` slice-serialization) without leaking the backing
3599    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3600    /// reaches for (the storage-side `Vec` remains reachable through
3601    /// the `pub children` field for the mutation-carrying serde round-
3602    /// trip and per-test fixture-mutation paths, including the
3603    /// [`Self::supervisor_view`] fold-in path that clones the slot
3604    /// into the typed view). Named `children()` to match the storage
3605    /// field's name verbatim and the tatara-lisp author-surface term
3606    /// (`:children`) the field's own docstring already carries; the
3607    /// accessor's identity maps onto the canonical OTP supervision
3608    /// vocabulary the [`Caixa::children`] field's docstring already
3609    /// reaches for ("Static children of a supervisor").
3610    #[must_use]
3611    pub const fn children(&self) -> &[crate::supervisor::ChildSpec] {
3612        self.children.as_slice()
3613    }
3614
3615    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3616    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3617    /// accessor every consumer of the top-level manifest's per-Aplicacao
3618    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3619    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3620    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3621    /// same backing buffer the raw `self.membros.as_slice()` field access
3622    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3623    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3624    /// and every partially-authored Aplicacao carries before the
3625    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3626    /// `&[Membro]` degenerates to an empty slice on those arms without any
3627    /// silent `None` collapse).
3628    ///
3629    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3630    /// per-Aplicacao member list — the load-bearing container of every
3631    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3632    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3633    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3634    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3635    /// the `:entrada :para` external-gateway destination validates
3636    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3637    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3638    /// threads through a lifted per-entry accessor on the
3639    /// [`crate::aplicacao::Membro`] type: the
3640    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3641    /// identity scalar accessor (4a32abf) and the peer
3642    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3643    /// version-requirement scalar accessor (a40b0e3). Every downstream
3644    /// consumer of the mesh-graph path first passes through this outer
3645    /// accessor onto the slice and then dispatches per-member through
3646    /// the inner accessors — the two-level dispatch means every per-
3647    /// `:membros` reader now routes through a typed dispatch on the
3648    /// substrate primitive at both altitudes.
3649    ///
3650    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3651    /// inline at three production sites across two files — the
3652    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3653    /// enumerator's `!self.membros.is_empty()` presence probe
3654    /// (caixa-core/src/manifest.rs, which drives the
3655    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3656    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3657    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3658    /// composer's `self.membros.clone()` per-member fold-in path
3659    /// (caixa-core/src/manifest.rs, which materializes the typed
3660    /// [`crate::aplicacao::AplicacaoSpec`] view every
3661    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3662    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3663    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3664    /// [`crate::aplicacao::validate_no_self_membership`] input
3665    /// (caixa-core/src/layout.rs, which pins the "no member names the
3666    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3667    /// extension of the outer `:membros` axis (a per-cluster
3668    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3669    /// admission time so a cluster-specific member-set can tighten a
3670    /// caixa-declared list without re-authoring the `caixa.lisp`,
3671    /// promotion of the plain `Vec<Membro>` to a richer
3672    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3673    /// members land as a typed axis, per-member priority annotation once
3674    /// multi-strategy fan-out lands) would have had to be threaded
3675    /// through all three open-coded copies in lockstep or one consumer
3676    /// would silently disagree with the peers on which member slice a
3677    /// given Caixa resolves to — the enumerator's presence probe reading
3678    /// the raw slot while the peer view-composer's fold-in path read an
3679    /// operator-resolved slot would silently split the paired
3680    /// declared-slot enumerator and typed-view composition, and the
3681    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3682    /// refusal probe reading a third borrow would silently drift the
3683    /// cross-slot coherence gate's traversal input from the two peers, a
3684    /// three-consumer split at the enumerator, the view composer, and
3685    /// the self-membership gate far from the source `caixa.lisp` with no
3686    /// field naming the member-set-drift root cause. Lifting the
3687    /// resolution rule to a typed method on the substrate primitive
3688    /// means every downstream consumer of the caixa's per-`Caixa`
3689    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3690    /// typed dispatch — the resolver's accept-set migrates as a unit on
3691    /// any future axis addition.
3692    ///
3693    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3694    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3695    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3696    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3697    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3698    /// altitude. Peer at the outer altitude of the closed inner-
3699    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3700    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3701    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3702    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3703    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3704    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3705    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3706    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3707    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3708    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3709    /// pattern onto the sibling M3 typed-composite-element axis
3710    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3711    /// [`crate::AplicacaoSpec::membros`] element type at a different
3712    /// altitude).
3713    ///
3714    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3715    /// because every downstream consumer of the member list treats it
3716    /// as a read-only sequence — the slice-view is the narrowest borrow
3717    /// that supports every present + roadmapped consumer (`.iter()`,
3718    /// `.len()`, `.is_empty()`, the
3719    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3720    /// input, `serde` slice-serialization) without leaking the backing
3721    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3722    /// reaches for (the storage-side `Vec` remains reachable through the
3723    /// `pub membros` field for the mutation-carrying serde round-trip
3724    /// and per-test fixture-mutation paths, including the
3725    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3726    /// the typed view). Named `membros()` to match the storage field's
3727    /// name verbatim and the tatara-lisp author-surface term
3728    /// (`:membros`) the field's own docstring already carries; the
3729    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3730    /// vocabulary the [`Caixa::membros`] field's docstring already
3731    /// reaches for ("Member Servicos that make up this Aplicacao").
3732    #[must_use]
3733    pub const fn membros(&self) -> &[crate::aplicacao::Membro] {
3734        self.membros.as_slice()
3735    }
3736
3737    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3738    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3739    /// inter-Servico contract-list slice accessor every consumer of the
3740    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3741    /// slice-view keys off — returns the author-declared `:contratos`
3742    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3743    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3744    /// backing buffer the raw `self.contratos.as_slice()` field access
3745    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3746    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3747    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3748    /// single member with no inter-Servico edge carries; the returned
3749    /// `&[WitContract]` degenerates to an empty slice on those arms
3750    /// without any silent `None` collapse).
3751    ///
3752    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3753    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3754    /// container of every per-edge `{de, para, wit, endpoint | subject |
3755    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3756    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3757    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3758    /// adjacency-list seed dispatch on at mesh-artifact materialization
3759    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3760    /// `:membros` vertex set resolves against, closed by the
3761    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3762    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3763    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3764    /// per-edge axis threads through a lifted per-entry accessor on the
3765    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3766    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3767    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3768    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3769    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3770    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3771    /// and the WIT-world discriminant. Every downstream consumer of the
3772    /// mesh-graph edge path first passes through this outer accessor
3773    /// onto the slice and then dispatches per-contract through the
3774    /// inner accessors — the two-level dispatch means every
3775    /// per-`:contratos` reader now routes through a typed dispatch on
3776    /// the substrate primitive at both altitudes.
3777    ///
3778    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3779    /// accessed inline at two production sites in
3780    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3781    /// mesh-slot declared-slot enumerator's
3782    /// `!self.contratos.is_empty()` presence probe (which drives the
3783    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3784    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3785    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3786    /// typed-view composer's `self.contratos.clone()` per-contract
3787    /// fold-in path (which materializes the typed
3788    /// [`crate::aplicacao::AplicacaoSpec`] view every
3789    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3790    /// downstream `caixa-mesh` renderer dispatches on). A future
3791    /// extension of the outer `:contratos` axis (a per-cluster
3792    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3793    /// at admission time so a cluster-specific edge-set can tighten a
3794    /// caixa-declared list without re-authoring the `caixa.lisp`,
3795    /// promotion of the plain `Vec<WitContract>` to a richer
3796    /// `{static, dynamic}` partition once runtime-resolved contract
3797    /// edges land, per-edge policy annotation once the M4 per-edge
3798    /// policy overlay axis lands) would have had to be threaded through
3799    /// both open-coded copies in lockstep or one consumer would
3800    /// silently disagree with the peer on which edge slice a given
3801    /// Caixa resolves to — the enumerator's presence probe reading the
3802    /// raw slot while the peer view-composer's fold-in path read an
3803    /// operator-resolved slot would silently split the paired
3804    /// declared-slot enumerator and typed-view composition, a
3805    /// two-consumer split at the enumerator and the view composer far
3806    /// from the source `caixa.lisp` with no field naming the edge-set-
3807    /// drift root cause. Lifting the resolution rule to a typed method
3808    /// on the substrate primitive means every downstream consumer of
3809    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3810    /// reaches for exactly one typed dispatch — the resolver's
3811    /// accept-set migrates as a unit on any future axis addition.
3812    ///
3813    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3814    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3815    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3816    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3817    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3818    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3819    /// mesh-slot arm of the composite-slice sub-family the sibling
3820    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3821    /// Peer at the outer altitude of the closed inner-
3822    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3823    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3824    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3825    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3826    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3827    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3828    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3829    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3830    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3831    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3832    /// pattern onto the sibling M3 typed-composite-element axis
3833    /// ([`crate::aplicacao::WitContract`] composite, matching the
3834    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3835    /// different altitude).
3836    ///
3837    /// Returns `&[crate::aplicacao::WitContract]` (not
3838    /// `&Vec<WitContract>`) because every downstream consumer of the
3839    /// contract list treats it as a read-only sequence — the slice-view
3840    /// is the narrowest borrow that supports every present + roadmapped
3841    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3842    /// discriminant dispatch, `serde` slice-serialization) without
3843    /// leaking the backing `Vec`'s grow/push/reserve surface no
3844    /// consumer of the typed view reaches for (the storage-side `Vec`
3845    /// remains reachable through the `pub contratos` field for the
3846    /// mutation-carrying serde round-trip and per-test fixture-mutation
3847    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3848    /// clones the slot into the typed view). Named `contratos()` to
3849    /// match the storage field's name verbatim and the tatara-lisp
3850    /// author-surface term (`:contratos`) the field's own docstring
3851    /// already carries; the accessor's identity maps onto the canonical
3852    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3853    /// docstring already reaches for ("WIT-typed inter-Servico
3854    /// contracts").
3855    #[must_use]
3856    pub const fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3857        self.contratos.as_slice()
3858    }
3859
3860    /// Compose the Aplicacao-related flat slots into a single typed
3861    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3862    /// downstream renderer consumption. Returns `None` when the
3863    /// caixa isn't a `:kind Aplicacao`.
3864    #[must_use]
3865    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3866        if !self.kind().is_aplicacao() {
3867            return None;
3868        }
3869        Some(crate::aplicacao::AplicacaoSpec {
3870            membros: self.membros().to_vec(),
3871            contratos: self.contratos().to_vec(),
3872            politicas: self.politicas().cloned().unwrap_or_default(),
3873            placement: self.placement().cloned().unwrap_or_default(),
3874            entrada: self.entrada().cloned(),
3875        })
3876    }
3877
3878    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3879    /// *declares* a value on, in canonical declaration order
3880    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3881    /// `:entrada`). A slot counts as declared when its backing field
3882    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3883    ///
3884    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3885    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3886    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3887    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3888    /// caixa-flux / caixa-helm renderers only emit them for an
3889    /// Aplicacao. On any *other* kind a declared mesh slot is the
3890    /// manifest field's documented "ignored otherwise" (see the
3891    /// `:membros` … `:entrada` field docs): it silently passes
3892    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3893    /// rendered — far from the source caixa.lisp.
3894    /// [`crate::StandardLayout::verify`] consults this to reject that
3895    /// silent-drop at caixa-build time
3896    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3897    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3898    /// a slot foreign to the kind is a build error, not a silent drop.
3899    ///
3900    /// Lifted as a typed method (rather than an inline disjunction at
3901    /// the verify call site) so the mesh-slot set lives in one place —
3902    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3903    /// overlay, distributed-app takeover config) is one push here, and
3904    /// every consumer reaching for "which mesh slots are set" (the
3905    /// verify gate, a future `feira lint` kind-coherence advisory)
3906    /// inherits the canonical order without rolling its own.
3907    ///
3908    /// Each per-arm kebab-case label is routed through the peer
3909    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3910    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3911    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3912    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3913    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3914    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3915    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3916    /// kebab-case label + renderer-side artifact key) route through one
3917    /// canonical declaration per arm — same discipline the peer
3918    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3919    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3920    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3921    /// axis, extended here to close the M3 mesh-slot author-facing-label
3922    /// axis so both altitudes of the typed-slot algebra
3923    /// (per-Servico M2 + per-Aplicacao M3) share the same
3924    /// "one canonical byte-string per arm, next to the axis" discipline.
3925    #[must_use]
3926    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3927        let mut slots = Vec::new();
3928        if !self.membros().is_empty() {
3929            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3930        }
3931        if !self.contratos().is_empty() {
3932            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3933        }
3934        if self.politicas().is_some() {
3935            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3936        }
3937        if self.placement().is_some() {
3938            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3939        }
3940        if self.entrada().is_some() {
3941            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3942        }
3943        slots
3944    }
3945
3946    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3947    /// caixa *declares* a value on, in canonical declaration order
3948    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3949    /// `:children`). A slot counts as declared when its backing field
3950    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3951    ///
3952    /// The supervisor-tree slots compose the typed OTP supervisor of a
3953    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3954    /// `:children` field docs above). [`Self::supervisor_view`] only
3955    /// folds them into a validatable [`SupervisorSpec`] when the kind
3956    /// matches (returns `None` otherwise), and the wasm-operator's
3957    /// hierarchical reconciler only consumes them for a Supervisor. On
3958    /// any *other* kind a declared supervisor slot is the manifest
3959    /// field's documented "ignored otherwise" (see the `:estrategia` …
3960    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3961    /// and then vanishes — never validated, never reconciled — far from
3962    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3963    /// this to reject that silent-drop at caixa-build time
3964    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3965    /// exact mirror of the [`Self::declared_mesh_slots`] /
3966    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3967    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3968    /// error, not a silent drop.
3969    #[must_use]
3970    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3971        let mut slots = Vec::new();
3972        if self.estrategia().is_some() {
3973            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3974        }
3975        if self.max_restarts().is_some() {
3976            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3977        }
3978        if self.restart_window().is_some() {
3979            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3980        }
3981        if !self.children().is_empty() {
3982            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3983        }
3984        slots
3985    }
3986
3987    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3988    /// caixa *declares* a value on, in canonical declaration order
3989    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3990    /// declared when its backing field carries a value — a `Some(...)`,
3991    /// or a non-empty `Vec`.
3992    ///
3993    /// The M2 slots configure the runtime of a long-running wasm
3994    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3995    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3996    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3997    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3998    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3999    /// emit these slots for a Servico; on any *other* kind a declared M2
4000    /// slot is the manifest field's documented "ignored otherwise": its
4001    /// well-formedness is checked by [`crate::StandardLayout::verify`]
4002    /// but the value is never rendered into a chart / programs.yaml entry
4003    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
4004    /// vanishes, far from the source caixa.lisp.
4005    /// [`crate::StandardLayout::verify`] consults this to reject that
4006    /// silent-drop at caixa-build time
4007    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
4008    /// mirror of the [`Self::declared_mesh_slots`] /
4009    /// [`Self::declared_supervisor_slots`] gates on the peer
4010    /// kind-exclusive slot sets: a slot foreign to the kind is a build
4011    /// error, not a silent drop.
4012    ///
4013    /// Each per-arm kebab-case label is routed through the peer
4014    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
4015    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
4016    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
4017    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
4018    /// both halves of the M2 top-level slot's dual axis (author-facing
4019    /// kebab-case label + renderer-side camelCase overlay-container wire
4020    /// key) route through one canonical declaration per arm — same
4021    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
4022    /// author-label consts (889dc18) establish on the sibling
4023    /// per-callback axis inside the `:behavior` overlay block.
4024    #[must_use]
4025    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
4026        let mut slots = Vec::new();
4027        if self.limits().is_some() {
4028            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
4029        }
4030        if self.behavior().is_some() {
4031            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
4032        }
4033        if !self.upgrade_from().is_empty() {
4034            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
4035        }
4036        slots
4037    }
4038
4039    /// The kebab-case `:slot` tags of every code-surface slot this caixa
4040    /// declares a value on that its [`CaixaKind`] doesn't natively own,
4041    /// in canonical declaration order (`:exe` → `:servicos`). A
4042    /// code-surface slot is owned by exactly one kind: `:exe` by
4043    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
4044    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
4045    /// `ComputeUnit` daemon surface).
4046    ///
4047    /// Each is silently ignored when declared on the wrong kind: the
4048    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
4049    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
4050    /// code-running kind a declared `:exe` / `:servicos` is the manifest
4051    /// field's documented "ignored otherwise" — its path is checked for
4052    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
4053    /// (which run after [`Caixa::from_lisp`]), but the value is never
4054    /// rendered into a build target or programs.yaml entry. It silently
4055    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
4056    /// caixa.lisp, with no field naming which slot is foreign.
4057    ///
4058    /// [`crate::StandardLayout::verify`] consults this to reject that
4059    /// silent-drop at caixa-build time
4060    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
4061    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
4062    /// gates ([`Self::declared_servico_slots`] /
4063    /// [`Self::declared_supervisor_slots`] /
4064    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
4065    /// axis to be closed on the typed surface. The Supervisor /
4066    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
4067    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
4068    /// diagnostics — they fire ahead of this gate on the same `verify`
4069    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
4070    /// and this method is moot. For Biblioteca / Binario / Servico, this
4071    /// gate fires when a code-running kind declares another code-running
4072    /// kind's exclusive code surface.
4073    ///
4074    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
4075    /// may legitimately ship a `lib/` helper that the underlying
4076    /// substrate (the nix flake for Binario, the wasm component build
4077    /// for Servico) bundles into its build, so the slot's
4078    /// declared-on-wrong-kind cardinality isn't a structural error on
4079    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
4080    /// is the native case (the slot's owning kind). Supervisor /
4081    /// Aplicacao declaring `:bibliotecas` is gated upstream by
4082    /// [`crate::LayoutError::SupervisorOwnsCode`] /
4083    /// [`crate::LayoutError::AplicacaoOwnsCode`].
4084    ///
4085    /// Lifted as a typed method (rather than an inline disjunction at
4086    /// the verify call site) so the foreign-code-slot set lives in one
4087    /// place — a future kind that gains its own code-surface slot is
4088    /// one push here, and every consumer reaching for "which code
4089    /// surfaces are foreign to this kind" (the verify gate, a future
4090    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
4091    /// per-caixa build-target classifier) inherits the canonical order
4092    /// without rolling its own.
4093    #[must_use]
4094    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
4095        let mut slots = Vec::new();
4096        if !self.exe().is_empty() && !self.kind().requires_exe() {
4097            slots.push(":exe");
4098        }
4099        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
4100            slots.push(":servicos");
4101        }
4102        slots
4103    }
4104
4105    /// Validate every entry of `:deps` and `:deps-dev` through
4106    /// [`Dep::validate`] — closing the parity loop with the per-axis
4107    /// `:versao` gates already wired into the typed-graph
4108    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
4109    /// 9888b13) and typed supervisor tree
4110    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
4111    ///
4112    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
4113    /// were the only `:versao` axes still untyped past
4114    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
4115    /// as a String without parsing it, so a malformed-but-non-empty
4116    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
4117    /// silently passed parse and the `semver::Error` surfaced at
4118    /// lacre-resolve time, far from the source caixa.lisp, with no
4119    /// field naming which `:deps` entry carried the typo. Lifting the
4120    /// gate here makes the four `:versao` typed surfaces (`:deps`,
4121    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
4122    /// every requirement string past `validate_deps` is round-trippable
4123    /// through [`crate::parse_requirement`] without re-checking at the
4124    /// resolver layer.
4125    ///
4126    /// Both lists run through the same per-entry validator so a typo
4127    /// in `:deps-dev` surfaces with the same diagnostic as one in
4128    /// `:deps` — neither axis is a second-class citizen of the typed
4129    /// surface.
4130    ///
4131    /// Within each list, [`DepError::DuplicateNome`] closes the
4132    /// set-not-multiset discipline on the `:nome` axis: two entries
4133    /// naming the same caixa carry two `:versao` / `:fonte` / feature
4134    /// triples that the caixa-resolver's lacre pipeline collapses to one
4135    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
4136    /// silently overwrites the first at `concrete_versao`-resolve time
4137    /// (the same "second wins / one silently overwrites the other"
4138    /// shape the peer typed-graph duplicate gates already close on every
4139    /// other Vec-shaped authoring surface that keys by name). The
4140    /// duplicate check fires per-list and runs *after* each per-entry
4141    /// [`Dep::validate`] call so a malformed-and-duplicated entry
4142    /// surfaces its narrower per-entry diagnostic
4143    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
4144    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
4145    /// diagnostic — the canonical "per-entry shape before cross-entry
4146    /// uniqueness" precedence the peer `:children :caixa`
4147    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
4148    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
4149    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
4150    /// ([`crate::AplicacaoSpec::validate_placement`]),
4151    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
4152    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
4153    /// and the within-`:upgrade-from`-entry per-instruction-class
4154    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
4155    /// [`crate::UpgradeError::DuplicateStateChange`],
4156    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
4157    ///
4158    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
4159    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
4160    /// same name in both tables (the dev table's pin overrides the
4161    /// runtime table's pin in test/dev contexts), and caixa's surface
4162    /// mirrors that convention until a deliberate choice retires the
4163    /// override pattern. Only within-list duplicates are structurally
4164    /// incoherent — those are what this gate closes.
4165    ///
4166    /// Compound per-`Caixa` entry gate on the dep-graph axis: folds the
4167    /// two standalone dep-list validators — the per-entry + within-list
4168    /// duplicate-`:nome` walk (the [`Dep::validate`] +
4169    /// [`crate::render::insert_first_seen`] cascade this method opened
4170    /// on) and the cross-slot self-edge gate
4171    /// ([`crate::dep::validate_no_self_dep`]) — onto one substrate
4172    /// primitive on [`Caixa`]. The two arms run in the same canonical
4173    /// order the layout pipeline
4174    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4175    /// author-time gate) has always sequenced them (per-entry +
4176    /// cross-entry duplicate → cross-slot self-edge), so the fold is
4177    /// byte-for-byte equivalent to the pre-fold two-block cascade at
4178    /// that call site (pinned by the paired
4179    /// `validate_deps_folds_per_entry_arm_matches_gate` /
4180    /// `validate_deps_folds_self_edge_arm_matches_gate` equivalence
4181    /// pins and the `validate_deps_per_entry_arm_fires_before_self_edge_arm`
4182    /// ordering pin). Self-contained on `&self` — resolves its three
4183    /// inputs ([`Self::deps`], [`Self::deps_dev`], [`Self::nome`])
4184    /// through the substrate primitives' own accessor family, the same
4185    /// posture every peer per-slot compound gate
4186    /// ([`crate::AplicacaoSpec::validate_contratos`],
4187    /// [`crate::MeshPolicy::validate`],
4188    /// [`crate::SupervisorSpec::validate_children`],
4189    /// [`Self::validate_upgrade_from`]) already carries.
4190    ///
4191    /// Prior to this lift [`crate::dep::validate_no_self_dep`] lived
4192    /// only open-coded at the layout wire-up site
4193    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs)
4194    /// as a standalone two-arg dispatch immediately after this method's
4195    /// per-entry + cross-entry walk, both wrapped through the same
4196    /// [`crate::LayoutError::DepsViolation`] envelope: every future
4197    /// consumer that wanted to gate the dep-graph as a whole — the
4198    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4199    /// per-CR admission webhook re-checking `:deps` / `:deps-dev` after
4200    /// a per-entry patch, a future `feira validate --deps` per-caixa
4201    /// admission verb, a per-`:deps` overlay resolver a per-cluster
4202    /// overlay lift would materialize (each the deferred consumer this
4203    /// method's peer [`Self::deps`] / [`Self::deps_dev`] accessors'
4204    /// docstrings already name) — was structurally forced to either
4205    /// re-inline the two-dispatch cascade in lockstep with the layout
4206    /// wire-up (the duplication the PRIME DIRECTIVE names as a bug) or
4207    /// call the whole [`crate::layout::StandardLayout::verify`] pipeline
4208    /// and pay every peer per-Caixa gate to re-check one slot. Post-fold
4209    /// each such consumer reaches the two-arm compound gate through one
4210    /// call on the substrate primitive.
4211    pub fn validate_deps(&self) -> Result<(), DepError> {
4212        for &list in crate::dep::DepList::ALL {
4213            let mut seen = std::collections::HashSet::new();
4214            for dep in self.deps_of(list) {
4215                dep.validate()?;
4216                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
4217                    DepError::DuplicateNome {
4218                        nome: dep.nome().to_string(),
4219                        list: list.as_str(),
4220                    }
4221                })?;
4222            }
4223        }
4224        crate::dep::validate_no_self_dep(self.deps(), self.deps_dev(), self.nome())?;
4225        Ok(())
4226    }
4227
4228    /// Reject `:nome` values the K8s apiserver would refuse at admission
4229    /// time. The top-level Caixa identity flows directly into every
4230    /// substrate-side artifact's `metadata.name` axis: the
4231    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
4232    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
4233    /// aggregator keys ComputeUnit derivation off
4234    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
4235    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
4236    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
4237    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
4238    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
4239    /// ([`caixa-mesh::lib::cilium_network_policies`],
4240    /// [`caixa-mesh::lib::gateway_routes`]), and the default
4241    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
4242    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
4243    /// schema enforces the DNS-1123 label rule on admission; a
4244    /// structurally invalid `:nome` (`"MyApp"` — the canonical
4245    /// "I copied the display name verbatim" footgun, `"my_app"` — the
4246    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
4247    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
4248    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
4249    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
4250    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
4251    /// failure surfaced at `kubectl apply` time as a `metadata.name:
4252    /// Invalid value` rejection on whichever derived artifact admitted
4253    /// first, far from the source `caixa.lisp` and without any field
4254    /// naming the offending `:nome`.
4255    ///
4256    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
4257    /// substrate-side predicate the per-axis name gates already share:
4258    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
4259    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
4260    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
4261    /// diagnostic is self-locating (the offending `:nome` is named
4262    /// verbatim) and the author can grep their `caixa.lisp` for
4263    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
4264    /// every per-axis sibling gate already exposes
4265    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
4266    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
4267    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
4268    ///
4269    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
4270    /// derive macro stores the raw String) is gated by the narrower
4271    /// [`ManifestError::NomeEmpty`] arm before the predicate is
4272    /// consulted, mirroring the empty-first cascade every per-axis
4273    /// name gate already uses (e.g. `MembroCaixaEmpty` before
4274    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
4275    pub fn validate_nome(&self) -> Result<(), ManifestError> {
4276        // Routes through the shared
4277        // [`crate::render::require_valid_dns_1123_label`] gate the peer
4278        // name axes each land on so drift between the eight axes'
4279        // accepted DNS-1123-label sets is structurally impossible.
4280        let nome = self.nome();
4281        crate::render::require_valid_dns_1123_label(
4282            nome,
4283            || ManifestError::NomeEmpty,
4284            |reason| ManifestError::NomeInvalid {
4285                nome: nome.to_string(),
4286                reason,
4287            },
4288        )
4289    }
4290
4291    /// Reject `:nome` values whose joint length with the canonical
4292    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4293    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4294    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4295    /// substrate carries materializes the caixa's `:nome` through the
4296    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4297    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4298    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4299    /// `ChartDir.name` + `Chart.yaml::name`
4300    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4301    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4302    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4303    /// `oci://<registry>/lareira-<nome>` chart ref
4304    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4305    /// admission rule strict-parses against DNS-1123-label, the Helm
4306    /// operator's tracking-secret name is derived from `release_name`
4307    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4308    /// K8s object `metadata.name` axes embed the chart name as a
4309    /// prefix — every one fails admission on a > 63-byte chart name.
4310    ///
4311    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4312    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4313    /// `:nome` of 56–63 bytes silently passed validate (the inner
4314    /// DNS-1123 check accepts the bare `:nome`) but produced a
4315    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4316    /// rejected at admission — far from the source `caixa.lisp`, with
4317    /// no field naming the overflow root cause. The
4318    /// [`lareira_chart_name`] helper's own doc comment
4319    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4320    /// "the M4 admission webhook will pin the joint-length invariant
4321    /// when it lands". This gate lands the invariant at the
4322    /// manifest-validate layer rather than waiting for the apiserver
4323    /// — the same fail-at-the-source posture every peer per-axis
4324    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4325    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4326    /// `:edicao`, etc.) takes.
4327    ///
4328    /// Thin wrapper around
4329    /// [`crate::render::is_lareira_chart_name_shape`] (the
4330    /// substrate-side predicate that composes [`lareira_chart_name`] +
4331    /// [`is_dns_1123_label`] via the lifted
4332    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4333    /// shared parser-shaped reason into the
4334    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4335    /// diagnostic is self-locating (the offending `:nome` is named
4336    /// verbatim alongside the rendered chart name and the budget) and
4337    /// the author can shorten in one edit. The gate runs across every
4338    /// `:kind` — `:nome` is the substrate-wide identity axis any
4339    /// future renderer the substrate adds can derive a
4340    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4341    /// the drift footgun where a future kind grows a chart-emitting
4342    /// render path while the validate cascade doesn't catch it.
4343    ///
4344    /// Runs *after* [`Self::validate_nome`] so the narrower
4345    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4346    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4347    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4348    /// specific shape error rather than the chart-name-budget error,
4349    /// preserving the legitimate "well-shaped `:nome` that happens to
4350    /// overflow the joint cap" arm for this gate.
4351    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4352        let nome = self.nome();
4353        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
4354            ManifestError::NomeChartNameBudgetExceeded {
4355                nome: nome.to_string(),
4356                reason,
4357            }
4358        })
4359    }
4360
4361    /// Reject `:versao` values that don't parse as [`semver::Version`].
4362    /// The top-level Caixa version flows directly into every
4363    /// substrate-side artifact that carries a "this is which version of
4364    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4365    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4366    /// SemVer-2-strict at `helm template` / `helm install` time per
4367    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4368    /// `feira publish` Zig-style `v<versao>` git tag
4369    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4370    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4371    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4372    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4373    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4374    /// `skopeo push`, the lacre closure's pinned versions
4375    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4376    /// `:upgrade-from :from` references peers in this exact `versao`
4377    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4378    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4379    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4380    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4381    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4382    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4383    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4384    /// into the version field a peer `:deps :versao` accepts;
4385    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4386    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4387    /// derive macro stores the raw String) and the failure surfaced at
4388    /// the *first* downstream consumer that strict-parses it: at
4389    /// `helm install` time as a chart-version rejection, at
4390    /// `feira publish` time as a malformed git tag, at lacre-resolve
4391    /// time as a `semver::Error` not naming the offending caixa, at
4392    /// `feira upgrade --to <versao>` time as an unresolvable
4393    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4394    /// and without any field naming the offending `:versao`.
4395    ///
4396    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4397    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4398    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4399    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4400    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4401    /// variant, carrying the offending `:versao` verbatim + a
4402    /// parser-shaped reason naming the specific violation, so the
4403    /// diagnostic is self-locating (the author can grep their
4404    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4405    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4406    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4407    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4408    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4409    /// now structurally equivalent (every value past validate is
4410    /// round-trippable through [`semver::Version::parse`] without
4411    /// re-checking at the renderer, resolver, or operator hot-upgrade
4412    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4413    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4414    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4415    ///
4416    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4417    /// the derive macro stores the raw String) is gated by the
4418    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4419    /// consulted, mirroring the empty-first cascade every per-axis
4420    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4421    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4422    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4423    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4424        let versao = self.versao();
4425        if versao.is_empty() {
4426            return Err(ManifestError::VersaoEmpty);
4427        }
4428        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
4429            versao: versao.to_string(),
4430            reason: e.to_string(),
4431        })?;
4432        Ok(())
4433    }
4434
4435    /// Compound per-`Caixa` entry gate on the M2 `:upgrade-from` slot:
4436    /// folds the three [`crate::upgrade`] top-level validators — the
4437    /// per-entry shape + cross-entry duplicate-`:from` gate
4438    /// ([`crate::upgrade::validate_upgrade_from`]), the cross-slot
4439    /// `:from < :versao` SemVer-2 precedence gate
4440    /// ([`crate::upgrade::validate_upgrade_from_against_versao`]), and the
4441    /// cross-slot `:state-change` ↔ `:on-state-change` composition gate
4442    /// ([`crate::upgrade::validate_upgrade_from_against_behavior`]) — onto
4443    /// one substrate primitive on [`Caixa`]. The three dispatches run in
4444    /// the same order the layout pipeline
4445    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4446    /// author-time gate) has always sequenced them, so the fold is
4447    /// byte-for-byte equivalent to the pre-fold three-block cascade at
4448    /// that call site (pinned by the per-arm
4449    /// `validate_upgrade_from_folds_per_entry_arm_matches_gate` /
4450    /// `_folds_versao_arm_matches_gate` / `_folds_behavior_arm_matches_gate`
4451    /// equivalence pins and by the cross-arm
4452    /// `validate_upgrade_from_per_entry_arm_fires_before_versao_arm` /
4453    /// `_versao_arm_fires_before_behavior_arm` ordering pins).
4454    ///
4455    /// Prior to this lift the three [`crate::upgrade`] top-level validators
4456    /// lived only open-coded at the layout wire-up site
4457    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4458    /// each threaded through the same `self.upgrade_from()` slice and each
4459    /// paired with the same [`crate::LayoutError::UpgradeViolation`]-wrap
4460    /// envelope: every future consumer that wanted to gate `:upgrade-from`
4461    /// as a whole — the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
4462    /// materializer's per-CR admission webhook re-checking `:upgrade-from`
4463    /// after a per-`(:from … :instructions …)` patch, a future `feira
4464    /// validate --upgrade` per-caixa admission verb, a per-`:upgrade-from`
4465    /// overlay resolver a per-cluster overlay lift would materialize —
4466    /// was structurally forced to either re-inline the three-dispatch
4467    /// cascade in lockstep with the layout wire-up (the duplication the
4468    /// PRIME DIRECTIVE names as a bug) or call the whole
4469    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4470    /// peer per-Caixa gate to re-check one slot. Post-fold each such
4471    /// consumer reaches the three-arm compound gate through one call on
4472    /// the substrate primitive.
4473    ///
4474    /// The three arms together name one contract with three axes:
4475    ///
4476    ///   - **per-entry + cross-entry graph-edge invariant** — every entry's
4477    ///     `:from` parses as SemVer-2 and every per-instruction / within-
4478    ///     entry ordering / singularity gate on each entry's
4479    ///     `:instructions` list passes, and no two entries share the same
4480    ///     parsed `:from` (the wasm-operator's OTP appup
4481    ///     `release_handler:install_release/1` analog picks at most one
4482    ///     matching block per running version — two entries with the same
4483    ///     parsed semver are an ambiguous edge in the typed upgrade graph).
4484    ///   - **cross-slot reachability invariant** — every entry's `:from`
4485    ///     is strictly less than the caixa's own `:versao` under SemVer-2
4486    ///     precedence. An entry whose `:from >= :versao` is structurally
4487    ///     unreachable by the operator's `:from`-match dispatch (the
4488    ///     operator loads the current `:versao` and matches the *running*
4489    ///     version against each entry's `:from`; an entry whose `:from >=
4490    ///     :versao` is never reached because the operator never runs a
4491    ///     version >= the current one that it could then upgrade *to* the
4492    ///     current one).
4493    ///   - **cross-slot composition invariant** — every entry carrying a
4494    ///     `(:state-change …)` instruction has a `:behavior
4495    ///     :on-state-change` callback declared on the same caixa. The
4496    ///     per-version migration script is the `gen_server:code_change/3`
4497    ///     analog and the runtime hook it is delivered through during hot
4498    ///     upgrade is the `:on-state-change` callback (the upgrade.rs
4499    ///     module doc pins the composition verbatim: "Composes with the
4500    ///     `:behavior :on-state-change` callback to deliver state migration
4501    ///     during hot upgrades").
4502    ///
4503    /// All three axes must hold together — every consumer's
4504    /// `:upgrade-from` accept-set past this compound gate is the same
4505    /// set the `feira build` author-time gate admits.
4506    ///
4507    /// The per-slot compound entry gate discipline lifted here onto the
4508    /// M2 `:upgrade-from` axis is the sibling of the peer per-kind
4509    /// compound entry gates ([`crate::render::require_supervisor_view`]
4510    /// / [`crate::render::require_aplicacao_view`] /
4511    /// [`crate::render::require_v0_servico_shape`]) that fold every
4512    /// per-kind cascade at the per-kind altitude, and of the peer
4513    /// per-slot compound gates ([`crate::AplicacaoSpec::validate_contratos`],
4514    /// [`crate::MeshPolicy::validate`],
4515    /// [`crate::SupervisorSpec::validate_children`]) that fold every
4516    /// structural axis on their slot onto one substrate primitive.
4517    /// Extended here to the last unlifted compound-cascade wire-up at
4518    /// the layout-pipeline altitude — the three-dispatch M2
4519    /// `:upgrade-from` cascade that lived only open-coded at the layout
4520    /// wire-up site.
4521    ///
4522    /// The per-instruction script-path on-disk existence-probe walk that
4523    /// [`crate::layout::StandardLayout::verify`] runs immediately after
4524    /// this gate (which resolves each entry's `:instructions
4525    /// (:state-change :script)` against the layout root) stays open-coded
4526    /// at the layout wire-up site — that arm needs the filesystem oracle
4527    /// on the [`crate::LayoutInvariants`] trait, not the pure per-Caixa
4528    /// typed-shape surface this compound gate folds. Same posture the
4529    /// peer [`Self::validate_code_paths`] takes on the sibling code-path
4530    /// axes: the typed-shape gate fires on the per-Caixa surface, the
4531    /// on-disk existence check fires on the [`crate::StandardLayout`]
4532    /// surface.
4533    ///
4534    /// # Errors
4535    ///
4536    /// Returns [`crate::UpgradeError::FromInvalid`] /
4537    /// [`crate::UpgradeError::ModuleEmpty`] /
4538    /// [`crate::UpgradeError::ModuleInvalid`] /
4539    /// [`crate::UpgradeError::EmptyScript`] /
4540    /// [`crate::UpgradeError::AbsoluteScript`] /
4541    /// [`crate::UpgradeError::ParentEscapeScript`] /
4542    /// [`crate::UpgradeError::NonLispExtensionScript`] /
4543    /// [`crate::UpgradeError::RestartNotExclusive`] /
4544    /// [`crate::UpgradeError::StateChangeWithoutPriorLoad`] /
4545    /// [`crate::UpgradeError::PurgeWithoutPriorLoad`] /
4546    /// [`crate::UpgradeError::StateChangeAfterCleanup`] /
4547    /// [`crate::UpgradeError::DuplicateLoadModule`] /
4548    /// [`crate::UpgradeError::DuplicateStateChange`] /
4549    /// [`crate::UpgradeError::DuplicateCleanup`] /
4550    /// [`crate::UpgradeError::DuplicateFrom`] on the per-entry +
4551    /// cross-entry axis; [`crate::UpgradeError::FromNotBeforeVersao`] on
4552    /// the cross-slot `:from ↔ :versao` axis;
4553    /// [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
4554    /// on the cross-slot `:state-change ↔ :on-state-change` axis.
4555    pub fn validate_upgrade_from(&self) -> Result<(), crate::UpgradeError> {
4556        crate::upgrade::validate_upgrade_from(self.upgrade_from())?;
4557        crate::upgrade::validate_upgrade_from_against_versao(self.upgrade_from(), self.versao())?;
4558        crate::upgrade::validate_upgrade_from_against_behavior(
4559            self.upgrade_from(),
4560            self.behavior(),
4561        )?;
4562        Ok(())
4563    }
4564
4565    /// Compound per-`Caixa` entry gate on the M2 `:limits` slot — folds
4566    /// the [`crate::LimitsSpec::validate`] four-axis cascade (`:memory`
4567    /// wasm32 zero-floor / below-page / above-cap / non-page-multiple;
4568    /// `:fuel` zero-floor / cap; `:wall-clock` zero-floor / cap; `:cpu`
4569    /// zero-floor / cap) onto one substrate primitive on [`Caixa`]. The
4570    /// `#[serde(default)]` absent-slot arm (`limits: None`, the
4571    /// canonical "no bound declared — engine-default applies" author
4572    /// shape [`crate::LimitsSpec::is_empty`]'s per-axis `None` cascade
4573    /// reads) is the fold's identity element and passes trivially; the
4574    /// present-slot arm (`limits: Some(l)`) dispatches to
4575    /// [`crate::LimitsSpec::validate`] verbatim, threading its per-axis
4576    /// [`crate::LimitsError`] Display through untouched.
4577    ///
4578    /// Prior to this lift the M2 `:limits` slot lived only wired
4579    /// open-coded at the layout wire-up site
4580    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4581    /// through the `if let Some(l) = caixa.limits() { l.validate() … }`
4582    /// three-line `Option::None → Ok(()) | Some(_) → …` unwrap-and-
4583    /// dispatch pattern paired with the same
4584    /// [`crate::LayoutError::LimitsViolation`]-wrap envelope: every
4585    /// future consumer that wanted to gate `:limits` as a whole — the
4586    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4587    /// per-CR admission webhook re-checking `:limits` after a per-
4588    /// `{:memory, :fuel, :wall-clock, :cpu}` patch (the exact case the
4589    /// [`Self::limits`] accessor docstring names as the second
4590    /// consumer of the slot), a future `feira validate --limits` per-
4591    /// caixa admission verb, a per-`:limits` overlay resolver a per-
4592    /// cluster `:limits-overrides` overlay lift would materialize — was
4593    /// structurally forced to either re-inline the two-line
4594    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4595    /// pattern in lockstep with the layout wire-up (the duplication the
4596    /// PRIME DIRECTIVE names as a bug) or call the whole
4597    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4598    /// peer per-Caixa gate ([`Self::validate_nome`],
4599    /// [`Self::validate_versao`], [`Self::validate_deps`],
4600    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4601    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4602    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4603    /// [`Self::validate_upgrade_from`], [`Self::validate_code_paths`],
4604    /// plus the per-kind `require_supervisor_view` /
4605    /// `require_aplicacao_view` gates, plus the on-disk existence
4606    /// walks) to re-check one slot. Post-lift each such consumer
4607    /// reaches the [`crate::LimitsSpec::validate`] four-axis cascade
4608    /// (and its identity-element on the absent slot) through one call
4609    /// on the substrate primitive.
4610    ///
4611    /// The per-slot compound entry-gate discipline lifted here onto the
4612    /// M2 `:limits` axis is the sibling of the peer per-slot compound
4613    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4614    /// [`crate::MeshPolicy::validate`],
4615    /// [`crate::SupervisorSpec::validate_children`],
4616    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`]) that
4617    /// fold every structural + cross-slot axis on their slot onto one
4618    /// substrate primitive. Extended here to the M2 `:limits` slot, the
4619    /// first of the two M2 typed slots (`:limits`, `:behavior`) whose
4620    /// per-Caixa compound-gate wire-up still lived open-coded at the
4621    /// layout altitude after the [`Self::validate_upgrade_from`] lift
4622    /// (d6801df) closed the sibling M2 slot's cascade.
4623    ///
4624    /// # Errors
4625    ///
4626    /// Returns every [`crate::LimitsError`] variant on the present-slot
4627    /// arm — verbatim from [`crate::LimitsSpec::validate`]. Passes
4628    /// trivially on the absent-slot arm (`limits: None`, the fold's
4629    /// identity element).
4630    pub fn validate_limits(&self) -> Result<(), crate::LimitsError> {
4631        match self.limits() {
4632            Some(l) => l.validate(),
4633            None => Ok(()),
4634        }
4635    }
4636
4637    /// Compound per-`Caixa` entry gate on the M2 `:behavior` slot's
4638    /// pure typed-shape surface — folds the
4639    /// [`crate::BehaviorSpec::validate`] six-slot value-shape cascade
4640    /// (each declared `:on-init` / `:on-call` / `:on-cast` / `:on-info`
4641    /// / `:on-state-change` / `:on-terminate` callback-path is
4642    /// non-empty / relative / no-`..`-parent-escape / terminating-
4643    /// `.lisp`-extension, routed through the shared
4644    /// [`crate::render::require_sandboxed_lisp_path`] arm-set) onto one
4645    /// substrate primitive on [`Caixa`]. The `#[serde(default)]`
4646    /// absent-slot arm (`behavior: None`, the canonical "no callback
4647    /// declared — the runtime falls back to the wasm-engine's default
4648    /// callback per arm" author shape [`crate::BehaviorSpec::is_empty`]'s
4649    /// per-slot `None` cascade reads) is the fold's identity element
4650    /// and passes trivially; the present-slot arm (`behavior: Some(b)`)
4651    /// dispatches to [`crate::BehaviorSpec::validate`] verbatim,
4652    /// threading its per-slot [`crate::BehaviorError`] Display through
4653    /// untouched.
4654    ///
4655    /// Scope note — the on-disk callback-path existence walk paired
4656    /// with the value-shape gate at
4657    /// [`crate::layout::StandardLayout::verify`] stays open-coded at
4658    /// the layout altitude, because it needs the
4659    /// [`crate::layout::LayoutInvariants`] filesystem oracle
4660    /// ([`crate::layout::LayoutInvariants::exists`]) that the pure
4661    /// per-Caixa typed-shape surface this compound gate folds onto has
4662    /// no reference to. Same posture the peer M2 `:upgrade-from`
4663    /// per-Caixa compound gate ([`Self::validate_upgrade_from`]
4664    /// d6801df) already carries: the pure typed-shape surface folds
4665    /// onto the substrate primitive; the per-instruction script-path
4666    /// existence probe on the paired axis (there `:state-change
4667    /// :script`; here `:on-*`) stays at the layout altitude.
4668    ///
4669    /// Prior to this lift the pure value-shape surface of the M2
4670    /// `:behavior` slot lived only wired open-coded at the layout
4671    /// wire-up site ([`crate::layout::StandardLayout::verify`],
4672    /// caixa-core/src/layout.rs), through the
4673    /// `if let Some(b) = caixa.behavior() { b.validate() … }`
4674    /// unwrap-and-dispatch pattern paired with the same
4675    /// [`crate::LayoutError::BehaviorViolation`]-wrap envelope: every
4676    /// future consumer that wanted to gate the `:behavior` slot's
4677    /// value-shape as a whole — the deferred
4678    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4679    /// admission webhook re-checking `:behavior` after a per-`{:on-init,
4680    /// :on-call, :on-cast, :on-info, :on-state-change, :on-terminate}`
4681    /// patch (the exact case the peer `:on-*` accessor docstrings on
4682    /// [`crate::BehaviorSpec`] already name as deferred consumers of
4683    /// the slot), a future `feira validate --behavior` per-caixa
4684    /// admission verb, a per-`:behavior` overlay resolver a future
4685    /// per-cluster callback-overlay lift would materialize — was
4686    /// structurally forced to either re-inline the two-line
4687    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4688    /// pattern in lockstep with the layout wire-up (the duplication the
4689    /// PRIME DIRECTIVE names as a bug) or call the whole
4690    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4691    /// peer per-Caixa gate ([`Self::validate_nome`],
4692    /// [`Self::validate_versao`], [`Self::validate_deps`],
4693    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4694    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4695    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4696    /// [`Self::validate_limits`], [`Self::validate_upgrade_from`],
4697    /// [`Self::validate_code_paths`], plus the per-kind
4698    /// `require_supervisor_view` / `require_aplicacao_view` gates, plus
4699    /// the on-disk existence walks) to re-check one slot. Post-lift
4700    /// each such consumer reaches the [`crate::BehaviorSpec::validate`]
4701    /// six-slot cascade (and its identity-element on the absent slot)
4702    /// through one call on the substrate primitive.
4703    ///
4704    /// The per-slot compound entry-gate discipline lifted here onto the
4705    /// M2 `:behavior` axis is the sibling of the peer per-slot compound
4706    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4707    /// [`crate::MeshPolicy::validate`],
4708    /// [`crate::SupervisorSpec::validate_children`],
4709    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`],
4710    /// [`Self::validate_limits`]) that fold every structural + cross-
4711    /// slot axis on their slot onto one substrate primitive. Extended
4712    /// here to the M2 `:behavior` slot, the last of the four M2 typed
4713    /// slots (`:limits`, `:behavior`, `:upgrade-from`, plus the
4714    /// supervisor-only `:children` peer) whose per-Caixa compound-gate
4715    /// wire-up still lived open-coded at the layout altitude after the
4716    /// [`Self::validate_limits`] lift (baa4688) closed the sibling M2
4717    /// `:limits` slot's cascade. With this lift the "one named per-slot
4718    /// / per-Caixa compound gate per typed slot folding every structural
4719    /// axis on that slot (plus the `Option::None` identity element for
4720    /// the `Option`-shaped slots) onto one substrate primitive"
4721    /// discipline spans every M2 typed slot uniformly, so a reader who
4722    /// has learned any peer M2 gate reads `:behavior` without a per-
4723    /// slot exception carve-out.
4724    ///
4725    /// # Errors
4726    ///
4727    /// Returns every [`crate::BehaviorError`] variant on the present-
4728    /// slot arm — verbatim from [`crate::BehaviorSpec::validate`].
4729    /// Passes trivially on the absent-slot arm (`behavior: None`, the
4730    /// fold's identity element).
4731    pub fn validate_behavior(&self) -> Result<(), crate::BehaviorError> {
4732        match self.behavior() {
4733            Some(b) => b.validate(),
4734            None => Ok(()),
4735        }
4736    }
4737
4738    /// Reject `:restart-window` values the shared
4739    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4740    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4741    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4742    /// `Option<Duration>` routed through the shared codec via `with =
4743    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4744    /// view-construction path ([`Self::supervisor_view`]) folds the
4745    /// raw string through the same shared codec and soft-swallows the
4746    /// parse error as `None` to keep the view best-effort. Without
4747    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4748    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4749    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4750    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4751    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4752    /// edge case) silently produced a `SupervisorSpec` with
4753    /// `restart_window: None`, indistinguishable from the canonical
4754    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4755    /// `MaxIntensity / Period` invariant turns into a never-reset
4756    /// supervisor far from the source `caixa.lisp`, with no field
4757    /// naming the offending `:restart-window`. Lifting the gate to a
4758    /// Caixa-level validator mirrors the trajectory of the peer
4759    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4760    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4761    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4762    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4763    ///
4764    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4765    /// (the shared codec backing `:supervisor :restart-window` as
4766    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4767    /// `:politicas :circuit-breaker :window` — all three covered by
4768    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4769    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4770    /// variant, carrying the offending raw string + a parser-shaped
4771    /// reason naming the canonical authoring form, so the diagnostic
4772    /// is self-locating (the author can grep their `caixa.lisp` for
4773    /// `:restart-window "<value>"` and fix it in one edit) and
4774    /// uniform with every other manifest-level validate diagnostic.
4775    /// With this gate the four `:restart-window`-shaped surfaces (the
4776    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4777    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4778    /// now structurally equivalent — every value past the codec is in
4779    /// one accepted set, by construction.
4780    ///
4781    /// `None` (the canonical "omit the slot to express no reset"
4782    /// shape) is accepted trivially — the gate is a no-op when the
4783    /// author didn't author a window. The empty string is rejected by
4784    /// the shared codec (its digit-only gate refuses an empty
4785    /// magnitude), surfacing the same `RestartWindowMalformed`
4786    /// diagnostic as every other rejected non-canonical shape.
4787    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4788        let Some(s) = self.restart_window() else {
4789            return Ok(());
4790        };
4791        crate::supervisor::duration_codec::parse(s)
4792            .map(|_| ())
4793            .map_err(|reason| ManifestError::RestartWindowMalformed {
4794                restart_window: s.to_string(),
4795                reason,
4796            })
4797    }
4798
4799    /// Compound per-`Caixa` entry gate on the Aplicacao-kind mesh-slot
4800    /// family — folds the paired [`crate::AplicacaoSpec::validate`]
4801    /// typed-shape cascade (per-slot gates on `:membros`, `:contratos`,
4802    /// `:entrada`, `:placement`, `:politicas`, in that declared order)
4803    /// plus the cross-slot self-edge gate
4804    /// ([`crate::aplicacao::validate_no_self_membership`], the
4805    /// `:membros :caixa` ≠ `:nome` invariant the typed view cannot
4806    /// enforce on its own because it carries the membros but not the
4807    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
4808    /// non-Aplicacao kinds the fold is the identity element — the paired
4809    /// [`Self::aplicacao_view`] accessor returns `None` off the
4810    /// Aplicacao arm (peer with the [`Self::validate_limits`] /
4811    /// [`Self::validate_behavior`] M2 `Option`-arm identity element),
4812    /// so the gate passes trivially without touching the mesh slots.
4813    ///
4814    /// Prior to this lift the paired cascade lived only wired open-coded
4815    /// at the layout wire-up site
4816    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4817    /// as the three-line `let view = caixa.aplicacao_view().expect(...);
4818    /// view.validate() … validate_no_self_membership(...) …` pattern
4819    /// paired with two `.map_err(|err| LayoutError::AplicacaoViolation
4820    /// { caixa, issue })` wraps — every future consumer that wanted to
4821    /// gate the Aplicacao-shape cascade as a whole (the deferred
4822    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4823    /// admission webhook re-checking `:membros` / `:contratos` after a
4824    /// per-slot patch, a future `feira validate --aplicacao` per-caixa
4825    /// admission verb, a per-Aplicacao overlay resolver) was structurally
4826    /// forced to either re-inline the two-dispatch cascade in lockstep
4827    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
4828    /// names as a bug) or call the whole
4829    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4830    /// peer per-Caixa gate to re-check one slot family. Post-fold each
4831    /// such consumer reaches the two-arm compound gate through one call
4832    /// on the substrate primitive.
4833    ///
4834    /// Peer to the [`crate::render::require_aplicacao_view`] compound
4835    /// entry gate every per-Aplicacao *renderer* routes through
4836    /// (3aefefb folded `validate_no_self_membership` onto the renderer
4837    /// path) — this gate mirrors the same fold on the *layout* path, so
4838    /// the two consumers of the Aplicacao-shape cascade (the author-time
4839    /// gate and every per-Aplicacao renderer) share one substrate
4840    /// primitive rather than two open-coded cascades kept in lockstep.
4841    /// Same lift discipline the peer per-slot compound gates
4842    /// ([`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
4843    /// b5dd55e, [`Self::validate_limits`] baa4688,
4844    /// [`Self::validate_behavior`] 0d2877a) each carry.
4845    ///
4846    /// # Errors
4847    ///
4848    /// Returns every [`crate::AplicacaoError`] variant on the present-
4849    /// kind arm — the typed-shape cascade's per-slot arms first
4850    /// (matching [`crate::AplicacaoSpec::validate`]'s declared order),
4851    /// then the cross-slot self-edge arm
4852    /// ([`crate::AplicacaoError::MembroIsSelfAplicacao`]). Passes
4853    /// trivially on non-Aplicacao kinds (the fold's identity element).
4854    pub fn validate_aplicacao_shape(&self) -> Result<(), crate::AplicacaoError> {
4855        let Some(view) = self.aplicacao_view() else {
4856            return Ok(());
4857        };
4858        view.validate()?;
4859        crate::aplicacao::validate_no_self_membership(self.membros(), self.nome())?;
4860        Ok(())
4861    }
4862
4863    /// Reject per-entry values on the three Caixa-level code-surface
4864    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
4865    /// layout checker's `root.join(p)` sandbox would silently subvert.
4866    /// Same three structural footguns the peer
4867    /// [`BehaviorSpec::validate`] (b0c8389) and
4868    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
4869    /// (26da2c7) already close on the M2 `:behavior :on-*` and
4870    /// `:upgrade-from :state-change :script` axes, here lifted onto
4871    /// the three top-level code-path axes through the shared
4872    /// [`is_sandboxed_relative_path`] predicate:
4873    ///
4874    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
4875    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
4876    ///     [`Path::join`] as the base itself — `root.join("")` ==
4877    ///     `root`, so the existence check (`self.exists(&root)`)
4878    ///     trivially passes (the project root exists), and the layout
4879    ///     silently treats the project root as a biblioteca / exe /
4880    ///     servico entry. The `:bibliotecas` loop then hands the root
4881    ///     to `tatara_lisp::read` at `feira build` time as if the root
4882    ///     directory itself were a Lisp source file — a parse error
4883    ///     far from the source `caixa.lisp` with no field naming the
4884    ///     offending entry.
4885    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
4886    ///     [`Path::join`] *replaces* the base when the right-hand side
4887    ///     is absolute, so `root.join("/etc/passwd")` resolves to
4888    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
4889    ///     The existence check then silently consults whatever the
4890    ///     escaped path resolves to — for `:bibliotecas`, the layout
4891    ///     has no `starts_with`-fence (only `:exe` is fenced under
4892    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
4893    ///     `:bibliotecas` entry that happens to resolve on disk
4894    ///     silently passes. For `:exe` / `:servicos` the fence catches
4895    ///     the absolute case downstream as `ExeOutsideDir` /
4896    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
4897    ///     doesn't exist), but with a downstream-shaped diagnostic
4898    ///     that names the resolved escape path rather than the
4899    ///     authoring footgun at the source.
4900    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
4901    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
4902    ///     [`std::path::Component::ParentDir`] anywhere round-trips
4903    ///     through [`Path::join`] as a traversal above the caixa root.
4904    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
4905    ///     *component-aware* (not canonical-path-aware), so
4906    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
4907    ///     is **true** even though the canonical resolution
4908    ///     `{parent of root}/escape.lisp` lives outside the caixa root
4909    ///     — the fence silently lets the parent-escape through, and
4910    ///     the existence check passes if that escape-target happens
4911    ///     to exist. Caught regardless of where the `..` sits
4912    ///     (leading, mid-path, trailing) so the gate matches the peer
4913    ///     predicate's full coverage.
4914    ///
4915    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
4916    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
4917    /// same per-slot diagnostic shape every peer per-axis path-gate
4918    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
4919    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
4920    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
4921    /// order [`Caixa::declared_foreign_code_slots`] uses for its
4922    /// canonical foreign-code-slot diagnostic, so a manifest with
4923    /// multiple malformed slots surfaces the lexicographically-earliest
4924    /// slot's diagnostic deterministically.
4925    ///
4926    /// Lifted to the typed surface as a Caixa-level validator (peer
4927    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
4928    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
4929    /// and wired into [`crate::StandardLayout::verify`] before the
4930    /// existence-check loops so the diagnostic names the offending
4931    /// slot at the source caixa.lisp rather than reporting a
4932    /// downstream `MissingEntry` / `ExeOutsideDir` /
4933    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
4934    /// The fourth typed code-path surface — every author-supplied
4935    /// path on the manifest — is now structurally accept-shaped
4936    /// past validate, peer with `:behavior :on-*` and
4937    /// `:upgrade-from :state-change :script`.
4938    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
4939        /// Per-slot file-type contract for the three Caixa-level
4940        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
4941        /// Each variant names the predicate the per-entry file-type
4942        /// gate consults; [`Self::None`] opts the slot out of any
4943        /// file-type contract. Lifted as a typed local enum so the
4944        /// per-slot dispatch is exhaustive at the `match` — adding a
4945        /// future axis to the typed-substrate `:` slot set (the
4946        /// future `:assets` resource axis the M5 roadmap names, the
4947        /// future `:nix-flake` derivation axis the caixa-flake
4948        /// emitter consults) lands as one variant + one `match` arm,
4949        /// not a coordinated rewrite of every per-slot bool flag.
4950        ///
4951        /// Peer of the typed-substrate per-slot variant disciplines
4952        /// already established on this surface
4953        /// ([`crate::supervisor::RestartStrategy`] +
4954        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
4955        /// supervision-tree axis,
4956        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
4957        /// placement axis, [`crate::aplicacao::WitTarget`] on the
4958        /// `:contratos` payload-target axis): the typed `enum` is
4959        /// the substrate's single source of truth for the per-axis
4960        /// dispatch, and every consumer (the per-arm body here, the
4961        /// future feira-lint per-slot diagnostic renderer, the M4
4962        /// per-axis admission webhook) reaches for the same typed
4963        /// surface rather than re-deriving the partition from inline
4964        /// flag combinations.
4965        enum CodePathFileType {
4966            /// `:exe` — nix-build derivation output, no terminating-
4967            /// extension contract (the canonical `"exe/<name>"`
4968            /// fixtures the layout's `ExeOutsideDir` error message
4969            /// documents carry no extension by convention).
4970            None,
4971            /// `:bibliotecas` — tatara-lisp source files the
4972            /// `feira build` loop reads through `tatara_lisp::read`
4973            /// at parse time. Routes to [`is_lisp_extension`].
4974            LispSource,
4975            /// `:servicos` — ComputeUnit-CR YAML files the
4976            /// caixa-helm / caixa-flux renderers consume through
4977            /// `serde_yaml::from_str`. Routes to
4978            /// [`is_computeunit_yaml_extension`].
4979            ComputeUnitYaml,
4980        }
4981
4982        // The per-slot [`CodePathFileType`] selects which axes carry the
4983        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
4984        // source axis (the `feira build` loop at
4985        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
4986        // `tatara_lisp::read` at parse time) — the lifted
4987        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
4988        // `:exe` is the nix-built executable surface (per the canonical
4989        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
4990        // error message documents and every in-tree
4991        // `caixa_with_code_paths` positive control uses) — its file-type
4992        // contract is "nix-build derivation output", not a typed source
4993        // file, so [`CodePathFileType::None`] opts the slot out of any
4994        // file-type gate. `:servicos` is the `.computeunit.yaml`
4995        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
4996        // renderers consume each entry through `serde_yaml::from_str` as
4997        // a typed `ComputeUnit` CR) — the lifted
4998        // [`is_computeunit_yaml_extension`] predicate gates the compound
4999        // `.computeunit.yaml` suffix. All three axes are surfaced through
5000        // the same iteration so the sandbox-shape + duplicate gates
5001        // apply uniformly; the typed file-type dispatch fires per-slot
5002        // exactly where the downstream consumer's accepted set demands
5003        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
5004        // compounding lift on the peer 64772a9 `:bibliotecas`
5005        // `.lisp`-gate trajectory — the second of the three code-path
5006        // axes to land on a typed compound-suffix gate, with the same
5007        // self-locating per-slot diagnostic shape every peer per-axis
5008        // file-type lift uses (`*NonLispExtension { slot, path }` /
5009        // `*NonComputeUnitYamlExtension { slot, path }`).
5010        for (slot, list, file_type) in [
5011            (
5012                ":bibliotecas",
5013                &self.bibliotecas,
5014                CodePathFileType::LispSource,
5015            ),
5016            (":exe", &self.exe, CodePathFileType::None),
5017            (
5018                ":servicos",
5019                &self.servicos,
5020                CodePathFileType::ComputeUnitYaml,
5021            ),
5022        ] {
5023            // Per-slot set-not-multiset gate on the typed code-path axis.
5024            // Every peer Vec-shaped author-supplied list past validate is
5025            // a set, not a multiset: `:membros :caixa`
5026            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
5027            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
5028            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
5029            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
5030            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
5031            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
5032            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
5033            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
5034            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
5035            // the three code-path lists are the last Vec-shaped author-
5036            // supplied slots on the typed Caixa surface still admitting a
5037            // duplicate entry silently. Scope is per-list (`:bibliotecas`
5038            // duplicates are flagged within `:bibliotecas`, not across
5039            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
5040            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
5041            // legitimate dev-vs-runtime shape on the dep axis, fenced
5042            // separately by [`crate::dep::validate_no_self_dep`]). On the
5043            // code-path axis a cross-slot collision is structurally
5044            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
5045            // fence — `:exe` and `:servicos` entries are confined to their
5046            // own directory trees, so the only way a string could appear
5047            // on two code-path lists is the (rare, structurally invalid)
5048            // case where `:bibliotecas` carries an `"exe/<x>"` or
5049            // `"servicos/<x>.yaml"`-shaped path.
5050            //
5051            // Without the gate three authoring footguns silently passed:
5052            //
5053            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
5054            //     canonical copy-paste-the-wrong-file footgun. `feira
5055            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
5056            //     list and re-parses the same file twice, wasting work
5057            //     and silently masking the author's intent to declare a
5058            //     *second* biblioteca.
5059            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
5060            //     Binario surface. The future `caixa-flake` `nix flake`
5061            //     emitter that materializes each `:exe` entry as a flake
5062            //     `packages.<exe-name>` derivation would collide on the
5063            //     duplicate package name and surface a flake-eval error
5064            //     far from the source `caixa.lisp`.
5065            //   - `:servicos ("servicos/x.computeunit.yaml"
5066            //     "servicos/x.computeunit.yaml")` — the same footgun on
5067            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
5068            //     renderers already refuse `:servicos.len() != 1` with
5069            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
5070            //     that diagnostic surfaces "too many servicos" without
5071            //     naming "duplicate entry" — the typed self-locating
5072            //     "which entry is the duplicate" framing only lands at
5073            //     this gate.
5074            //
5075            // Same `seen.insert(entry.as_str())` shape every peer per-list
5076            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
5077            // 86c769b, `:deps` 359fba5) and the same "structural shape
5078            // checks fire before the duplicate check on the same entry"
5079            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
5080            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
5081            // empty entry first, not the duplicate on the later pair).
5082            let mut seen = std::collections::HashSet::new();
5083            for entry in list {
5084                let path = Path::new(entry);
5085                match is_sandboxed_relative_path(path) {
5086                    Ok(()) => {}
5087                    Err(PathShapeViolation::Empty) => {
5088                        return Err(ManifestError::CodePathEmpty { slot });
5089                    }
5090                    Err(PathShapeViolation::Absolute) => {
5091                        return Err(ManifestError::CodePathAbsolute {
5092                            slot,
5093                            path: path.to_path_buf(),
5094                        });
5095                    }
5096                    Err(PathShapeViolation::ParentEscape) => {
5097                        return Err(ManifestError::CodePathParentEscape {
5098                            slot,
5099                            path: path.to_path_buf(),
5100                        });
5101                    }
5102                }
5103                // The per-slot file-type gate dispatched through the
5104                // typed [`CodePathFileType`] selector above. Each variant
5105                // routes to the lifted predicate the downstream consumer
5106                // demands:
5107                //
5108                //   - [`LispSource`] → [`is_lisp_extension`] for
5109                //     `:bibliotecas` (the `feira build` loop's
5110                //     `tatara_lisp::read` consumer);
5111                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
5112                //     for `:servicos` (the caixa-helm / caixa-flux
5113                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
5114                //     accepted set);
5115                //   - [`None`] for `:exe` — the nix-build derivation-
5116                //     output axis has no terminating-extension contract.
5117                //
5118                // Fires after the sandbox-shape arms so a path that is
5119                // *both* sandbox-escaping and wrong-extension surfaces
5120                // the more fundamental sandbox-shape diagnostic first
5121                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
5122                // `ParentEscape` → `NonLispExtension` arm-ordering on
5123                // `:behavior :on-*` c97815a, and `EmptyScript` →
5124                // `AbsoluteScript` → `ParentEscapeScript` →
5125                // `NonLispExtensionScript` on
5126                // `:upgrade-from :state-change :script` 33cc830), and
5127                // before the duplicate gate so the narrower per-entry
5128                // file-type shape dominates the cross-entry uniqueness
5129                // diagnostic (a
5130                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
5131                // `:servicos` surfaces
5132                // `CodePathNonComputeUnitYamlExtension` on the first
5133                // entry rather than `CodePathDuplicate` on the pair —
5134                // peer with the 64772a9 `:bibliotecas`
5135                // `("lib/x.txt" "lib/x.txt")` ordering).
5136                match file_type {
5137                    CodePathFileType::None => {}
5138                    CodePathFileType::LispSource => {
5139                        if !is_lisp_extension(path) {
5140                            return Err(ManifestError::CodePathNonLispExtension {
5141                                slot,
5142                                path: path.to_path_buf(),
5143                            });
5144                        }
5145                    }
5146                    CodePathFileType::ComputeUnitYaml => {
5147                        if !is_computeunit_yaml_extension(path) {
5148                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
5149                                slot,
5150                                path: path.to_path_buf(),
5151                            });
5152                        }
5153                    }
5154                }
5155                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
5156                    ManifestError::CodePathDuplicate {
5157                        slot,
5158                        path: path.to_path_buf(),
5159                    }
5160                })?;
5161            }
5162        }
5163        Ok(())
5164    }
5165
5166    /// Reject `:etiquetas` lists with an empty entry or with two entries
5167    /// agreeing on the same string. `:etiquetas` is the universal
5168    /// registry-search-tag axis on [`Caixa`] (every kind carries the
5169    /// `Vec<String>` slot) and lands verbatim as the Helm chart
5170    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
5171    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
5172    /// a [`std::collections::BTreeSet`] alongside the four substrate-
5173    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
5174    /// Two authoring footguns silently passed validate without this gate:
5175    ///
5176    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
5177    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
5178    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
5179    ///     `chart.metadata.keywords` admits the value without a strict
5180    ///     parser-side gate, but the empty keyword has no operational
5181    ///     meaning — it indexes nothing in the future caixa-registry
5182    ///     search axis and clutters the rendered chart with a no-op tag.
5183    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
5184    ///     copy-paste-the-wrong-tag footgun) silently passed validate
5185    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
5186    ///     at chart render — a "second wins / one silently disappears"
5187    ///     shape divergent from every peer typed-graph set gate
5188    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
5189    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
5190    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
5191    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
5192    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
5193    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
5194    ///     on `:upgrade-from`, the per-instruction-class singularity
5195    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
5196    ///     [`crate::UpgradeError::DuplicateStateChange`] /
5197    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
5198    ///     discipline is uniform: every Vec-shaped author-supplied list
5199    ///     past validate is set-not-multiset, by construction.
5200    ///
5201    /// Past the empty arm the gate enforces the chart-keyword shape
5202    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
5203    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
5204    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
5205    /// continuation. Closes the canonical paste-from-doc footguns the
5206    /// bare empty + duplicate arms left open: paste-from-aligned-doc
5207    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
5208    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
5209    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
5210    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
5211    /// — the author meant three separate list entries), path-separator
5212    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
5213    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
5214    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
5215    /// control bytes that would silently land as malformed search tags
5216    /// in the rendered Chart.yaml `keywords:` array and break the
5217    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
5218    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
5219    /// established on the sibling universal-axis `Vec<String>` surface
5220    /// — the second universal-axis Vec<String> surface to land the
5221    /// empty-first-then-shape-then-duplicate per-entry cascade.
5222    ///
5223    /// Same empty-first cascade discipline every peer per-axis gate
5224    /// uses: the per-entry empty arm fires before the per-entry shape
5225    /// arm fires before the cross-entry duplicate arm, so an
5226    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
5227    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
5228    /// has no value" defect) before either the shape or the duplicate
5229    /// diagnostic. Walks the list in declaration order so the
5230    /// first-collision diagnostic surfaces the lexicographically-
5231    /// earliest offending position, peer with every other duplicate
5232    /// gate on this surface.
5233    ///
5234    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
5235    /// caixa-build gate alongside the peer universal gates
5236    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5237    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
5238    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
5239    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5240    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5241    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
5242    /// slot sets. The future caixa-registry search axis can reach for
5243    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
5244    /// chart-keyword-shaped string without re-deriving the precondition.
5245    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
5246        let mut seen = std::collections::HashSet::new();
5247        for etiqueta in self.etiquetas() {
5248            if etiqueta.is_empty() {
5249                return Err(ManifestError::EtiquetaEmpty);
5250            }
5251            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
5252                ManifestError::EtiquetaInvalid {
5253                    etiqueta: etiqueta.clone(),
5254                    reason,
5255                }
5256            })?;
5257            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
5258                ManifestError::EtiquetaDuplicate {
5259                    etiqueta: etiqueta.clone(),
5260                }
5261            })?;
5262        }
5263        Ok(())
5264    }
5265
5266    /// Reject `:autores` lists with an empty entry or with two entries
5267    /// agreeing on the same string. `:autores` is the universal
5268    /// maintainer-axis on [`Caixa`] (every kind carries the
5269    /// `Vec<String>` slot) and lands verbatim as the Helm chart
5270    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
5271    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
5272    /// to a `Maintainer { name, email: None }` without dedup). Two
5273    /// authoring footguns silently passed validate without this gate:
5274    ///
5275    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
5276    ///     blank-doc footgun) rendered as
5277    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
5278    ///     empty maintainer name has no operational meaning — it
5279    ///     identifies no one in the substrate's authorship index and
5280    ///     clutters the rendered chart with a no-op maintainer.
5281    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
5282    ///     the copy-paste-the-wrong-author footgun) silently passed
5283    ///     validate and rendered as two identical maintainer entries.
5284    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
5285    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
5286    ///     rendered `keywords:` array at chart-render time), the
5287    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
5288    ///     entries stack verbatim in the chart, divergent from every
5289    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
5290    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
5291    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
5292    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
5293    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
5294    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
5295    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
5296    ///     `:etiquetas`).
5297    ///
5298    /// Past the empty arm the gate enforces the chart-maintainer-name
5299    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
5300    /// the structural single-line printable-UTF-8 floor every realistic
5301    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
5302    /// or trailing whitespace, no ASCII control characters anywhere,
5303    /// Unicode bytes accepted. Closes the canonical paste-from-doc
5304    /// footguns the bare empty + duplicate arms left open:
5305    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
5306    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
5307    /// pasted a multi-line block of author records into one `:autores`
5308    /// entry instead of splitting into one entry per author),
5309    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
5310    /// and the paste-from-binary-blob control bytes that would silently
5311    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
5312    /// `maintainers:` array. Mirrors the shape-predicate cascade
5313    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
5314    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
5315    /// establish past their own empty arms on the sibling universal-axis
5316    /// `Option<String>` surfaces — the first universal-axis Vec<String>
5317    /// surface to land the empty-first-then-shape-then-duplicate per-entry
5318    /// cascade.
5319    ///
5320    /// Same empty-first cascade discipline every peer per-axis gate
5321    /// uses: the per-entry empty arm fires before the per-entry shape
5322    /// arm before the cross-entry duplicate arm. Walks the list in
5323    /// declaration order so the first-collision diagnostic surfaces the
5324    /// lexicographically-earliest offending position, peer with every
5325    /// other duplicate gate on this surface.
5326    ///
5327    /// Universal-axis (every kind carries `:autores`), so wired at the
5328    /// caixa-build gate alongside the peer universal gates
5329    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5330    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5331    /// [`Self::validate_code_paths`] — before the kind-coherence gates
5332    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5333    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5334    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5335    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
5336    /// slot sets.
5337    pub fn validate_autores(&self) -> Result<(), ManifestError> {
5338        let mut seen = std::collections::HashSet::new();
5339        for autor in self.autores() {
5340            if autor.is_empty() {
5341                return Err(ManifestError::AutorEmpty);
5342            }
5343            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
5344                ManifestError::AutorInvalid {
5345                    autor: autor.clone(),
5346                    reason,
5347                }
5348            })?;
5349            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
5350                ManifestError::AutorDuplicate {
5351                    autor: autor.clone(),
5352                }
5353            })?;
5354        }
5355        Ok(())
5356    }
5357
5358    /// Reject `:repositorio` values whose shape the shared
5359    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
5360    /// `repositorio: Option<String>` slot on [`Caixa`] is the
5361    /// universal git-shaped homepage axis every kind carries — the
5362    /// substrate routes the same string through two load-bearing
5363    /// consumers:
5364    ///
5365    ///   - [`caixa-helm`] folds it verbatim into the rendered
5366    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
5367    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
5368    ///     the chart `README.md` `repo = …` interpolation
5369    ///     (`caixa-helm/src/lib.rs:359`).
5370    ///   - [`caixa-flux`] folds it verbatim into the standalone
5371    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
5372    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
5373    ///     `GitRepository.spec.url` the cluster's source-controller
5374    ///     polls — the load-bearing deploy-time axis.
5375    ///
5376    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
5377    /// substitute a placeholder when the slot is absent (`None` → the
5378    /// fallback fires); a `Some("")` *skips the fallback* and silently
5379    /// passes the empty string through to `Chart.yaml home: ""` /
5380    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
5381    /// controller both reject the empty URL far from the source
5382    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
5383    /// Similarly a malformed `:repositorio` (whitespace, control char,
5384    /// missing `:` separator, leading `-`) silently lands in the
5385    /// rendered artifacts and breaks at `git clone` / `helm template`
5386    /// / `flux reconcile` time.
5387    ///
5388    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
5389    /// same shared predicate the peer [`crate::DepSource::validate`]
5390    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
5391    /// gate the two `git URL`-shaped surfaces on the typed Caixa
5392    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
5393    /// structurally equivalent: every value past validate is
5394    /// guaranteed-acceptable by the predicate's union of constraints
5395    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
5396    /// control chars, ASCII only, no leading `:`, contains a `:`
5397    /// separator). The predicate accepts every documented authoring
5398    /// shape — `github:org/repo` shorthand, `https://host/path`,
5399    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
5400    /// scp-style SSH, `file:///path` — and refuses the canonical
5401    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
5402    /// injection footguns at validate time. Maps the predicate's
5403    /// `String` reason verbatim into the
5404    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
5405    /// offending value + parser-shaped reason so the diagnostic is
5406    /// self-locating (the author can grep their `caixa.lisp` for
5407    /// `:repositorio "<value>"` and fix it in one edit).
5408    ///
5409    /// `None` (the canonical "omit the slot to express no published
5410    /// homepage" shape) is accepted trivially — the gate is a no-op
5411    /// when the author didn't declare a value. `Some("")` is gated by
5412    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
5413    /// shape predicate is consulted, mirroring the empty-first cascade
5414    /// every peer per-axis identity gate uses
5415    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
5416    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
5417    /// [`crate::DepError::FonteRepoEmpty`] →
5418    /// [`crate::DepError::FonteRepoInvalid`]).
5419    ///
5420    /// Universal-axis (every kind carries `:repositorio`), so wired at
5421    /// the caixa-build gate alongside the peer universal gates
5422    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5423    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5424    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
5425    /// before the kind-coherence gates
5426    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5427    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5428    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5429    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5430    /// specific slot sets.
5431    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
5432        let Some(s) = self.repositorio() else {
5433            return Ok(());
5434        };
5435        if s.is_empty() {
5436            return Err(ManifestError::RepositorioEmpty);
5437        }
5438        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
5439            repositorio: s.to_string(),
5440            reason,
5441        })
5442    }
5443
5444    /// Reject `:descricao` values that are the empty string. The flat
5445    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
5446    /// free-form-prose homepage axis every kind carries — the
5447    /// substrate routes the same string through two load-bearing
5448    /// consumers in the [`caixa-helm`] renderer:
5449    ///
5450    ///   - `build_chart_yaml` folds it verbatim into the rendered
5451    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
5452    ///     field (`caixa-helm/src/lib.rs:232-235`).
5453    ///   - `build_readme` folds it verbatim into the rendered chart
5454    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
5455    ///
5456    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
5457    /// substitute a `caixa.nome`-derived placeholder when the slot is
5458    /// absent (`None` → the fallback fires); a `Some("")` *skips the
5459    /// fallback* and silently passes the empty string through to
5460    /// `Chart.yaml description: ""` / a blank chart `README.md`
5461    /// header. Helm's chart spec requires a non-empty `description:`
5462    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
5463    /// `WARNING [chart.metadata.description]: description is required`),
5464    /// so the empty `Some("")` silently lands in the rendered
5465    /// artifacts and breaks at `helm lint` / `helm install` time far
5466    /// from the source `caixa.lisp`, with no field naming the
5467    /// offending `:descricao`.
5468    ///
5469    /// `None` (the canonical "omit the slot to defer to the renderer's
5470    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
5471    /// the gate is a no-op when the author didn't declare a value.
5472    /// `Some("")` is gated by the narrower
5473    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
5474    /// shape every peer per-axis empty gate uses
5475    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5476    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5477    /// [`ManifestError::RepositorioEmpty`]).
5478    ///
5479    /// Universal-axis (every kind carries `:descricao`), so wired at
5480    /// the caixa-build gate alongside the peer universal gates
5481    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5482    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5483    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5484    /// [`Self::validate_code_paths`] — before the kind-coherence
5485    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5486    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5487    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5488    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5489    /// specific slot sets.
5490    ///
5491    /// Past the empty arm the gate enforces the chart-description
5492    /// shape predicate via [`crate::render::is_chart_description_shape`]:
5493    /// the structural single-line UTF-8 floor every realistic chart
5494    /// description in the wild matches — 1..=512 bytes, no leading
5495    /// or trailing whitespace, no ASCII control characters anywhere
5496    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
5497    /// carriage return, and every other control byte), Unicode
5498    /// continuation bytes accepted (the canonical fixtures carry
5499    /// `→` and `—`). Closes the canonical paste-from-doc footguns
5500    /// the bare empty-arm gate left open: paste-from-aligned-doc
5501    /// leading / trailing whitespace (`" Checkout flow."`,
5502    /// `"Checkout flow. "`), paste-from-multiline-doc newline
5503    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
5504    /// (`"Checkout\rflow."`), tab-from-aligned-doc
5505    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
5506    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
5507    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
5508    /// [`Self::validate_edicao`] establish past their own empty arms
5509    /// on the sibling universal-axis `Option<String>` Caixa-level
5510    /// value-shape surfaces.
5511    ///
5512    /// The empty-first cascade discipline mirrors every peer per-axis
5513    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
5514    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
5515    /// diagnostic surfaces on `Some("")` rather than the broader
5516    /// shape-predicate diagnostic — peer with how
5517    /// [`ManifestError::LicencaEmpty`] runs before
5518    /// [`ManifestError::LicencaInvalid`],
5519    /// [`ManifestError::EdicaoEmpty`] runs before
5520    /// [`ManifestError::EdicaoInvalid`],
5521    /// [`ManifestError::RepositorioEmpty`] runs before
5522    /// [`ManifestError::RepositorioInvalid`].
5523    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
5524        let Some(s) = self.descricao() else {
5525            return Ok(());
5526        };
5527        if s.is_empty() {
5528            return Err(ManifestError::DescricaoEmpty);
5529        }
5530        crate::render::is_chart_description_shape(s).map_err(|reason| {
5531            ManifestError::DescricaoInvalid {
5532                descricao: s.to_string(),
5533                reason,
5534            }
5535        })?;
5536        Ok(())
5537    }
5538
5539    /// Reject `:licenca` values that are the empty string. The flat
5540    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
5541    /// SPDX-shaped license-expression axis every kind carries — the
5542    /// substrate routes the same string through the [`caixa-helm`]
5543    /// renderer's `build_readme` which folds it verbatim into the
5544    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
5545    /// section (`caixa-helm/src/lib.rs:361`) via
5546    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
5547    /// fallback only fires on `None`; a `Some("")` *skips the
5548    /// fallback* and silently passes the empty string through to a
5549    /// chart `README.md` whose `License` section renders as the bare
5550    /// trailing period (`.\n`) — peer footgun with the
5551    /// `Some("")`-skips-`unwrap_or_else` shape the
5552    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
5553    /// gates close on the sibling free-form-prose and git-URL axes.
5554    ///
5555    /// `None` (the canonical "omit the slot to defer to the
5556    /// renderer's `MIT` fallback" shape every existing fixture
5557    /// carries) is accepted trivially — the gate is a no-op when the
5558    /// author didn't declare a value. `Some("")` is gated by the
5559    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
5560    /// empty-arm shape every peer per-axis empty gate uses
5561    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5562    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5563    /// [`ManifestError::RepositorioEmpty`],
5564    /// [`ManifestError::DescricaoEmpty`]).
5565    ///
5566    /// Universal-axis (every kind carries `:licenca`), so wired at
5567    /// the caixa-build gate alongside the peer universal gates
5568    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5569    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5570    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5571    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
5572    /// — before the kind-coherence gates
5573    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5574    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5575    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5576    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5577    /// specific slot sets.
5578    ///
5579    /// Past the empty arm the gate enforces the SPDX-expression shape
5580    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
5581    /// structural alphabet floor every realistic SPDX expression in
5582    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
5583    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
5584    /// single ASCII space (token separator). Closes the canonical
5585    /// paste-from-doc footguns the bare empty-arm gate left open:
5586    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
5587    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
5588    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
5589    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
5590    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
5591    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
5592    /// Apache-2.0"`), and semicolon-list-separator confusion
5593    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
5594    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
5595    /// establish past their own empty arms.
5596    ///
5597    /// The empty-first cascade discipline mirrors every peer per-axis
5598    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
5599    /// [`ManifestError::LicencaInvalid`], so the narrower empty
5600    /// diagnostic surfaces on `Some("")` rather than the broader
5601    /// shape-predicate diagnostic — peer with how
5602    /// [`ManifestError::EdicaoEmpty`] runs before
5603    /// [`ManifestError::EdicaoInvalid`],
5604    /// [`ManifestError::RepositorioEmpty`] runs before
5605    /// [`ManifestError::RepositorioInvalid`].
5606    ///
5607    /// A future tightening on this axis can extend the alphabet
5608    /// floor into a full SPDX expression parser + license-id
5609    /// allowlist (rejecting alphabet-valid values that don't name a
5610    /// real SPDX license identifier — e.g., `"NotAReal"` is
5611    /// alphabet-valid but no `NotAReal` license-id exists). That
5612    /// parser only becomes meaningful past a real SPDX-spec
5613    /// dependency; this gate establishes the structural floor by
5614    /// refusing every non-SPDX-alphabet value at validate time.
5615    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
5616        let Some(s) = self.licenca() else {
5617            return Ok(());
5618        };
5619        if s.is_empty() {
5620            return Err(ManifestError::LicencaEmpty);
5621        }
5622        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
5623            ManifestError::LicencaInvalid {
5624                licenca: s.to_string(),
5625                reason,
5626            }
5627        })?;
5628        Ok(())
5629    }
5630
5631    /// Reject `:edicao` values that are the empty string. The flat
5632    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
5633    /// language-edition axis every kind carries — it determines the
5634    /// tatara-lisp macro surface + compatibility flags the substrate
5635    /// applies when building a caixa, and lands verbatim in the
5636    /// `Caixa::template` author-time scaffold (the canonical
5637    /// `:edicao "2026"` line every `feira init` emits via
5638    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
5639    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
5640    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
5641    /// `caixa-core/src/render.rs:2510`) via
5642    /// `edicao: Some("2026".into())`.
5643    ///
5644    /// `None` (the canonical "omit the slot to defer to the
5645    /// substrate's default edition" shape every existing
5646    /// [`caixa-resolver`] integration test fixture carries via
5647    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5648    /// is accepted trivially — the gate is a no-op when the author
5649    /// didn't declare a value. `Some("")` is gated by the narrower
5650    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
5651    /// shape every peer per-axis empty gate uses
5652    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5653    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5654    /// [`ManifestError::RepositorioEmpty`],
5655    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
5656    ///
5657    /// Universal-axis (every kind carries `:edicao`), so wired at
5658    /// the caixa-build gate alongside the peer universal gates
5659    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5660    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5661    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5662    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
5663    /// [`Self::validate_code_paths`] — before the kind-coherence
5664    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5665    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5666    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5667    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5668    /// specific slot sets.
5669    ///
5670    /// Past the empty arm the gate enforces the canonical year-shape
5671    /// predicate: every documented tatara-lisp edition is a 4-digit
5672    /// ASCII decimal year (`"2026"` is the only edition currently
5673    /// minted; future-introduced siblings will follow the same
5674    /// shape, peer with Cargo's `[package] edition` grammar which
5675    /// every value Cargo has ever accepted matches — `"2015"`,
5676    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
5677    /// 4 ASCII decimal bytes is rejected with the narrower
5678    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
5679    /// shape-predicate cascade [`Self::validate_repositorio`]
5680    /// establishes past its own empty arm
5681    /// ([`ManifestError::RepositorioEmpty`] →
5682    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
5683    /// paste-from-doc footguns the bare empty-arm gate left open:
5684    ///
5685    ///   - leading / trailing whitespace from a paste-from-doc
5686    ///     (`"2026 "`, `" 2026"`)
5687    ///   - control characters / CRLF from a paste-from-multiline-doc
5688    ///     (`"2026\n"`)
5689    ///   - non-ASCII look-alikes from a fullwidth keyboard
5690    ///     (`"2026"`) which would silently land as a non-ASCII
5691    ///     string in the rendered caixa.lisp
5692    ///   - free-form non-year values (`"x"`, `"latest"`,
5693    ///     `"nightly"`) that have no operational meaning on the
5694    ///     substrate's build-time edition selector
5695    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
5696    ///     `"r2026"`) — common version-tag idioms that don't apply
5697    ///     to the year-shaped edition axis
5698    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
5699    ///     edition is a year, not a fractional version
5700    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
5701    ///     `"00026"`) that don't name a year
5702    ///
5703    /// `None` (the canonical "omit the slot to defer to the
5704    /// substrate's default edition" shape every existing
5705    /// [`caixa-resolver`] integration test fixture carries via
5706    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5707    /// is accepted trivially — the gate is a no-op when the author
5708    /// didn't declare a value. The empty-first cascade discipline
5709    /// mirrors every peer per-axis identity gate:
5710    /// [`ManifestError::EdicaoEmpty`] runs before
5711    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
5712    /// diagnostic surfaces on `Some("")` rather than the broader
5713    /// shape-predicate diagnostic — peer with how
5714    /// [`ManifestError::NomeEmpty`] runs before
5715    /// [`ManifestError::NomeInvalid`],
5716    /// [`ManifestError::VersaoEmpty`] runs before
5717    /// [`ManifestError::VersaoInvalid`],
5718    /// [`ManifestError::RepositorioEmpty`] runs before
5719    /// [`ManifestError::RepositorioInvalid`].
5720    ///
5721    /// A future tightening on this axis can extend the shape
5722    /// predicate into a known-edition allowlist (rejecting
5723    /// year-shaped values that don't name a tatara-lisp edition
5724    /// the substrate actually understands — e.g., `"1999"` is
5725    /// year-shaped but no `1999` edition exists). That allowlist
5726    /// only becomes meaningful past the introduction of a sibling
5727    /// edition to `"2026"`; this gate establishes the structural
5728    /// floor by refusing every non-year-shaped value at validate
5729    /// time.
5730    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
5731        let Some(s) = self.edicao() else {
5732            return Ok(());
5733        };
5734        if s.is_empty() {
5735            return Err(ManifestError::EdicaoEmpty);
5736        }
5737        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
5738            return Err(ManifestError::EdicaoInvalid {
5739                edicao: s.to_string(),
5740                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
5741            });
5742        }
5743        Ok(())
5744    }
5745
5746    /// Compose the supervisor-related flat slots into a single
5747    /// [`SupervisorSpec`] for validation. Returns `None` when the
5748    /// caixa isn't a `:kind Supervisor`.
5749    ///
5750    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
5751    /// simple (one form, no nested `:supervisor (…)` block); this view
5752    /// is the "typed shape" the operator + supervisor reconciler
5753    /// consume.
5754    #[must_use]
5755    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
5756        if !self.kind().is_supervisor() {
5757            return None;
5758        }
5759        // Fold through the shared `supervisor::duration_codec::parse`
5760        // — the same parser the serde-routed `with = "duration_codec"`
5761        // on `SupervisorSpec::restart_window`, the `:politicas
5762        // :timeout` codec, and the `:politicas :circuit-breaker
5763        // :window` codec all consume. The prior inline f64-shaped
5764        // duplicate (`parse_window_inline`) admitted every magnitude
5765        // the integer-magnitude gate (1c55a2a) rejects on the three
5766        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
5767        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
5768        // `None` (i.e. "no reset"), divergent from the shared codec's
5769        // integer-magnitude discipline by construction. The fold
5770        // closes the divergence: every value the typed
5771        // `SupervisorSpec` carries past `supervisor_view` is in the
5772        // shared codec's accepted set. The `.ok()` here preserves the
5773        // existing soft-swallow shape on this view-construction path;
5774        // the new [`Caixa::validate_restart_window`] (sibling of
5775        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
5776        // the offending raw string at build time so authoring tools
5777        // (`feira lint`, the future layout-side wire-up) surface a
5778        // self-locating diagnostic instead of a silently dropped
5779        // window.
5780        let restart_window = self
5781            .restart_window()
5782            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
5783        Some(SupervisorSpec {
5784            // Route the author-omitted `:estrategia` arm through the
5785            // substrate-canonical
5786            // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
5787            // `pub const` rather than the transitively-derived
5788            // [`RestartStrategy::default`] route the prior
5789            // `.unwrap_or_default()` fold reached for — one source of
5790            // truth for the Erlang/OTP `one_for_one` half of Learn You
5791            // Some Erlang's `{one_for_one, intensity, 5, 60}` worker-
5792            // supervisor canonical default that also backs the
5793            // [`crate::supervisor::Default for RestartStrategy`] impl
5794            // and the [`crate::supervisor::Default for SupervisorSpec`]
5795            // impl's struct-literal `estrategia` field, all now routed
5796            // through the same lifted constant. Prior to the lift the
5797            // composition site carried `.unwrap_or_default()` with no
5798            // compile-time link back to the shared OTP-canonical
5799            // default that the peer paired
5800            // `.unwrap_or(SUPERVISOR_MAX_RESTARTS_DEFAULT)` (b698ec0)
5801            // arm on the sibling `:max-restarts` axis routes through —
5802            // so a future rebrand of the OTP-canonical strategy default
5803            // (a widening to `rest_for_one` once the substrate
5804            // discovers startup-order-coupled child cohorts as the more
5805            // common shape, a per-cluster overlay the operator pins
5806            // through the MESH-COMPOSITION §III.2 supervision-canary
5807            // `:estrategia-overrides` roadmap slot) would have had to
5808            // migrate the paired `MaxIntensity` + `Period` halves
5809            // through the lifted constants and the `one_for_one` half
5810            // through a `RestartStrategy::default()` route in lockstep
5811            // or the three halves of the same OTP-canonical default
5812            // would silently drift out of pairing. Byte-parity against
5813            // the lifted constant closes the split. Pinned by
5814            // [`supervisor_view_estrategia_fallback_routes_through_lifted_default`]
5815            // in the tests module.
5816            estrategia: self
5817                .estrategia()
5818                .unwrap_or(crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT),
5819            // Route the author-omitted `:max-restarts` arm through the
5820            // substrate-canonical [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`]
5821            // typed `pub const` rather than the raw `5` literal — one
5822            // source of truth for the Erlang/OTP-canonical
5823            // `{intensity, 5, 60}` `MaxIntensity` default that also
5824            // backs the serde-side wire-format author-omitted arm on
5825            // [`crate::supervisor::SupervisorSpec::max_restarts`] via
5826            // `#[serde(default = "default_max_restarts")]` and the
5827            // [`Default for SupervisorSpec`] impl's struct-literal
5828            // default field. Prior to the lift the composition site
5829            // carried a raw `5` with no compile-time link back to the
5830            // serde-side default, so a future rebrand of the OTP-
5831            // canonical default (a tightening to Elixir's `3`, a
5832            // widening to a per-cluster overlay the operator pins
5833            // through the MESH-COMPOSITION §III.2 supervision-canary
5834            // `:supervisor :max-restarts-overrides` roadmap slot)
5835            // would have had to be threaded through both open-coded
5836            // copies in lockstep or the wire-format author-omitted arm
5837            // and this view-construction author-omitted arm would
5838            // silently disagree on which restart-budget an omitted
5839            // `:max-restarts` resolves to. Pinned by
5840            // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
5841            // in the tests module.
5842            max_restarts: self
5843                .max_restarts()
5844                .unwrap_or(crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT),
5845            restart_window,
5846            children: self.children().to_vec(),
5847        })
5848    }
5849
5850    /// A minimal starter manifest emitted by `feira init`.
5851    #[must_use]
5852    pub fn template(nome: &str) -> String {
5853        format!(
5854            "(defcaixa\n  \
5855               :nome        {nome:?}\n  \
5856               :versao      \"0.1.0\"\n  \
5857               :kind        Biblioteca\n  \
5858               :edicao      \"2026\"\n  \
5859               :descricao   \"FIXME — describe this caixa\"\n  \
5860               :autores     ()\n  \
5861               :etiquetas   ()\n  \
5862               :deps        ()\n  \
5863               :deps-dev    ()\n  \
5864               :bibliotecas (\"lib/{nome}.lisp\"))\n"
5865        )
5866    }
5867
5868    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
5869    /// back after mutation (e.g. `feira add`).
5870    ///
5871    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
5872    /// The derive-macro `compile_from_sexp` path is the inverse, so any
5873    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
5874    #[must_use]
5875    pub fn to_lisp(&self) -> String {
5876        let json = serde_json::to_value(self).expect("Caixa serialize");
5877        let sexp = tatara_lisp::domain::json_to_sexp(&json);
5878        let tatara_lisp::Sexp::List(items) = sexp else {
5879            return format!("(defcaixa {sexp})\n");
5880        };
5881        let mut out = String::from("(defcaixa");
5882        let mut i = 0;
5883        while i + 1 < items.len() {
5884            out.push_str("\n  ");
5885            out.push_str(&items[i].to_string());
5886            out.push(' ');
5887            out.push_str(&items[i + 1].to_string());
5888            i += 2;
5889        }
5890        out.push_str(")\n");
5891        out
5892    }
5893}
5894
5895/// Errors raised by top-level [`Caixa`] validators that don't fit
5896/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
5897/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
5898/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
5899/// through every substrate-side artifact's `metadata.name` /
5900/// version derivation.
5901///
5902/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
5903/// doc-comment anticipates) can hold one of each per-axis error
5904/// family without reshaping individual diagnostics; this enum is
5905/// the first such per-Caixa-identity family.
5906#[derive(Debug, Error, PartialEq, Eq)]
5907pub enum ManifestError {
5908    #[error(
5909        ":nome is empty (every caixa must name itself; the value flows \
5910         into every K8s artifact's `metadata.name` derivation and into \
5911         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
5912    )]
5913    NomeEmpty,
5914    #[error(
5915        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
5916         apiserver enforces this rule on every `metadata.name` the \
5917         caixa's substrate-side renderers derive from `:nome` — the \
5918         `lareira-<nome>` Helm chart name, the programs.yaml entry \
5919         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
5920         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
5921         name; use a lowercase alphanumeric + hyphen identifier like \
5922         `\"checkout\"` or `\"cart-v2\"`)"
5923    )]
5924    NomeInvalid { nome: String, reason: String },
5925    #[error(
5926        ":nome {nome:?} overflows the joint-length budget on the canonical \
5927         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
5928         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
5929         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
5930         `chart:` slot, `caixa-tatara`'s `release_name` + \
5931         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
5932         joint name through the canonical `lareira_chart_name` helper, and \
5933         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
5934         DNS-1123 label cap on every chart-name-derived `metadata.name` \
5935         reject any joint name exceeding 63 bytes; the narrower \
5936         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
5937         arm gates the chart-name budget downstream renderers inherit)"
5938    )]
5939    NomeChartNameBudgetExceeded { nome: String, reason: String },
5940    #[error(
5941        ":versao is empty (every caixa must pin its own version; the value flows \
5942         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
5943         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
5944         `:latest` tags, the lacre closure's `concrete_versao`, and the \
5945         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
5946    )]
5947    VersaoEmpty,
5948    #[error(
5949        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
5950         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
5951         with optional `-prerelease` and `+build` — across every artifact derived \
5952         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
5953         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
5954         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
5955         and the `:upgrade-from :from` peers that match against this exact shape; \
5956         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
5957         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
5958         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
5959    )]
5960    VersaoInvalid { versao: String, reason: String },
5961    #[error(
5962        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
5963         substrate consumes this string through the shared \
5964         `supervisor::duration_codec` — the same parser routed via `with = \
5965         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
5966         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
5967         the canonical authoring form is `<integer><unit>` where the unit is one \
5968         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
5969         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
5970         Without this gate a malformed `:restart-window` silently produced a \
5971         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
5972         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
5973         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
5974         layer with the offending value named verbatim. Omit the slot entirely to \
5975         express \"no reset\"; carry a positive integer duration to express the \
5976         sliding window)"
5977    )]
5978    RestartWindowMalformed {
5979        restart_window: String,
5980        reason: String,
5981    },
5982    #[error(
5983        "{slot} entry is an empty path string — every {slot} entry must name \
5984         a file relative to the caixa root; omit the entry to omit the file \
5985         (the layout checker's `root.join(\"\")` resolves to the caixa root \
5986         itself, so an empty entry silently aliases the project root as a \
5987         declared {slot} file, then fails downstream at parse / existence \
5988         time with a diagnostic that names the root rather than the offending \
5989         entry)"
5990    )]
5991    CodePathEmpty { slot: &'static str },
5992    #[error(
5993        "{slot} entry {} is an absolute path — entries must be relative to \
5994         the caixa root, since `Path::join` replaces the base with an absolute \
5995         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
5996         outside the caixa root sandbox; rewrite the entry as a relative path \
5997         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
5998         `\"servicos/<name>.computeunit.yaml\"`)",
5999        path.display()
6000    )]
6001    CodePathAbsolute { slot: &'static str, path: PathBuf },
6002    #[error(
6003        "{slot} entry {} contains a `..` component — entries must not traverse \
6004         above the caixa root (the layout's `starts_with(<dir>)` fence on \
6005         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
6006         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
6007         has no such fence, so a leading `..` escapes unconditionally if the \
6008         resolved target happens to exist)",
6009        path.display()
6010    )]
6011    CodePathParentEscape { slot: &'static str, path: PathBuf },
6012    #[error(
6013        "{slot} entry {} does not terminate in the `.lisp` extension — every \
6014         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
6015         loop reads through `tatara_lisp::read` at parse time, so any other \
6016         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
6017         structurally a parser error far from the source caixa.lisp, with \
6018         no field naming the offending `:bibliotecas` entry. Pin a relative \
6019         path under the caixa root whose terminating extension is \
6020         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
6021         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
6022         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
6023         (33cc830) axes already carry through the same lifted \
6024         `is_lisp_extension` predicate",
6025        path.display()
6026    )]
6027    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
6028    #[error(
6029        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
6030         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
6031         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
6032         through `serde_yaml::from_str` at chart / FluxCD bundle render \
6033         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
6034         off-by-one-segment `.computeunit-yaml`, the editor-backup \
6035         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
6036         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
6037         source caixa.lisp, with no field naming the offending `:servicos` \
6038         entry. Pin a relative path under the caixa root whose terminating \
6039         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
6040         `\"servicos/<name>.computeunit.yaml\"`, \
6041         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
6042         contract the sibling `:bibliotecas` axis (64772a9) already carries \
6043         on the tatara-lisp-source axis through the peer lifted \
6044         `is_lisp_extension` predicate, here on the compound-suffix axis \
6045         `Path::extension` can't express on its own through the lifted \
6046         `is_computeunit_yaml_extension` predicate",
6047        path.display()
6048    )]
6049    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
6050    #[error(
6051        "{slot} entry {} appears more than once (the code-path list is \
6052         a set, not a multiset; every peer Vec-shaped author-supplied \
6053         list past validate is set-not-multiset — `:membros :caixa`, \
6054         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
6055         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
6056         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
6057         code-path lists are the last Vec-shaped author-supplied slots on \
6058         the typed Caixa surface still admitting a duplicate entry. \
6059         `:bibliotecas` duplicates re-parse the same file at \
6060         `feira build` time and silently mask the author's intent to \
6061         declare a *second* biblioteca; `:exe` duplicates collide on the \
6062         flake `packages.<name>` derivation key at the future \
6063         `caixa-flake` materializer; `:servicos` duplicates surface as the \
6064         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
6065         rejection far from the source `caixa.lisp`. Drop the duplicate \
6066         or rename it to the actual second file intended)",
6067        path.display()
6068    )]
6069    CodePathDuplicate { slot: &'static str, path: PathBuf },
6070    #[error(
6071        ":etiquetas entry is empty (every tag must carry a non-empty \
6072         registry-search identifier; the empty entry has no operational \
6073         meaning — it indexes nothing in the future caixa-registry search \
6074         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
6075         with a no-op tag; omit the entry to express \"no tag on this \
6076         position\")"
6077    )]
6078    EtiquetaEmpty,
6079    #[error(
6080        ":etiquetas entry {etiqueta:?} appears more than once (the \
6081         registry-search tag set is a set, not a multiset; duplicate \
6082         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
6083         at chart render — a \"second wins / one silently disappears\" \
6084         shape divergent from every peer typed-graph set gate \
6085         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
6086         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
6087         duplicate or rename it to the actual tag intended)"
6088    )]
6089    EtiquetaDuplicate { etiqueta: String },
6090    #[error(
6091        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
6092         {reason} (the substrate consumes this string through the shared \
6093         `crate::render::is_chart_keyword_shape` predicate — the same \
6094         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
6095         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
6096         continuation. The canonical authoring shapes are short kebab-case \
6097         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
6098         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
6099         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
6100         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
6101         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
6102         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
6103         `\"mesh,http,grpc\"` — the author meant to author three separate \
6104         list entries; path-separator confusion `\"caixa/servico\"`; \
6105         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
6106         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
6107         `\"café\"` — every legitimate search tag is strict ASCII; \
6108         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
6109         passed `from_lisp` + `validate_etiquetas` + \
6110         `StandardLayout::verify` and landed in the rendered \
6111         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
6112         malformed search tag — Artifact Hub's keyword index + the future \
6113         caixa-registry's keyword index would either silently drop the \
6114         tag or fail to index it far from the source caixa.lisp; the gate \
6115         moves the diagnostic to the manifest layer with the offending \
6116         value named verbatim)"
6117    )]
6118    EtiquetaInvalid { etiqueta: String, reason: String },
6119    #[error(
6120        ":autores entry is empty (every maintainer must carry a non-empty \
6121         identifier; the empty entry has no operational meaning — it \
6122         identifies no one in the substrate's authorship index and renders \
6123         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
6124         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
6125         omit the entry to express \"no maintainer on this position\")"
6126    )]
6127    AutorEmpty,
6128    #[error(
6129        ":autores entry {autor:?} appears more than once (the maintainer \
6130         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
6131         `maintainers:` rendering does *no* dedup — duplicate entries \
6132         stack verbatim in `Chart.yaml` as two identical \
6133         `Maintainer {{ name, email: None }}` records, divergent from every \
6134         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
6135         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
6136         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
6137         rename it to the actual author intended)"
6138    )]
6139    AutorDuplicate { autor: String },
6140    #[error(
6141        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
6142         {reason} (the substrate consumes this string through the shared \
6143         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
6144         single-line-UTF-8 floor every realistic chart maintainer name carries: \
6145         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
6146         characters anywhere, Unicode bytes accepted. The canonical authoring \
6147         shapes are short single-line identifiers like `\"pleme-io\"`, \
6148         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
6149         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
6150         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
6151         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
6152         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
6153         records into one entry instead of splitting into one entry per author; \
6154         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
6155         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
6156         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
6157         `validate_autores` + `StandardLayout::verify` and landed in the \
6158         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
6159         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
6160         round-trip — every chart-aware UI (`helm list`, `helm search`, \
6161         Artifact Hub maintainer index) would render the maintainer name in a \
6162         single-line column far from the source caixa.lisp; the gate moves the \
6163         diagnostic to the manifest layer with the offending value named \
6164         verbatim)"
6165    )]
6166    AutorInvalid { autor: String, reason: String },
6167    #[error(
6168        ":repositorio is the empty string (every published caixa names its \
6169         git source via a non-empty `:repositorio` locator — the value \
6170         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
6171         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
6172         `GitRepository.spec.url` via `caixa-flux`'s \
6173         `ClusterBundleOpts::for_caixa`; both consumers' \
6174         `Option::unwrap_or_else` fallbacks only fire when the slot is \
6175         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
6176         `url: \"\"` in the rendered artifacts and breaks at `helm \
6177         template` / FluxCD source-controller reconcile time far from the \
6178         source caixa.lisp; omit the slot entirely to defer to the \
6179         renderer's `https://github.com/pleme-io/<nome>` / \
6180         `caixa.nome`-derived fallback, or carry a canonical authoring \
6181         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
6182         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
6183         `\"file:///path\"`)"
6184    )]
6185    RepositorioEmpty,
6186    #[error(
6187        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
6188         (the substrate consumes this string through the shared \
6189         `crate::render::is_git_repo_url` predicate — the same parser the \
6190         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
6191         value through via `DepSource::validate`; the canonical authoring \
6192         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
6193         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
6194         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
6195         scp-style SSH form. Without this gate a malformed `:repositorio` \
6196         (whitespace from a paste-from-doc; control characters / CRLF \
6197         from a paste-from-multiline-doc; a leading `-` from a \
6198         CLI-argument-injection footgun; a missing `:` separator from a \
6199         bare `org/repo` shape git treats as a relative filesystem path) \
6200         silently landed in the rendered `Chart.yaml home:` and the \
6201         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
6202         FluxCD reconcile time far from the source caixa.lisp; the gate \
6203         moves the diagnostic to the manifest layer with the offending \
6204         value named verbatim)"
6205    )]
6206    RepositorioInvalid { repositorio: String, reason: String },
6207    #[error(
6208        ":descricao is the empty string (every published caixa names \
6209         its purpose via a non-empty `:descricao` summary — the value \
6210         flows verbatim into the rendered `lareira-<nome>` Helm \
6211         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
6212         `build_chart_yaml` and into the chart `README.md` header via \
6213         `build_readme`; both consumers' `Option::unwrap_or_else` \
6214         `caixa.nome`-derived fallbacks only fire when the slot is \
6215         `None`, so an empty `Some(\"\")` silently lands as \
6216         `description: \"\"` / a blank `README.md` header in the \
6217         rendered artifacts and breaks at `helm lint` time \
6218         (`WARNING [chart.metadata.description]: description is \
6219         required` on `apiVersion: v2` charts) far from the source \
6220         caixa.lisp; omit the slot entirely to defer to the \
6221         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
6222         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
6223         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
6224         Servico.\"`)"
6225    )]
6226    DescricaoEmpty,
6227    #[error(
6228        ":descricao {descricao:?} is not a valid chart-description shape: \
6229         {reason} (the substrate consumes this string through the shared \
6230         `crate::render::is_chart_description_shape` predicate — the same \
6231         single-line-UTF-8 floor every realistic chart description carries: \
6232         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
6233         characters anywhere, Unicode prose bytes accepted. The canonical \
6234         authoring shapes are short single-line summaries like `\"Canonical \
6235         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
6236         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
6237         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
6238         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
6239         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
6240         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
6241         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
6242         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
6243         `validate_descricao` + `StandardLayout::verify` and landed in the \
6244         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
6245         field + `README.md` header paragraph as a YAML-illegal multi-line \
6246         scalar or a silently-trimmed whitespace round-trip — every \
6247         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
6248         render the description in a single-line column far from the source \
6249         caixa.lisp; the gate moves the diagnostic to the manifest layer \
6250         with the offending value named verbatim)"
6251    )]
6252    DescricaoInvalid { descricao: String, reason: String },
6253    #[error(
6254        ":licenca is the empty string (every published caixa names \
6255         its license via a non-empty `:licenca` SPDX expression — the \
6256         value flows verbatim into the rendered `lareira-<nome>` Helm \
6257         chart's `README.md` `## License` section via `caixa-helm`'s \
6258         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
6259         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
6260         only fires when the slot is `None`, so an empty `Some(\"\")` \
6261         silently lands as a bare trailing period in the rendered \
6262         chart `README.md` `License` section far from the source \
6263         caixa.lisp; omit the slot entirely to defer to the \
6264         renderer's `MIT` fallback, or carry a canonical SPDX \
6265         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
6266         `\"Apache-2.0 OR MIT\"`)"
6267    )]
6268    LicencaEmpty,
6269    #[error(
6270        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
6271         (the substrate consumes this string through the shared \
6272         `crate::render::is_spdx_expression_shape` predicate — the same \
6273         alphabet-floor parser every peer per-axis value-shape gate routes \
6274         its value through; the canonical authoring shapes are single \
6275         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
6276         compound expressions like `\"Apache-2.0 OR MIT\"`, \
6277         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
6278         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
6279         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
6280         like `\"LicenseRef-MyLicense\"` / \
6281         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
6282         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
6283         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
6284         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
6285         a smart-quote paste; underscore-instead-of-hyphen typo \
6286         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
6287         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
6288         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
6289         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
6290         `README.md` `## License` section + a future SPDX-aware \
6291         `Chart.yaml license:` emitter would refuse the value at \
6292         `helm lint` time far from the source caixa.lisp; the gate moves \
6293         the diagnostic to the manifest layer with the offending value \
6294         named verbatim)"
6295    )]
6296    LicencaInvalid { licenca: String, reason: String },
6297    #[error(
6298        ":edicao is the empty string (every published caixa names \
6299         its language edition via a non-empty `:edicao` value — the \
6300         edition determines the tatara-lisp macro surface + \
6301         compatibility flags the substrate applies when building \
6302         the caixa; the canonical `Caixa::template` scaffold every \
6303         `feira init` emits carries `:edicao \"2026\"` verbatim and \
6304         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
6305         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
6306         construction, so an empty `Some(\"\")` silently lands as a \
6307         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
6308         a future renderer-side consumer that folds it through \
6309         `Option::unwrap_or_else` will skip the fallback and pass the \
6310         empty edition through to the substrate's build-time edition \
6311         selector far from the source caixa.lisp; omit the slot \
6312         entirely to defer to the substrate's default edition, or \
6313         carry a canonical edition like `\"2026\"`)"
6314    )]
6315    EdicaoEmpty,
6316    #[error(
6317        ":edicao {edicao:?} is not a valid edition: {reason} (every \
6318         documented tatara-lisp edition is a 4-digit ASCII decimal \
6319         year — `\"2026\"` is the only edition currently minted; \
6320         future-introduced siblings will follow the same shape, peer \
6321         with Cargo's `[package] edition` grammar which every value \
6322         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
6323         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
6324         paste-from-doc footguns silently passed: a trailing space \
6325         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
6326         from a paste-from-multiline-doc, a fullwidth-keyboard \
6327         look-alike (`\"2026\"`), a free-form non-year value \
6328         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
6329         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
6330         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
6331         wrong-length numeric value (`\"26\"`, `\"202\"`, \
6332         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
6333         rendered caixa.lisp and broke at the substrate's \
6334         build-time edition selector far from the source caixa.lisp; \
6335         omit the slot entirely to defer to the substrate's default \
6336         edition, or carry a canonical 4-digit ASCII decimal year \
6337         like `\"2026\"`)"
6338    )]
6339    EdicaoInvalid { edicao: String, reason: String },
6340}
6341
6342#[cfg(test)]
6343mod tests {
6344    use super::*;
6345
6346    #[test]
6347    fn template_round_trips() {
6348        let src = Caixa::template("demo");
6349        let c = Caixa::from_lisp(&src).expect("template must parse");
6350        assert_eq!(c.nome, "demo");
6351        assert_eq!(c.versao, "0.1.0");
6352        assert_eq!(c.kind, CaixaKind::Biblioteca);
6353        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
6354        assert!(c.deps.is_empty());
6355        assert!(c.deps_dev.is_empty());
6356    }
6357
6358    #[test]
6359    fn caixa_universal_axis_scalar_accessor_pair_is_const_fn() {
6360        // Fail-before-pass-after pin on [`Caixa::nome`] +
6361        // [`Caixa::versao`]'s `const`-eval-surface posture. Each
6362        // accessor projects the top-level manifest's per-`:nome` /
6363        // per-`:versao` [`String`] storage through the `pub const fn`
6364        // [`String::as_str`] (const-stable since Rust 1.87, well within
6365        // the workspace MSRV) — any future accidental downgrade to
6366        // non-`const` fails the corresponding `<name>_via_const_fn`
6367        // wrapper at caixa-core build time with E0015 (`cannot call
6368        // non-const method`), strictly stronger than a runtime
6369        // `assert!`. Sibling of the peer per-M2/M3-slot `String → &str`
6370        // scalar-accessor family pins on the sibling `const`-eval-
6371        // surface passes ([`crate::CaixaVersion::as_str`] at the
6372        // typed-newtype wrapper, [`crate::aplicacao::Membro::nome`] /
6373        // [`crate::aplicacao::Membro::versao_requirement`] at the M3
6374        // membership axis, [`crate::aplicacao::Entrada::hostname`] /
6375        // [`crate::aplicacao::Entrada::destination`] at the M3 ingress
6376        // axis, [`crate::supervisor::ChildSpec::nome`] /
6377        // [`crate::supervisor::ChildSpec::versao_requirement`] at the
6378        // M2 supervisor-tree axis,
6379        // [`crate::upgrade::UpgradeFromEntry::prior_versao`] at the M2
6380        // upgrade axis, [`crate::dep::Dep::nome`] /
6381        // [`crate::dep::Dep::versao_requirement`] at the dep-graph
6382        // axis, and the per-`:contratos`
6383        // [`crate::aplicacao::WitContract::source`] /
6384        // [`crate::aplicacao::WitContract::destination`] /
6385        // [`crate::aplicacao::WitContract::world_ref`] trio the
6386        // sibling pin at 279823b already anchors).
6387        const fn nome_via_const_fn(c: &Caixa) -> &str {
6388            c.nome()
6389        }
6390        const fn versao_via_const_fn(c: &Caixa) -> &str {
6391            c.versao()
6392        }
6393        let src = Caixa::template("demo");
6394        let c = Caixa::from_lisp(&src).expect("template must parse");
6395        assert_eq!(nome_via_const_fn(&c), c.nome());
6396        assert_eq!(versao_via_const_fn(&c), c.versao());
6397        assert_eq!(c.nome(), "demo");
6398        assert_eq!(c.versao(), "0.1.0");
6399    }
6400
6401    #[test]
6402    fn caixa_option_string_scalar_accessor_family_is_const_fn() {
6403        // Fail-before-pass-after pin on the five per-`Caixa`
6404        // `Option<String> → Option<&str>` scalar accessors
6405        // ([`Caixa::licenca`] / [`Caixa::repositorio`] /
6406        // [`Caixa::descricao`] / [`Caixa::edicao`] on the top-level
6407        // manifest's optional universal-axis surface, plus
6408        // [`Caixa::restart_window`] on the M2 supervisor-tree
6409        // per-`SupervisorSpec` peer raw-window-string projection axis).
6410        // Each accessor destructures the typed slot's `Option<String>`
6411        // storage through the `match &self.<field> { Some(s) =>
6412        // Some(s.as_str()), None => None }` shape — routing through
6413        // [`String::as_str`] (const-stable since Rust 1.87, well within
6414        // the workspace MSRV) rather than the non-const
6415        // [`Option::as_deref`] the pre-lift bodies carried — and any
6416        // future accidental downgrade to non-`const` fails the
6417        // corresponding `<name>_via_const_fn` wrapper at caixa-core
6418        // build time with E0015 (`cannot call non-const method`),
6419        // strictly stronger than a runtime `assert!` and strictly
6420        // stronger than a module-scope `const _: () = assert!(…)` pin
6421        // (which cannot be formed on a `&Caixa` fixture because the
6422        // type's `String` / `Option<String>` carriers rule out
6423        // `const`-context value construction; the `const fn` wrapper
6424        // is the load-bearing shape that side-steps the destructor-in-
6425        // const restriction on the value axis while still pinning the
6426        // `const`-fn posture on the callee — mirror of the sibling
6427        // [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
6428        // pin's discipline verbatim on the peer non-`Option`
6429        // `String → &str` axis at the same struct).
6430        //
6431        // Peer of the sibling per-M2/M3-slot `Option<String> →
6432        // Option<&str>` accessor family pin
6433        // [`m3_option_string_scalar_accessor_family_is_const_fn`] on
6434        // the M3 mesh-slot atom axes ([`WitContract::endpoint`] /
6435        // [`WitContract::subject`] / [`WitContract::slot`] on the
6436        // per-`:contratos` payload-carrier trio,
6437        // [`Placement::shard_key`] / [`Placement::affinity`] on the
6438        // per-`:placement` optional-scalar pair).
6439        const fn licenca_via_const_fn(c: &Caixa) -> Option<&str> {
6440            c.licenca()
6441        }
6442        const fn repositorio_via_const_fn(c: &Caixa) -> Option<&str> {
6443            c.repositorio()
6444        }
6445        const fn descricao_via_const_fn(c: &Caixa) -> Option<&str> {
6446            c.descricao()
6447        }
6448        const fn edicao_via_const_fn(c: &Caixa) -> Option<&str> {
6449            c.edicao()
6450        }
6451        const fn restart_window_via_const_fn(c: &Caixa) -> Option<&str> {
6452            c.restart_window()
6453        }
6454        // Sweep both the `Some`-carrying arm (author-declared slot,
6455        // the byte-string projection payload) and the `None`-carrying
6456        // arm (author-omitted slot, the default-path projection) on
6457        // every accessor so the `const fn` wrapper family pins each
6458        // axis's canonical two-arm partition through the same const
6459        // dispatch as the runtime path.
6460        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6461        c1.licenca = Some("MIT".to_string());
6462        c1.repositorio = Some("https://github.com/pleme-io/demo".to_string());
6463        c1.descricao = Some("demo caixa".to_string());
6464        c1.edicao = Some("2024".to_string());
6465        c1.restart_window = Some("60s".to_string());
6466        assert_eq!(licenca_via_const_fn(&c1), c1.licenca());
6467        assert_eq!(repositorio_via_const_fn(&c1), c1.repositorio());
6468        assert_eq!(descricao_via_const_fn(&c1), c1.descricao());
6469        assert_eq!(edicao_via_const_fn(&c1), c1.edicao());
6470        assert_eq!(restart_window_via_const_fn(&c1), c1.restart_window());
6471        assert_eq!(c1.licenca(), Some("MIT"));
6472        assert_eq!(c1.repositorio(), Some("https://github.com/pleme-io/demo"));
6473        assert_eq!(c1.descricao(), Some("demo caixa"));
6474        assert_eq!(c1.edicao(), Some("2024"));
6475        assert_eq!(c1.restart_window(), Some("60s"));
6476        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6477        c2.licenca = None;
6478        c2.repositorio = None;
6479        c2.descricao = None;
6480        c2.edicao = None;
6481        c2.restart_window = None;
6482        assert_eq!(licenca_via_const_fn(&c2), None);
6483        assert_eq!(repositorio_via_const_fn(&c2), None);
6484        assert_eq!(descricao_via_const_fn(&c2), None);
6485        assert_eq!(edicao_via_const_fn(&c2), None);
6486        assert_eq!(restart_window_via_const_fn(&c2), None);
6487    }
6488
6489    #[test]
6490    fn caixa_outer_copy_return_accessor_pair_is_const_fn() {
6491        // Fail-before-pass-after pin on the two outer-[`Caixa`]
6492        // `Copy`-return accessors — [`Caixa::kind`] on the required
6493        // [`CaixaKind`] enum-discriminant axis and [`Caixa::estrategia`]
6494        // on the M2 supervisor-tree flat-spread `Option<RestartStrategy>`
6495        // axis. Both accessors project a `Copy`-carrier field
6496        // (`CaixaKind: Copy` at caixa-core/src/kind.rs:17,
6497        // `RestartStrategy: Copy` at caixa-core/src/supervisor.rs:33 →
6498        // `Option<RestartStrategy>: Copy`) by value through a bare
6499        // `self.<field>` field-access — no dispatch, no destructor, no
6500        // heap. Any future accidental downgrade to non-`const` fails
6501        // the corresponding `<name>_via_const_fn` wrapper at caixa-core
6502        // build time with E0015 (`cannot call non-const method`),
6503        // strictly stronger than a runtime `assert!` and strictly
6504        // stronger than a module-scope `const _: () = assert!(…)` pin
6505        // (which cannot be formed on a `&Caixa` fixture because the
6506        // type's `String` / `Vec` / `Option<Composite>` carriers rule
6507        // out `const`-context value construction; the `const fn`
6508        // wrapper is the load-bearing shape that side-steps the
6509        // destructor-in-const restriction on the value axis while still
6510        // pinning the `const`-fn posture on the callee — mirror of the
6511        // sibling [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
6512        // + [`caixa_option_string_scalar_accessor_family_is_const_fn`]
6513        // pins' discipline verbatim on the peer outer-`Caixa`
6514        // `String → &str` + `Option<String> → Option<&str>` axes at the
6515        // same struct).
6516        //
6517        // Peer of the sibling per-M2/M3-slot `Copy`-return accessor pin
6518        // family on the inner-altitude nested-spec typed-slot
6519        // discriminator axes: [`crate::supervisor::SupervisorSpec::estrategia`]
6520        // + [`crate::supervisor::ChildSpec::restart`] on the M2
6521        // supervisor-tree axis (pinned at 152c868), and
6522        // [`crate::aplicacao::Placement::estrategia`] +
6523        // [`crate::aplicacao::Entrada::port`] on the M3 mesh-slot axis
6524        // (pinned at bafa004) — the outer-`Caixa` altitude is the last
6525        // unlifted altitude for the `Copy`-return-accessor family.
6526        const fn kind_via_const_fn(c: &Caixa) -> CaixaKind {
6527            c.kind()
6528        }
6529        const fn estrategia_via_const_fn(c: &Caixa) -> Option<crate::supervisor::RestartStrategy> {
6530            c.estrategia()
6531        }
6532        // Sweep every arm of both discriminant partitions the accessors
6533        // fan on — every [`CaixaKind`] variant the six-arm required
6534        // discriminant carries (Biblioteca / Binario / Servico /
6535        // Supervisor / Aplicacao / Acao) and both arms of the
6536        // [`Option<RestartStrategy>`] flat-spread supervisor-tree slot
6537        // (`Some(<strategy>)` on an author-declared supervisor and
6538        // `None` on the author-omitted default arm every non-Supervisor
6539        // caixa carries by `#[serde(default)]`) — so the `const fn`
6540        // wrapper family pins the closed-set partition through the
6541        // same const dispatch as the runtime path.
6542        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6543        c1.kind = CaixaKind::Servico;
6544        c1.estrategia = Some(crate::supervisor::RestartStrategy::OneForAll);
6545        assert_eq!(kind_via_const_fn(&c1), c1.kind());
6546        assert_eq!(estrategia_via_const_fn(&c1), c1.estrategia());
6547        assert_eq!(c1.kind(), CaixaKind::Servico);
6548        assert_eq!(
6549            c1.estrategia(),
6550            Some(crate::supervisor::RestartStrategy::OneForAll)
6551        );
6552        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6553        c2.kind = CaixaKind::Aplicacao;
6554        c2.estrategia = None;
6555        assert_eq!(kind_via_const_fn(&c2), CaixaKind::Aplicacao);
6556        assert_eq!(estrategia_via_const_fn(&c2), None);
6557        // Anchor the remaining discriminant arms so any future
6558        // reordering of [`CaixaKind`]'s six-variant enum surfaces
6559        // through the wrapper dispatch, not just through the direct
6560        // method call.
6561        for kind in [
6562            CaixaKind::Biblioteca,
6563            CaixaKind::Binario,
6564            CaixaKind::Servico,
6565            CaixaKind::Supervisor,
6566            CaixaKind::Aplicacao,
6567            CaixaKind::Acao,
6568        ] {
6569            let mut c = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6570            c.kind = kind;
6571            assert_eq!(kind_via_const_fn(&c), kind);
6572        }
6573    }
6574
6575    #[test]
6576    fn caixa_outer_string_slice_return_accessor_family_is_const_fn() {
6577        // Fail-before-pass-after pin on the five outer-[`Caixa`]
6578        // `Vec<String> → &[String]` slice-return accessors on the
6579        // universal-axis surface — [`Caixa::autores`] / [`Caixa::etiquetas`]
6580        // / [`Caixa::bibliotecas`] / [`Caixa::exe`] / [`Caixa::servicos`].
6581        // Each body is a bare `self.<field>.as_slice()` dispatch through
6582        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
6583        // the workspace MSRV). Any future accidental downgrade to
6584        // non-`const` fails the corresponding `<name>_via_const_fn`
6585        // wrapper at caixa-core build time with E0015 (`cannot call
6586        // non-const method`) — mirror of the sibling
6587        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] pin's
6588        // discipline on the peer outer-`Caixa` `Copy`-return accessor
6589        // axis, and peer of the sibling composite-carrier slice-return
6590        // pin below on the peer outer-`Caixa` composite-slice axis.
6591        const fn autores_via_const_fn(c: &Caixa) -> &[String] {
6592            c.autores()
6593        }
6594        const fn etiquetas_via_const_fn(c: &Caixa) -> &[String] {
6595            c.etiquetas()
6596        }
6597        const fn bibliotecas_via_const_fn(c: &Caixa) -> &[String] {
6598            c.bibliotecas()
6599        }
6600        const fn exe_via_const_fn(c: &Caixa) -> &[String] {
6601            c.exe()
6602        }
6603        const fn servicos_via_const_fn(c: &Caixa) -> &[String] {
6604            c.servicos()
6605        }
6606        // Sweep the empty arm (`autores` / `etiquetas` / `exe` /
6607        // `servicos` — the template's `Vec::new()` default) and the
6608        // populated arm (mutated below) on every accessor so the
6609        // `const fn` wrapper family pins each axis's two-arm partition
6610        // through the same const dispatch as the runtime path.
6611        // [`Caixa::template`] seeds `lib/demo.lisp` into `:bibliotecas`,
6612        // so that arm's "empty" fixture is the populated arm the
6613        // mutation sweep covers.
6614        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6615        assert!(autores_via_const_fn(&c_empty).is_empty());
6616        assert!(etiquetas_via_const_fn(&c_empty).is_empty());
6617        assert!(exe_via_const_fn(&c_empty).is_empty());
6618        assert!(servicos_via_const_fn(&c_empty).is_empty());
6619        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6620        c_full.autores = vec!["ada".to_string(), "erlang".to_string()];
6621        c_full.etiquetas = vec!["compounding".to_string()];
6622        c_full.bibliotecas = vec!["lib/one.lisp".to_string(), "lib/two.lisp".to_string()];
6623        c_full.exe = vec!["exe/cli.lisp".to_string()];
6624        c_full.servicos = vec!["servicos/one.computeunit.yaml".to_string()];
6625        assert_eq!(autores_via_const_fn(&c_full), c_full.autores());
6626        assert_eq!(autores_via_const_fn(&c_full), &["ada", "erlang"]);
6627        assert_eq!(etiquetas_via_const_fn(&c_full), c_full.etiquetas());
6628        assert_eq!(etiquetas_via_const_fn(&c_full), &["compounding"]);
6629        assert_eq!(bibliotecas_via_const_fn(&c_full), c_full.bibliotecas());
6630        assert_eq!(
6631            bibliotecas_via_const_fn(&c_full),
6632            &["lib/one.lisp", "lib/two.lisp"]
6633        );
6634        assert_eq!(exe_via_const_fn(&c_full), c_full.exe());
6635        assert_eq!(exe_via_const_fn(&c_full), &["exe/cli.lisp"]);
6636        assert_eq!(servicos_via_const_fn(&c_full), c_full.servicos());
6637        assert_eq!(
6638            servicos_via_const_fn(&c_full),
6639            &["servicos/one.computeunit.yaml"]
6640        );
6641    }
6642
6643    #[test]
6644    fn caixa_outer_composite_slice_return_accessor_family_is_const_fn() {
6645        // Fail-before-pass-after pin on the six outer-[`Caixa`] composite-
6646        // carrier `Vec<T> → &[T]` slice-return accessors — [`Caixa::deps`]
6647        // / [`Caixa::deps_dev`] on the dep-graph axis,
6648        // [`Caixa::upgrade_from`] on the M2 appup axis, [`Caixa::children`]
6649        // on the M2 supervisor-tree axis, and [`Caixa::membros`] /
6650        // [`Caixa::contratos`] on the M3 mesh-slot axis. Each body is a
6651        // bare `self.<field>.as_slice()` dispatch through
6652        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
6653        // the workspace MSRV) — peer of the sibling `String`-payload
6654        // slice-return pin above on the peer outer-`Caixa` universal-
6655        // axis surface, and peer of the sibling inner-composite-
6656        // altitude reference-return pin family
6657        // [`crate::aplicacao::tests::m3_aplicacao_spec_reference_return_accessor_family_is_const_fn`]
6658        // + [`crate::supervisor::tests::supervisor_children_slice_return_accessor_is_const_fn`]
6659        // + [`crate::upgrade::tests::upgrade_from_entry_instructions_slice_return_accessor_is_const_fn`]
6660        // (all pinned at 0b23e0f).
6661        const fn deps_via_const_fn(c: &Caixa) -> &[Dep] {
6662            c.deps()
6663        }
6664        const fn deps_dev_via_const_fn(c: &Caixa) -> &[Dep] {
6665            c.deps_dev()
6666        }
6667        const fn upgrade_from_via_const_fn(c: &Caixa) -> &[UpgradeFromEntry] {
6668            c.upgrade_from()
6669        }
6670        const fn children_via_const_fn(c: &Caixa) -> &[crate::supervisor::ChildSpec] {
6671            c.children()
6672        }
6673        const fn membros_via_const_fn(c: &Caixa) -> &[crate::aplicacao::Membro] {
6674            c.membros()
6675        }
6676        const fn contratos_via_const_fn(c: &Caixa) -> &[crate::aplicacao::WitContract] {
6677            c.contratos()
6678        }
6679        // Empty-arm sweep on all six composite-carrier axes — every
6680        // `Caixa::template` starts with `Vec::new()` on each.
6681        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6682        assert!(deps_via_const_fn(&c_empty).is_empty());
6683        assert!(deps_dev_via_const_fn(&c_empty).is_empty());
6684        assert!(upgrade_from_via_const_fn(&c_empty).is_empty());
6685        assert!(children_via_const_fn(&c_empty).is_empty());
6686        assert!(membros_via_const_fn(&c_empty).is_empty());
6687        assert!(contratos_via_const_fn(&c_empty).is_empty());
6688        // Populate `:membros` / `:contratos` directly via struct literals
6689        // — the parser-side validation path fans on `:kind`-gated cross-
6690        // slot invariants irrelevant to the accessor dispatch under test.
6691        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6692        c_full.membros = vec![
6693            crate::aplicacao::Membro {
6694                caixa: "demo-a".to_string(),
6695                versao: "^0.1.0".to_string(),
6696            },
6697            crate::aplicacao::Membro {
6698                caixa: "demo-b".to_string(),
6699                versao: "^0.2.0".to_string(),
6700            },
6701        ];
6702        c_full.contratos = vec![crate::aplicacao::WitContract {
6703            de: "demo-a".to_string(),
6704            para: "demo-b".to_string(),
6705            wit: "wasi:http/proxy".to_string(),
6706            endpoint: Some("/edge".to_string()),
6707            subject: None,
6708            slot: None,
6709        }];
6710        assert_eq!(membros_via_const_fn(&c_full), c_full.membros());
6711        assert_eq!(contratos_via_const_fn(&c_full), c_full.contratos());
6712        assert_eq!(membros_via_const_fn(&c_full).len(), 2);
6713        assert_eq!(contratos_via_const_fn(&c_full).len(), 1);
6714        // Alias-borrow check on the four remaining composite-carrier
6715        // slice-return arms — the wrapper's return borrow must alias the
6716        // caller's borrow so any future accessor re-routing that skips
6717        // the storage field surfaces through the assertion.
6718        assert!(std::ptr::eq(deps_via_const_fn(&c_full), c_full.deps()));
6719        assert!(std::ptr::eq(
6720            deps_dev_via_const_fn(&c_full),
6721            c_full.deps_dev()
6722        ));
6723        assert!(std::ptr::eq(
6724            upgrade_from_via_const_fn(&c_full),
6725            c_full.upgrade_from()
6726        ));
6727        assert!(std::ptr::eq(
6728            children_via_const_fn(&c_full),
6729            c_full.children()
6730        ));
6731    }
6732
6733    #[test]
6734    fn caixa_outer_option_composite_reference_return_accessor_family_is_const_fn() {
6735        // Fail-before-pass-after pin on the six outer-[`Caixa`]
6736        // `Option<Composite> → Option<&Composite>` reference-return
6737        // accessors — [`Caixa::limits`] / [`Caixa::behavior`] on the M2
6738        // Servico-runtime typed-slot axis, [`Caixa::politicas`] /
6739        // [`Caixa::placement`] / [`Caixa::entrada`] on the M3 mesh-slot
6740        // axis, and [`Caixa::ci`] on the Acao-kind typed-CI-run axis.
6741        // Each body is a bare `self.<field>.as_ref()` dispatch through
6742        // [`Option::as_ref`] (const-stable since Rust 1.83, well within
6743        // the workspace MSRV of 1.89). Any future accidental downgrade
6744        // to non-`const` fails the corresponding `<name>_via_const_fn`
6745        // wrapper at caixa-core build time with E0015 (`cannot call
6746        // non-const method`), strictly stronger than a runtime `assert!`
6747        // and strictly stronger than a module-scope `const _: () =
6748        // assert!(…)` pin (which cannot be formed on a `&Caixa` fixture
6749        // because the type's `String` / `Vec` / `Option<Composite>`
6750        // carriers rule out `const`-context value construction; the
6751        // `const fn` wrapper is the load-bearing shape that side-steps
6752        // the destructor-in-const restriction on the value axis while
6753        // still pinning the `const`-fn posture on the callee — mirror
6754        // of the sibling
6755        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] +
6756        // [`caixa_outer_string_slice_return_accessor_family_is_const_fn`] +
6757        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
6758        // pins' discipline verbatim on the peer outer-`Caixa` axes at
6759        // the same struct).
6760        //
6761        // Closes the outer-`Caixa` `Option<&Composite>` composite-
6762        // reference-return sub-family — the last unlifted altitude on
6763        // the outer-`Caixa` accessor-family const-eval surface after
6764        // the sibling `Copy`-return / universal-axis-`&str` /
6765        // `Option<&str>` / `&[String]` / composite-`&[T]` pins already
6766        // closed the sibling arms at 866d1d5 / 29c5d7e / 0650f64 /
6767        // 231a968 (the last of these pins the `Vec<T> → &[T]`
6768        // composite-slice arm the six accessors here close as their
6769        // `Option<Composite> → Option<&Composite>` peer). Peer of the
6770        // sibling inner-altitude nested-spec composite-reference-return
6771        // pin family — [`crate::AplicacaoSpec::politicas`] /
6772        // [`crate::AplicacaoSpec::placement`] /
6773        // [`crate::AplicacaoSpec::entrada`] on the inner
6774        // [`crate::AplicacaoSpec`] altitude (already `pub const fn`
6775        // per 0b23e0f), and the outer-`Caixa` altitude here now carries
6776        // the same shape so both altitudes of the reference-return
6777        // discipline (per-`Caixa` outer-slot presence + per-
6778        // `AplicacaoSpec` inner-slot presence) route through one typed
6779        // const dispatch on the substrate primitive.
6780        const fn limits_via_const_fn(c: &Caixa) -> Option<&LimitsSpec> {
6781            c.limits()
6782        }
6783        const fn behavior_via_const_fn(c: &Caixa) -> Option<&crate::BehaviorSpec> {
6784            c.behavior()
6785        }
6786        const fn politicas_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::MeshPolicy> {
6787            c.politicas()
6788        }
6789        const fn placement_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Placement> {
6790            c.placement()
6791        }
6792        const fn entrada_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Entrada> {
6793            c.entrada()
6794        }
6795        const fn ci_via_const_fn(c: &Caixa) -> Option<&canteiro_types::CiRun> {
6796            c.ci()
6797        }
6798        // Both-arm sweep on every accessor: the `None` author-omitted
6799        // arm (template default — no M2/M3/CI slot declared) and the
6800        // `Some(<composite>)` authored arm (mutated below via struct-
6801        // literal seeds, side-stepping the parser-side `:kind`-gated
6802        // cross-slot invariants irrelevant to the accessor dispatch
6803        // under test). Both arms route through the `const fn` wrapper
6804        // family so the two-arm `Option` partition is pinned through
6805        // the same const dispatch as the runtime path.
6806        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6807        assert!(limits_via_const_fn(&c_empty).is_none());
6808        assert!(behavior_via_const_fn(&c_empty).is_none());
6809        assert!(politicas_via_const_fn(&c_empty).is_none());
6810        assert!(placement_via_const_fn(&c_empty).is_none());
6811        assert!(entrada_via_const_fn(&c_empty).is_none());
6812        assert!(ci_via_const_fn(&c_empty).is_none());
6813        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6814        c_full.limits = Some(LimitsSpec::default());
6815        c_full.behavior = Some(crate::BehaviorSpec::default());
6816        c_full.politicas = Some(crate::aplicacao::MeshPolicy::default());
6817        c_full.placement = Some(crate::aplicacao::Placement::default());
6818        c_full.entrada = Some(crate::aplicacao::Entrada {
6819            host: "demo.quero.cloud".to_string(),
6820            para: "demo".to_string(),
6821            paths: Vec::new(),
6822            port: crate::aplicacao::DEFAULT_SERVICO_PORT,
6823        });
6824        c_full.ci = Some(canteiro_types::CiRun {
6825            workspace: "pleme-io".into(),
6826            repo: "caixa".into(),
6827            nodes: vec![],
6828        });
6829        assert!(limits_via_const_fn(&c_full).is_some());
6830        assert!(behavior_via_const_fn(&c_full).is_some());
6831        assert!(politicas_via_const_fn(&c_full).is_some());
6832        assert!(placement_via_const_fn(&c_full).is_some());
6833        assert!(entrada_via_const_fn(&c_full).is_some());
6834        assert!(ci_via_const_fn(&c_full).is_some());
6835        // Alias-borrow check on every arm: the wrapper's inner-`Option`
6836        // reference must alias the caller's borrow so any future accessor
6837        // re-routing that skips the storage field surfaces through the
6838        // assertion.
6839        assert!(std::ptr::eq(
6840            limits_via_const_fn(&c_full).unwrap(),
6841            c_full.limits().unwrap()
6842        ));
6843        assert!(std::ptr::eq(
6844            behavior_via_const_fn(&c_full).unwrap(),
6845            c_full.behavior().unwrap()
6846        ));
6847        assert!(std::ptr::eq(
6848            politicas_via_const_fn(&c_full).unwrap(),
6849            c_full.politicas().unwrap()
6850        ));
6851        assert!(std::ptr::eq(
6852            placement_via_const_fn(&c_full).unwrap(),
6853            c_full.placement().unwrap()
6854        ));
6855        assert!(std::ptr::eq(
6856            entrada_via_const_fn(&c_full).unwrap(),
6857            c_full.entrada().unwrap()
6858        ));
6859        assert!(std::ptr::eq(
6860            ci_via_const_fn(&c_full).unwrap(),
6861            c_full.ci().unwrap()
6862        ));
6863    }
6864
6865    #[test]
6866    fn register_populates_registry() {
6867        Caixa::register().expect("first register call in this test process must succeed");
6868        let kws = tatara_lisp::domain::registered_keywords();
6869        assert!(kws.contains(&"defcaixa"));
6870    }
6871
6872    #[test]
6873    fn to_lisp_round_trips() {
6874        let src = Caixa::template("demo");
6875        let c1 = Caixa::from_lisp(&src).unwrap();
6876        let emitted = c1.to_lisp();
6877        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
6878        assert_eq!(c1, c2);
6879    }
6880
6881    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
6882    //
6883    // The compounding pin: the variant stores only the typed
6884    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
6885    // (canonical keyword, description, consumer) routes through the enum's
6886    // own accessors at Display time. Prior to that closure the variant
6887    // carried each accessor's return value as a stored `&'static str`
6888    // snapshot alongside `dialeto`; a caller could construct the variant
6889    // with a snapshot that drifted from what `dialeto`'s accessors would
6890    // return, and every downstream user-facing projection would silently
6891    // disagree with the classification. Storing only the axis makes the
6892    // drift structurally impossible.
6893
6894    #[test]
6895    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
6896        // Single-field construction is the whole compounding shape — a
6897        // future re-introduction of a snapshot field (a `palavra_canonica:
6898        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
6899        // would re-open the drift surface and this construction would fail
6900        // to compile with "missing field" until every snapshot was seeded
6901        // at the call site again. The compile-time guarantee is the
6902        // invariant; the assertion below only witnesses that the
6903        // construction is well-formed after the closure.
6904        let err = LeituraError::DialetoEstrangeiro {
6905            dialeto: crate::dialeto::CaixaDialeto::Molde,
6906        };
6907        assert!(matches!(
6908            err,
6909            LeituraError::DialetoEstrangeiro {
6910                dialeto: crate::dialeto::CaixaDialeto::Molde,
6911            }
6912        ));
6913    }
6914
6915    #[test]
6916    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
6917        // For every foreign-dialect classification the variant surfaces —
6918        // [`crate::dialeto::CaixaDialeto::Molde`] and
6919        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
6920        // variants [`Caixa::from_lisp`] raises this error for — the
6921        // rendered [`std::fmt::Display`] byte-string must interpolate each
6922        // typed accessor's return verbatim. A future re-introduction of a
6923        // stored `&'static str` snapshot alongside `dialeto` that Display
6924        // read instead of the accessor would fail this pin as soon as the
6925        // two disagreed; a future accessor rebrand (a per-dialect
6926        // consumer rename, a canonical-keyword shift once the substrate
6927        // migration named in [`crate::dialeto`] completes) reaches every
6928        // consumer through one typed dispatch and this pin verifies the
6929        // display path is one of them.
6930        for d in [
6931            crate::dialeto::CaixaDialeto::Molde,
6932            crate::dialeto::CaixaDialeto::MoldePosicional,
6933        ] {
6934            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
6935            assert!(
6936                rendered.contains(d.palavra_canonica()),
6937                "Display must interpolate `dialeto.palavra_canonica()` \
6938                 verbatim — a stored snapshot would silently drift from \
6939                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
6940            );
6941            assert!(
6942                rendered.contains(d.descricao()),
6943                "Display must interpolate `dialeto.descricao()` verbatim. \
6944                 dialect: {d}, rendered: {rendered:?}"
6945            );
6946            assert!(
6947                rendered.contains(d.consumidor()),
6948                "Display must interpolate `dialeto.consumidor()` verbatim. \
6949                 dialect: {d}, rendered: {rendered:?}"
6950            );
6951        }
6952    }
6953
6954    #[test]
6955    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
6956        // The end-to-end pin the compounding closure defends: a
6957        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
6958        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
6959        // rendered Display byte-string names the Molde accessors'
6960        // returns verbatim. Any future path that constructed the variant
6961        // with a mismatched snapshot (a stored `palavra_canonica:
6962        // "defcaixa"` on a `Molde` classification) would land Display
6963        // pointing at `defcaixa` while the typed axis said `Molde` — the
6964        // exact drift the closure removes.
6965        let src = r#"
6966          (defcaixa
6967            :name "x"
6968            :kind :Biblioteca
6969            :ecosystem :rust-single-crate
6970            :package {:name "x" :version "0.1.0"})
6971        "#;
6972        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
6973        match err {
6974            LeituraError::DialetoEstrangeiro { dialeto } => {
6975                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
6976                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
6977                assert!(rendered.contains(dialeto.palavra_canonica()));
6978                assert!(rendered.contains(dialeto.consumidor()));
6979                assert!(rendered.contains(dialeto.descricao()));
6980            }
6981            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
6982        }
6983    }
6984
6985    #[test]
6986    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
6987        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
6988        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
6989        // positional-arity `defmolde` form written under a `(defcaixa …)`
6990        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
6991        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
6992        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
6993        // so no test exercised the positional-arity path through
6994        // `Caixa::from_lisp` specifically; the sibling
6995        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
6996        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
6997        // two arms route through the lifted
6998        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
6999        // typed predicate — the same predicate the pre-lift `foreign =>`
7000        // wildcard resolved to today — and this pin makes the
7001        // positional-arity arm's byte-shape at the gate explicit rather
7002        // than implied by wildcard-absorption. A future regression that
7003        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
7004        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
7005        // from the two-arity closure) would fail this pin at caixa-core
7006        // test time rather than surfacing far from the change as a
7007        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
7008        // …)` silently parsing past the derive.
7009        let src = r#"
7010          (defcaixa todoku-go
7011            :kind :Biblioteca
7012            :ecosystem :go
7013            :package {:name "todoku-go" :version "0.3.0"})
7014        "#;
7015        let err =
7016            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
7017        match err {
7018            LeituraError::DialetoEstrangeiro { dialeto } => {
7019                assert_eq!(
7020                    dialeto,
7021                    crate::dialeto::CaixaDialeto::MoldePosicional,
7022                    "DialetoEstrangeiro must carry the MoldePosicional \
7023                     variant verbatim — the positional-arity `defmolde` \
7024                     form under a `(defcaixa …)` head is the \
7025                     `MoldePosicional` arm's canonical byte-shape"
7026                );
7027                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
7028                assert!(
7029                    rendered.contains(dialeto.palavra_canonica()),
7030                    "Display must interpolate `dialeto.palavra_canonica()` \
7031                     verbatim on the MoldePosicional arm; rendered: \
7032                     {rendered:?}"
7033                );
7034                assert!(
7035                    rendered.contains(dialeto.consumidor()),
7036                    "Display must interpolate `dialeto.consumidor()` \
7037                     verbatim on the MoldePosicional arm; rendered: \
7038                     {rendered:?}"
7039                );
7040                assert!(
7041                    rendered.contains(dialeto.descricao()),
7042                    "Display must interpolate `dialeto.descricao()` \
7043                     verbatim on the MoldePosicional arm; rendered: \
7044                     {rendered:?}"
7045                );
7046            }
7047            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
7048        }
7049    }
7050
7051    #[test]
7052    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
7053        // Load-bearing byte-parity pin: for every arm in
7054        // [`crate::dialeto::CaixaDialeto::ALL`], the
7055        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
7056        // partition must agree with the lifted
7057        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
7058        // typed predicate — i.e. from_lisp raises
7059        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
7060        // `d.is_molde_family()` returns `true`, and does NOT raise
7061        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
7062        // predicate returns `false` (the arm's source falls through to
7063        // the derive — parses cleanly on
7064        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
7065        // [`LeituraError::Leitura`] on
7066        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
7067        //
7068        // Pre-lift the gate hand-rolled a three-arm match
7069        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
7070        // whose `foreign =>` wildcard expressed no compile-time link
7071        // back to the substrate primitive's arm-family; a future fifth
7072        // dialect the [`crate::dialeto`] module doc's "third dialect"
7073        // hazard actualises would fall silently onto the wildcard
7074        // regardless of whether it belonged to the `defmolde` family or
7075        // to a distinct `defcaixa`-family. Post-lift the partition
7076        // resolves through
7077        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
7078        // typed dispatch, and this pin refuses any future regression
7079        // that silently split the from_lisp partition from the typed
7080        // predicate — the two paths now migrate as one on any future
7081        // arm addition.
7082        //
7083        // Sibling in shape to the peer
7084        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
7085        // (e9d2315) that pins the same byte-parity between
7086        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
7087        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
7088        // `== "defmolde"` classifier — extends the discipline from the
7089        // two paths within the [`crate::dialeto`] primitive onto the
7090        // third external consumer of the `defmolde`-family partition
7091        // (the [`Caixa::from_lisp`] gate that raises
7092        // [`LeituraError::DialetoEstrangeiro`]).
7093        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
7094            (
7095                crate::dialeto::CaixaDialeto::Pacote,
7096                r#"
7097                  (defcaixa
7098                    :nome   "checkout"
7099                    :versao "0.1.0"
7100                    :kind   Biblioteca
7101                    :edicao "2026"
7102                    :descricao "canonical Pacote source"
7103                    :autores ()
7104                    :etiquetas ()
7105                    :deps ()
7106                    :deps-dev ()
7107                    :bibliotecas ("lib/checkout.lisp"))
7108                "#,
7109            ),
7110            (
7111                crate::dialeto::CaixaDialeto::Molde,
7112                r#"
7113                  (defcaixa
7114                    :name "base64"
7115                    :kind :Biblioteca
7116                    :ecosystem :rust-single-crate
7117                    :package {:name "base64" :version "0.22.1"}
7118                    :workflows [:auto-release])
7119                "#,
7120            ),
7121            (
7122                crate::dialeto::CaixaDialeto::MoldePosicional,
7123                r#"
7124                  (defcaixa todoku-go
7125                    :kind :Biblioteca
7126                    :ecosystem :go
7127                    :package {:name "todoku-go" :version "0.3.0"})
7128                "#,
7129            ),
7130            (
7131                crate::dialeto::CaixaDialeto::Desconhecido,
7132                r#"(defcaixa :licenca "MIT")"#,
7133            ),
7134        ];
7135
7136        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
7137        // must appear in the fixture table so the pin's arm-set stays
7138        // synchronised with the enum's arm-set. Fails at test time if a
7139        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
7140        // (with a corresponding `is_molde_family` return) forgot to
7141        // extend this fixture table with a canonical source for the new
7142        // arm — the pin cannot cover an arm it has no source for.
7143        for &expected in crate::dialeto::CaixaDialeto::ALL {
7144            assert!(
7145                fixtures.iter().any(|(d, _)| *d == expected),
7146                "fixture table must carry a canonical source for every \
7147                 CaixaDialeto arm; missing: {expected:?}"
7148            );
7149        }
7150
7151        for &(expected_dialect, src) in fixtures {
7152            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
7153                panic!(
7154                    "fixture source for {expected_dialect:?} must classify \
7155                     cleanly, got err: {err:?}"
7156                )
7157            });
7158            assert_eq!(
7159                classified, expected_dialect,
7160                "fixture source for {expected_dialect:?} must classify as \
7161                 {expected_dialect:?} (drift here defeats the byte-parity \
7162                 pin below — a source labelled for one arm but classifying \
7163                 as another would silently satisfy or violate the pin for \
7164                 the wrong reason)"
7165            );
7166
7167            let outcome = Caixa::from_lisp(src);
7168            match (expected_dialect.is_molde_family(), &outcome) {
7169                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
7170                    assert_eq!(
7171                        *dialeto, expected_dialect,
7172                        "DialetoEstrangeiro must carry the same typed arm \
7173                         the classifier returned — a drift here would let \
7174                         from_lisp raise the error while pointing at the \
7175                         wrong dialect (e.g. rejecting a \
7176                         MoldePosicional source as Molde). arm: \
7177                         {expected_dialect:?}"
7178                    );
7179                }
7180                (true, other) => panic!(
7181                    "arm {expected_dialect:?} has is_molde_family() = true \
7182                     so from_lisp must raise DialetoEstrangeiro carrying \
7183                     {expected_dialect:?}; got: {other:?}"
7184                ),
7185                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
7186                    "arm {expected_dialect:?} has is_molde_family() = false \
7187                     so from_lisp must NOT raise DialetoEstrangeiro; got \
7188                     one carrying: {dialeto:?}. This means the typed \
7189                     predicate and the from_lisp partition disagree on \
7190                     this arm — exactly the drift this pin refuses."
7191                ),
7192                (false, _) => {
7193                    // A non-molde arm's source falls through to the
7194                    // derive: Pacote sources parse to Ok(_); Desconhecido
7195                    // sources surface as LeituraError::Leitura from the
7196                    // derive's own unknown-keyword rejection. Either
7197                    // shape is acceptable here — the pin's promise is
7198                    // narrower: "no DialetoEstrangeiro on
7199                    // is_molde_family() == false".
7200                }
7201            }
7202        }
7203    }
7204
7205    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
7206
7207    #[test]
7208    fn limits_round_trip_via_json() {
7209        use crate::LimitsSpec;
7210        use std::time::Duration;
7211        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7212        c.limits = Some(LimitsSpec {
7213            memory: Some(64 * 1024 * 1024),
7214            fuel: Some(1_000_000),
7215            wall_clock: Some(Duration::from_secs(30)),
7216            cpu: Some(500),
7217        });
7218        let json = serde_json::to_string(&c).unwrap();
7219        assert!(json.contains("\"limits\""));
7220        assert!(json.contains("\"64MiB\""));
7221        assert!(json.contains("\"30s\""));
7222        assert!(json.contains("\"500m\""));
7223        let back: Caixa = serde_json::from_str(&json).unwrap();
7224        assert_eq!(c.limits, back.limits);
7225    }
7226
7227    #[test]
7228    fn behavior_round_trip_via_json() {
7229        use crate::BehaviorSpec;
7230        use std::path::PathBuf;
7231        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7232        c.behavior = Some(BehaviorSpec {
7233            on_init: Some(PathBuf::from("lib/init.lisp")),
7234            on_call: Some(PathBuf::from("lib/handlers.lisp")),
7235            ..Default::default()
7236        });
7237        let json = serde_json::to_string(&c).unwrap();
7238        let back: Caixa = serde_json::from_str(&json).unwrap();
7239        assert_eq!(c.behavior, back.behavior);
7240    }
7241
7242    #[test]
7243    fn upgrade_from_round_trip_via_json() {
7244        use crate::{UpgradeFromEntry, UpgradeInstruction};
7245        use std::path::PathBuf;
7246        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7247        c.upgrade_from = vec![UpgradeFromEntry {
7248            from: "0.1.0".into(),
7249            instructions: vec![
7250                UpgradeInstruction::LoadModule {
7251                    module: "demo".into(),
7252                },
7253                UpgradeInstruction::StateChange {
7254                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
7255                },
7256                UpgradeInstruction::SoftPurge {
7257                    module: "demo-old".into(),
7258                },
7259            ],
7260        }];
7261        let json = serde_json::to_string(&c).unwrap();
7262        let back: Caixa = serde_json::from_str(&json).unwrap();
7263        assert_eq!(c.upgrade_from, back.upgrade_from);
7264    }
7265
7266    #[test]
7267    fn supervisor_view_returns_typed_shape() {
7268        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
7269        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
7270        c.kind = CaixaKind::Supervisor;
7271        c.bibliotecas.clear();
7272        c.estrategia = Some(RestartStrategy::OneForOne);
7273        c.max_restarts = Some(5);
7274        c.restart_window = Some("60s".into());
7275        c.children = vec![ChildSpec {
7276            caixa: "worker".into(),
7277            versao: "^0.1".into(),
7278            restart: RestartPolicy::Permanent,
7279        }];
7280        let view = c.supervisor_view().expect("Supervisor kind has a view");
7281        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
7282        assert_eq!(view.max_restarts, 5);
7283        assert_eq!(
7284            view.restart_window,
7285            Some(std::time::Duration::from_secs(60))
7286        );
7287        assert_eq!(view.children.len(), 1);
7288        view.validate().unwrap();
7289    }
7290
7291    #[test]
7292    fn supervisor_view_none_for_non_supervisor_kinds() {
7293        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7294        assert!(c.supervisor_view().is_none());
7295    }
7296
7297    #[test]
7298    fn declared_mesh_slots_empty_for_bare_caixa() {
7299        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7300        assert!(c.declared_mesh_slots().is_empty());
7301    }
7302
7303    #[test]
7304    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
7305        use crate::{Entrada, Membro};
7306        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7307        // Set a non-adjacent pair (:membros + :entrada) to pin that the
7308        // canonical declaration order is preserved regardless of which
7309        // subset is populated.
7310        c.membros = vec![Membro {
7311            caixa: "a".into(),
7312            versao: "^0.1".into(),
7313        }];
7314        c.entrada = Some(Entrada {
7315            host: "x.example.com".into(),
7316            para: "a".into(),
7317            paths: vec![],
7318            port: 8080,
7319        });
7320        assert_eq!(
7321            c.declared_mesh_slots(),
7322            vec![
7323                crate::render::M3_AUTHOR_KEY_MEMBROS,
7324                crate::render::M3_AUTHOR_KEY_ENTRADA,
7325            ]
7326        );
7327    }
7328
7329    #[test]
7330    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
7331        // Scalar-value pin: the five author-facing kebab-case labels the
7332        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
7333        // mesh slot axis, one arm per typed slot. Mirrors the peer
7334        // scalar-value pin the sibling
7335        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
7336        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
7337        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
7338        // carry (f49c8b0), so both altitudes of the typed-slot algebra
7339        // (per-Servico M2 + per-Aplicacao M3) share the same
7340        // "one canonical byte-string per arm" discipline. A future
7341        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
7342        // `:politicas` → `:policies`, `:placement` → `:distribution`,
7343        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
7344        // and every consumer that reaches for the label picks it up at
7345        // build time rather than at runtime as a downstream mismatch.
7346        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
7347        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
7348        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
7349        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
7350        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
7351    }
7352
7353    #[test]
7354    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
7355        // Production-through-const pin: the five per-arm labels the
7356        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
7357        // `Vec` route through the lifted
7358        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
7359        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
7360        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
7361        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
7362        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
7363        // declaration order. A future re-order or drift at the tagger
7364        // (a rename that reaches the tagger but not the const, or vice
7365        // versa) surfaces here at build time rather than at runtime as
7366        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
7367        // `slots: <stale-kebab-case>` diagnostic far from the rename's
7368        // commit. Mirror of the peer
7369        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
7370        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
7371        // axis.
7372        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
7373        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7374        c.membros = vec![Membro {
7375            caixa: "a".into(),
7376            versao: "^0.1".into(),
7377        }];
7378        c.contratos = vec![WitContract {
7379            de: "a".into(),
7380            para: "a".into(),
7381            wit: "wasi:http/proxy".into(),
7382            endpoint: Some("/x".into()),
7383            subject: None,
7384            slot: None,
7385        }];
7386        c.politicas = Some(MeshPolicy::default());
7387        c.placement = Some(Placement {
7388            estrategia: PlacementStrategy::Replicated,
7389            clusters: vec!["rio".into()],
7390            affinity: None,
7391            shard_key: None,
7392        });
7393        c.entrada = Some(Entrada {
7394            host: "x.example.com".into(),
7395            para: "a".into(),
7396            paths: vec![],
7397            port: 8080,
7398        });
7399        assert_eq!(
7400            c.declared_mesh_slots(),
7401            vec![
7402                crate::render::M3_AUTHOR_KEY_MEMBROS,
7403                crate::render::M3_AUTHOR_KEY_CONTRATOS,
7404                crate::render::M3_AUTHOR_KEY_POLITICAS,
7405                crate::render::M3_AUTHOR_KEY_PLACEMENT,
7406                crate::render::M3_AUTHOR_KEY_ENTRADA,
7407            ]
7408        );
7409    }
7410
7411    #[test]
7412    fn declared_supervisor_slots_empty_for_bare_caixa() {
7413        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7414        assert!(c.declared_supervisor_slots().is_empty());
7415    }
7416
7417    #[test]
7418    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
7419        use crate::RestartStrategy;
7420        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7421        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
7422        // that the canonical declaration order is preserved regardless
7423        // of which subset is populated.
7424        c.estrategia = Some(RestartStrategy::OneForOne);
7425        c.restart_window = Some("60s".into());
7426        assert_eq!(
7427            c.declared_supervisor_slots(),
7428            vec![
7429                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
7430                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
7431            ]
7432        );
7433    }
7434
7435    #[test]
7436    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
7437        // Scalar-value pin: the four author-facing kebab-case labels the
7438        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
7439        // supervision-tree slot axis, one arm per typed slot. Mirrors the
7440        // peer scalar-value pins the sibling
7441        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
7442        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
7443        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
7444        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
7445        // top-level M3 slot consts carry, so all three kind-scoped
7446        // typed-slot-family author-facing-label axes route through one
7447        // canonical per-arm declaration. A future rebrand
7448        // (`:estrategia` → `:strategy` for English uniformity,
7449        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
7450        // `MaxIntensity` name, `:restart-window` → `:period` matching
7451        // OTP's `Period` name, `:children` → `:workers` matching Elixir
7452        // idiom) lands as an edit to exactly one const, and every
7453        // consumer that reaches for the label picks it up at build time
7454        // rather than at runtime as a downstream mismatch.
7455        assert_eq!(
7456            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
7457            ":estrategia"
7458        );
7459        assert_eq!(
7460            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
7461            ":max-restarts"
7462        );
7463        assert_eq!(
7464            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
7465            ":restart-window"
7466        );
7467        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
7468    }
7469
7470    #[test]
7471    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
7472        // Production-through-const pin: the four per-arm labels the
7473        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
7474        // return `Vec` route through the lifted
7475        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
7476        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
7477        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
7478        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
7479        // canonical declaration order. A future re-order or drift at the
7480        // tagger (a rename that reaches the tagger but not the const, or
7481        // vice versa) surfaces here at build time rather than at runtime
7482        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
7483        // `slots: <stale-kebab-case>` diagnostic far from the rename's
7484        // commit. Mirror of the peer
7485        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
7486        // (f49c8b0) and
7487        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
7488        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
7489        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
7490        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7491        c.estrategia = Some(RestartStrategy::OneForOne);
7492        c.max_restarts = Some(5);
7493        c.restart_window = Some("60s".into());
7494        c.children = vec![ChildSpec {
7495            caixa: "worker".into(),
7496            versao: "^0.1".into(),
7497            restart: RestartPolicy::Permanent,
7498        }];
7499        assert_eq!(
7500            c.declared_supervisor_slots(),
7501            vec![
7502                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
7503                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
7504                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
7505                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
7506            ]
7507        );
7508    }
7509
7510    #[test]
7511    fn declared_servico_slots_empty_for_bare_caixa() {
7512        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7513        assert!(c.declared_servico_slots().is_empty());
7514    }
7515
7516    #[test]
7517    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
7518        use crate::{UpgradeFromEntry, UpgradeInstruction};
7519        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7520        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
7521        // the canonical declaration order is preserved regardless of
7522        // which subset is populated.
7523        c.limits = Some(crate::LimitsSpec {
7524            fuel: Some(1_000_000),
7525            ..Default::default()
7526        });
7527        c.upgrade_from = vec![UpgradeFromEntry {
7528            from: "0.1.0".into(),
7529            instructions: vec![UpgradeInstruction::Restart],
7530        }];
7531        assert_eq!(
7532            c.declared_servico_slots(),
7533            vec![
7534                crate::render::M2_AUTHOR_KEY_LIMITS,
7535                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
7536            ]
7537        );
7538    }
7539
7540    #[test]
7541    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
7542        // Scalar-value pin: the three author-facing kebab-case labels
7543        // the `(defcaixa … :<slot> (…))` surface admits on the M2
7544        // top-level slot axis, one arm per typed slot. Mirrors the peer
7545        // scalar-value pin the sibling renderer-side
7546        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
7547        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
7548        // consts carry, so both halves of the M2 top-level slot dual
7549        // axis (author-facing kebab-case label + renderer-side
7550        // camelCase overlay-container wire key) route through one
7551        // canonical per-arm declaration. A future rebrand
7552        // (`:limits` → `:sandbox` matching Lunatic per-process
7553        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
7554        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
7555        // matching Erlang's verbatim appup name) lands as an edit to
7556        // exactly one const, and every consumer that reaches for the
7557        // label picks it up at build time rather than at runtime as a
7558        // downstream mismatch.
7559        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
7560        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
7561        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
7562    }
7563
7564    #[test]
7565    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
7566        // Production-through-const pin: the three per-arm labels the
7567        // [`Caixa::declared_servico_slots`] tagger pushes onto its
7568        // return `Vec` route through the lifted
7569        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
7570        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
7571        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
7572        // declaration order. A future re-order or drift at the tagger
7573        // (a rename that reaches the tagger but not the const, or vice
7574        // versa) surfaces here at build time rather than at runtime as
7575        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
7576        // `slots: <stale-kebab-case>` diagnostic far from the rename's
7577        // commit. Mirror of the peer
7578        // [`crate::behavior::BehaviorSpec::declared_slots`] production
7579        // tagger pin (889dc18) on the sibling per-callback axis.
7580        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
7581        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7582        c.limits = Some(crate::LimitsSpec {
7583            fuel: Some(1_000_000),
7584            ..Default::default()
7585        });
7586        c.behavior = Some(BehaviorSpec {
7587            on_init: Some(PathBuf::from("lib/init.lisp")),
7588            ..Default::default()
7589        });
7590        c.upgrade_from = vec![UpgradeFromEntry {
7591            from: "0.1.0".into(),
7592            instructions: vec![UpgradeInstruction::Restart],
7593        }];
7594        assert_eq!(
7595            c.declared_servico_slots(),
7596            vec![
7597                crate::render::M2_AUTHOR_KEY_LIMITS,
7598                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
7599                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
7600            ]
7601        );
7602    }
7603
7604    #[test]
7605    fn existing_manifests_unaffected_by_new_optional_slots() {
7606        // Regression test: a caixa.lisp authored before M2 typed slots
7607        // should still parse + serialize cleanly. The bare `defcaixa`
7608        // emitted by `Caixa::template` has none of the new fields.
7609        let src = Caixa::template("legacy");
7610        let c = Caixa::from_lisp(&src).unwrap();
7611        assert!(c.limits.is_none());
7612        assert!(c.behavior.is_none());
7613        assert!(c.upgrade_from.is_empty());
7614        assert!(c.estrategia.is_none());
7615        assert!(c.children.is_empty());
7616
7617        // And to_lisp emits a manifest with the new slots in the
7618        // empty/default state — round-trippable.
7619        let emitted = c.to_lisp();
7620        let back = Caixa::from_lisp(&emitted).unwrap();
7621        assert_eq!(c, back);
7622    }
7623
7624    #[test]
7625    fn validate_deps_accepts_canonical_caixa() {
7626        // Positive control: the bare template — zero deps, zero
7627        // deps_dev — passes the gate trivially. A future axis added to
7628        // `Dep::validate` mustn't regress an empty-deps caixa to a
7629        // build error.
7630        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7631        c.validate_deps().unwrap();
7632    }
7633
7634    #[test]
7635    fn validate_deps_rejects_invalid_versao_in_deps() {
7636        // Fail-before-pass-after pin: a malformed `:deps :versao`
7637        // surfaces at validate_deps() time, not at lacre-resolve time.
7638        // Mirrors `rejects_invalid_membro_versao_requirement` and
7639        // `validate_rejects_invalid_child_versao_requirement` on the
7640        // other two `:versao` axes.
7641        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7642        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
7643        let err = c.validate_deps().unwrap_err();
7644        assert!(
7645            matches!(
7646                err,
7647                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
7648                    if nome == "caixa-teia" && versao == "^bad-version"
7649            ),
7650            "got {err:?}"
7651        );
7652    }
7653
7654    #[test]
7655    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
7656        // Parity pin: `:deps-dev` must run through the same per-entry
7657        // validator as `:deps` — a typo in either axis surfaces the
7658        // same diagnostic. Without this leg, `:deps-dev` would be a
7659        // second-class citizen of the typed surface and an author
7660        // could land a build that passes validate_deps but fails at
7661        // `feira lock`-time when the dev-dep is resolved for a test
7662        // build.
7663        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7664        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
7665        let err = c.validate_deps().unwrap_err();
7666        assert!(
7667            matches!(
7668                err,
7669                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
7670                    if nome == "tatara-check" && versao == "^^0.1"
7671            ),
7672            "got {err:?}"
7673        );
7674    }
7675
7676    #[test]
7677    fn validate_deps_runs_deps_before_deps_dev() {
7678        // Order pin: when both lists carry typos, the `:deps`
7679        // diagnostic surfaces first. The author's mental model is
7680        // "runtime deps are load-bearing; dev deps are scaffolding";
7681        // surfacing the runtime axis first matches that hierarchy.
7682        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7683        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
7684        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
7685        let err = c.validate_deps().unwrap_err();
7686        assert!(
7687            matches!(
7688                err,
7689                crate::dep::DepError::VersaoInvalid { ref nome, .. }
7690                    if nome == "runtime-dep"
7691            ),
7692            "expected `:deps` typo to surface first, got {err:?}"
7693        );
7694    }
7695
7696    #[test]
7697    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
7698        // Positive control sweep across both lists. Pin every
7699        // canonical Cargo-shaped form so a future tightening of the
7700        // accepted set surfaces here as a test failure (parity with
7701        // `accepts_canonical_membro_versao_forms` and
7702        // `validate_accepts_canonical_child_versao_forms`).
7703        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7704        c.deps = vec![
7705            Dep::simple("caret", "^0.1"),
7706            Dep::simple("tilde", "~0.1.2"),
7707            Dep::simple("exact", "0.1.0"),
7708            Dep::simple("wildcard", "*"),
7709            Dep::simple("multi-range", ">=0.1, <2"),
7710        ];
7711        c.deps_dev = vec![
7712            Dep::simple("dev-caret", "^0.1"),
7713            Dep::simple("dev-wildcard", "*"),
7714        ];
7715        c.validate_deps().unwrap();
7716    }
7717
7718    #[test]
7719    fn validate_deps_diagnostic_carries_offending_dep() {
7720        // Diagnostic-shape pin: the error names the offending entry's
7721        // `:nome` + `:versao` verbatim and carries a non-empty
7722        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
7723        // run can render the diagnostic without re-parsing.
7724        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7725        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
7726        let err = c.validate_deps().unwrap_err();
7727        let crate::dep::DepError::VersaoInvalid {
7728            nome,
7729            versao,
7730            reason,
7731        } = err
7732        else {
7733            panic!("expected VersaoInvalid, got other variant");
7734        };
7735        assert_eq!(nome, "caixa-teia");
7736        assert_eq!(versao, "not-a-req");
7737        assert!(
7738            !reason.is_empty(),
7739            "VersaoInvalid `reason` must carry the parser's wording verbatim"
7740        );
7741    }
7742
7743    #[test]
7744    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
7745        // Cross-axis pin: `validate_deps` walks both :deps and
7746        // :deps-dev through `Dep::validate`, and the new fonte gate
7747        // (`:tag` + `:branch` both set — the canonical "pin drift"
7748        // footgun) must surface from the :deps-dev arm with the
7749        // offending entry's :nome named. Pin the :deps-dev arm
7750        // explicitly so a future shortcut that only walks :deps
7751        // surfaces here as a regression.
7752        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7753        c.deps_dev = vec![Dep {
7754            nome: "dev-only".into(),
7755            versao: "^0.1".into(),
7756            fonte: Some(crate::DepSource::Git {
7757                repo: "github:p/x".into(),
7758                tag: Some("v1".into()),
7759                rev: None,
7760                branch: Some("main".into()),
7761            }),
7762            opcional: false,
7763            caracteristicas: vec![],
7764        }];
7765        let err = c.validate_deps().unwrap_err();
7766        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
7767            panic!("expected FontePinAmbiguous from :deps-dev walk");
7768        };
7769        assert_eq!(nome, "dev-only");
7770        assert!(pins.contains(":tag") && pins.contains(":branch"));
7771    }
7772
7773    #[test]
7774    fn validate_deps_rejects_empty_repo_in_deps() {
7775        // Parity pin on the :deps arm: an empty :repo on the runtime
7776        // deps list surfaces the same FonteRepoEmpty diagnostic the
7777        // dep.rs per-entry tests pin, naming the offending entry.
7778        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7779        c.deps = vec![Dep {
7780            nome: "runtime".into(),
7781            versao: "^0.1".into(),
7782            fonte: Some(crate::DepSource::Git {
7783                repo: String::new(),
7784                tag: Some("v1".into()),
7785                rev: None,
7786                branch: None,
7787            }),
7788            opcional: false,
7789            caracteristicas: vec![],
7790        }];
7791        let err = c.validate_deps().unwrap_err();
7792        assert!(
7793            matches!(
7794                err,
7795                crate::dep::DepError::FonteRepoEmpty { ref nome }
7796                    if nome == "runtime"
7797            ),
7798            "got {err:?}"
7799        );
7800    }
7801
7802    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
7803
7804    #[test]
7805    fn validate_deps_rejects_duplicate_nome_in_deps() {
7806        // Fail-before-pass-after pin: two `:deps` entries naming the same
7807        // caixa carry two `:versao` / `:fonte` / feature triples that the
7808        // caixa-resolver's lacre pipeline collapses (the second silently
7809        // overwrites the first at `concrete_versao`-resolve time). The
7810        // gate surfaces the duplicate at validate-time, naming the
7811        // offending caixa + the list, before the resolver-side silent
7812        // drop. Mirrors the peer typed-graph duplicate gates
7813        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
7814        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7815        c.deps = vec![
7816            Dep::simple("caixa-teia", "^0.1"),
7817            Dep::simple("caixa-teia", "^0.2"),
7818        ];
7819        let err = c.validate_deps().unwrap_err();
7820        assert!(
7821            matches!(
7822                err,
7823                crate::dep::DepError::DuplicateNome { ref nome, list }
7824                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
7825            ),
7826            "got {err:?}"
7827        );
7828    }
7829
7830    #[test]
7831    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
7832        // Parity pin: `:deps-dev` runs through the same per-list
7833        // duplicate check as `:deps` — neither axis is a second-class
7834        // citizen of the set-not-multiset discipline.
7835        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7836        c.deps_dev = vec![
7837            Dep::simple("tatara-check", "*"),
7838            Dep::simple("tatara-check", "^0.1"),
7839        ];
7840        let err = c.validate_deps().unwrap_err();
7841        assert!(
7842            matches!(
7843                err,
7844                crate::dep::DepError::DuplicateNome { ref nome, list }
7845                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
7846            ),
7847            "got {err:?}"
7848        );
7849    }
7850
7851    #[test]
7852    fn validate_deps_accepts_cross_list_same_nome() {
7853        // The Cargo `[dependencies]` + `[dev-dependencies]` override
7854        // convention is preserved: a name appearing in *both* lists is
7855        // valid (the dev-pin overrides at test/dev time). Only
7856        // within-list duplicates are structurally incoherent — pin the
7857        // permissive cross-list semantics so a future shortcut that
7858        // collapses the two seen-sets into one surfaces here as a test
7859        // failure.
7860        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7861        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
7862        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
7863        c.validate_deps().unwrap();
7864    }
7865
7866    #[test]
7867    fn validate_deps_accepts_distinct_nome_in_both_lists() {
7868        // Positive control: distinct names within each list pass — the
7869        // gate's identity element on the canonical authoring shape.
7870        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7871        c.deps = vec![
7872            Dep::simple("caixa-teia", "^0.1"),
7873            Dep::simple("pleme-mesh", "*"),
7874        ];
7875        c.deps_dev = vec![
7876            Dep::simple("tatara-check", "*"),
7877            Dep::simple("dev-shim", "^0.1"),
7878        ];
7879        c.validate_deps().unwrap();
7880    }
7881
7882    #[test]
7883    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
7884        // Diagnostic-precedence pin: a malformed `:versao` on the
7885        // duplicating entry surfaces its narrower `VersaoInvalid`
7886        // diagnostic first, before the cross-entry duplicate gate fires
7887        // — the canonical "per-entry shape before cross-entry uniqueness"
7888        // precedence every peer set-not-multiset gate establishes
7889        // (`*_invalid_fires_before_duplicate_check` pins on
7890        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
7891        // `validate_upgrade_from`).
7892        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7893        c.deps = vec![
7894            Dep::simple("caixa-teia", "^0.1"),
7895            Dep::simple("caixa-teia", "^bad-version"),
7896        ];
7897        let err = c.validate_deps().unwrap_err();
7898        assert!(
7899            matches!(
7900                err,
7901                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
7902                    if nome == "caixa-teia" && versao == "^bad-version"
7903            ),
7904            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
7905        );
7906    }
7907
7908    #[test]
7909    fn validate_deps_duplicate_diagnostic_names_first_collision() {
7910        // First-collision determinism pin: with three entries naming the
7911        // same caixa, the first colliding pair surfaces — not the last.
7912        // Mirrors the peer first-collision posture on every
7913        // duplicate-target gate
7914        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
7915        // — the second entry is the first collision; this gate uses the
7916        // same shape: the second entry's `:nome` lands in the diagnostic
7917        // because `seen.insert(first.nome)` already populated the set).
7918        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7919        c.deps = vec![
7920            Dep::simple("caixa-teia", "^0.1"),
7921            Dep::simple("caixa-teia", "^0.2"),
7922            Dep::simple("caixa-teia", "^0.3"),
7923        ];
7924        let err = c.validate_deps().unwrap_err();
7925        // The diagnostic carries the offending caixa name; the
7926        // implementation surfaces on the *second* entry (the first
7927        // collision), so the test pins the `:nome` value.
7928        assert!(
7929            matches!(
7930                err,
7931                crate::dep::DepError::DuplicateNome { ref nome, list }
7932                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
7933            ),
7934            "got {err:?}"
7935        );
7936    }
7937
7938    #[test]
7939    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
7940        // Cross-list precedence pin: when both lists carry duplicates,
7941        // the `:deps` diagnostic surfaces first — same author-mental-
7942        // model ordering the `validate_deps_runs_deps_before_deps_dev`
7943        // pin establishes for malformed `:versao` (runtime axis before
7944        // dev axis).
7945        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7946        c.deps = vec![
7947            Dep::simple("runtime-dep", "^0.1"),
7948            Dep::simple("runtime-dep", "^0.2"),
7949        ];
7950        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
7951        let err = c.validate_deps().unwrap_err();
7952        assert!(
7953            matches!(
7954                err,
7955                crate::dep::DepError::DuplicateNome { ref nome, list }
7956                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
7957            ),
7958            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
7959        );
7960    }
7961
7962    #[test]
7963    fn validate_deps_empty_lists_pass_duplicate_gate() {
7964        // Empty-set identity pin: the bare template (zero deps, zero
7965        // deps_dev) passes the duplicate gate as the gate's identity
7966        // element. A future tighten that conflates "empty" with
7967        // "missing" would regress this baseline.
7968        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7969        c.validate_deps().unwrap();
7970    }
7971
7972    #[test]
7973    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
7974        // Diagnostic-shape pin: the `list:` field tags which list the
7975        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
7976        // `feira lint` run can route the author to the right block in
7977        // their caixa.lisp without re-deriving the list from context.
7978        // Same self-locating shape every peer per-axis diagnostic
7979        // already exposes.
7980        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7981        c.deps_dev = vec![
7982            Dep::simple("dev-thing", "*"),
7983            Dep::simple("dev-thing", "^0.1"),
7984        ];
7985        let err = c.validate_deps().unwrap_err();
7986        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
7987            panic!("expected DuplicateNome from :deps-dev walk");
7988        };
7989        assert_eq!(nome, "dev-thing");
7990        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
7991    }
7992
7993    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
7994
7995    #[test]
7996    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
7997        // Thread-through pin on `:deps`: the per-entry
7998        // `Dep::validate_caracteristicas` gate fires inside
7999        // `Caixa::validate_deps`'s linear walk, so a malformed feature
8000        // list on any `:deps` entry surfaces as a `DepError` from
8001        // `validate_deps` — the same reachability shape every per-entry
8002        // `Dep::validate` arm threads through. Without this pin a future
8003        // shortcut that skips the per-entry `Dep::validate` call on the
8004        // cross-entry-uniqueness path would mask the within-entry
8005        // `:caracteristicas` gates.
8006        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8007        c.deps = vec![Dep {
8008            nome: "caixa-teia".into(),
8009            versao: "^0.1".into(),
8010            fonte: None,
8011            opcional: false,
8012            caracteristicas: vec!["http".into(), "http".into()],
8013        }];
8014        let err = c.validate_deps().unwrap_err();
8015        let crate::dep::DepError::CaracteristicaDuplicate {
8016            nome,
8017            caracteristica,
8018        } = err
8019        else {
8020            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
8021        };
8022        assert_eq!(nome, "caixa-teia");
8023        assert_eq!(caracteristica, "http");
8024    }
8025
8026    #[test]
8027    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
8028        // Peer thread-through pin on `:deps-dev`: same reachability as
8029        // the `:deps` arm above, on the dev-only authoring axis. Pins
8030        // that the `validate_deps` walk visits both lists' per-entry
8031        // gates uniformly. The empty-feature arm carries here so both
8032        // new `:caracteristicas` arms are surfaced via at least one
8033        // `validate_deps` thread-through.
8034        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8035        c.deps_dev = vec![Dep {
8036            nome: "caixa-teia".into(),
8037            versao: "^0.1".into(),
8038            fonte: None,
8039            opcional: false,
8040            caracteristicas: vec![String::new()],
8041        }];
8042        let err = c.validate_deps().unwrap_err();
8043        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
8044            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
8045        };
8046        assert_eq!(nome, "caixa-teia");
8047    }
8048
8049    #[test]
8050    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
8051        // Thread-through pin on `:deps`: the per-entry
8052        // `Dep::validate_caracteristicas` value-shape gate (lifted via
8053        // `crate::render::is_cargo_feature_name`) fires inside
8054        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
8055        // a structurally invalid feature name on any `:deps` entry
8056        // surfaces as `DepError::CaracteristicaInvalid` from
8057        // `validate_deps` — the same reachability shape every per-entry
8058        // `Dep::validate` arm threads through. Without this pin a
8059        // future shortcut that skips the per-entry `Dep::validate` call
8060        // on the cross-entry-uniqueness path would mask the within-
8061        // entry `:caracteristicas` value-shape gate.
8062        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8063        c.deps = vec![Dep {
8064            nome: "caixa-teia".into(),
8065            versao: "^0.1".into(),
8066            fonte: None,
8067            opcional: false,
8068            caracteristicas: vec!["+http".into()],
8069        }];
8070        let err = c.validate_deps().unwrap_err();
8071        let crate::dep::DepError::CaracteristicaInvalid {
8072            nome,
8073            caracteristica,
8074            ..
8075        } = err
8076        else {
8077            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
8078        };
8079        assert_eq!(nome, "caixa-teia");
8080        assert_eq!(caracteristica, "+http");
8081    }
8082
8083    #[test]
8084    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
8085        // Peer thread-through pin on `:deps-dev`: same reachability as
8086        // the `:deps` arm above, on the dev-only authoring axis. The
8087        // `http/json` shape carries here so the segment-separator
8088        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
8089        // confusion footgun) is surfaced via the cross-entry walk too —
8090        // pinning that the `:deps-dev` list visits the same per-entry
8091        // value-shape gate as the `:deps` list.
8092        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8093        c.deps_dev = vec![Dep {
8094            nome: "caixa-teia".into(),
8095            versao: "^0.1".into(),
8096            fonte: None,
8097            opcional: false,
8098            caracteristicas: vec!["http/json".into()],
8099        }];
8100        let err = c.validate_deps().unwrap_err();
8101        let crate::dep::DepError::CaracteristicaInvalid {
8102            nome,
8103            caracteristica,
8104            ..
8105        } = err
8106        else {
8107            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
8108        };
8109        assert_eq!(nome, "caixa-teia");
8110        assert_eq!(caracteristica, "http/json");
8111    }
8112
8113    #[test]
8114    fn to_lisp_preserves_deps() {
8115        let src = r#"
8116(defcaixa
8117  :nome "x"
8118  :versao "0.1.0"
8119  :kind Biblioteca
8120  :deps ((:nome "a" :versao "^0.1")
8121         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
8122"#;
8123        let c1 = Caixa::from_lisp(src).unwrap();
8124        let emitted = c1.to_lisp();
8125        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
8126        assert_eq!(c1.deps, c2.deps);
8127    }
8128
8129    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
8130
8131    fn caixa_with_nome(nome: &str) -> Caixa {
8132        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
8133        c.nome = nome.to_string();
8134        c
8135    }
8136
8137    #[test]
8138    fn validate_nome_accepts_canonical_template() {
8139        // Positive control: the bare `feira init`-style template's
8140        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
8141        // not regress this baseline shape. A future tightening of the
8142        // accepted set surfaces here as a test failure first.
8143        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8144        c.validate_nome().unwrap();
8145    }
8146
8147    #[test]
8148    fn validate_nome_accepts_canonical_forms() {
8149        // Positive-set sweep: each realistic caixa-name shape the K8s
8150        // apiserver accepts as a `metadata.name` label must pass —
8151        // single-word, hyphen-joined, version-suffixed, single-char,
8152        // two-char, digit-start (DNS-1123 allows this; the stricter
8153        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
8154        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
8155        // the peer member-name axis.
8156        for nome in [
8157            "checkout",
8158            "cart-v2",
8159            "a",
8160            "db",
8161            "3rd-party-shim",
8162            "payment-retry",
8163            "0",
8164        ] {
8165            caixa_with_nome(nome)
8166                .validate_nome()
8167                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
8168        }
8169    }
8170
8171    #[test]
8172    fn validate_nome_rejects_empty() {
8173        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
8174        // an empty `:nome` (the derive macro stores the raw String);
8175        // the gate's empty arm names the offending axis with a narrower
8176        // diagnostic than the `NomeInvalid` parse arm would emit.
8177        let c = caixa_with_nome("");
8178        let err = c.validate_nome().unwrap_err();
8179        assert_eq!(err, ManifestError::NomeEmpty);
8180    }
8181
8182    #[test]
8183    fn validate_nome_rejects_uppercase() {
8184        // The canonical "I copied the TitleCase display name verbatim"
8185        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
8186        // admission on every derived artifact (Helm chart, ComputeUnit,
8187        // CNP, HTTPRoute, label values); the gate moves the diagnostic
8188        // to the source `caixa.lisp` and the reason suggests the
8189        // lowercased fix verbatim.
8190        let c = caixa_with_nome("MyApp");
8191        let err = c.validate_nome().unwrap_err();
8192        let ManifestError::NomeInvalid { nome, reason } = err else {
8193            panic!("expected NomeInvalid for uppercase :nome");
8194        };
8195        assert_eq!(nome, "MyApp");
8196        assert!(
8197            reason.contains("uppercase") && reason.contains("myapp"),
8198            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
8199        );
8200    }
8201
8202    #[test]
8203    fn validate_nome_rejects_underscore() {
8204        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
8205        // `_`; the apiserver rejects on admission across every derived
8206        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
8207        // and `:children :caixa` (31bfa43).
8208        let c = caixa_with_nome("my_app");
8209        let err = c.validate_nome().unwrap_err();
8210        assert!(
8211            matches!(
8212                err,
8213                ManifestError::NomeInvalid { ref nome, ref reason }
8214                    if nome == "my_app" && reason.contains('_')
8215            ),
8216            "got {err:?}"
8217        );
8218    }
8219
8220    #[test]
8221    fn validate_nome_rejects_dot() {
8222        // A `:nome` is a single DNS-1123 label, not a subdomain. The
8223        // "I want to namespace with `.`" footgun the gate redirects to
8224        // `-` via the shared predicate's reason wording.
8225        let c = caixa_with_nome("team.app");
8226        let err = c.validate_nome().unwrap_err();
8227        assert!(
8228            matches!(
8229                err,
8230                ManifestError::NomeInvalid { ref nome, ref reason }
8231                    if nome == "team.app" && reason.contains('.')
8232            ),
8233            "got {err:?}"
8234        );
8235    }
8236
8237    #[test]
8238    fn validate_nome_rejects_leading_hyphen() {
8239        // DNS-1123 boundary rule: the label must start with an ASCII
8240        // alphanumeric. Pin the leading-`-` arm explicitly.
8241        let c = caixa_with_nome("-app");
8242        let err = c.validate_nome().unwrap_err();
8243        assert!(
8244            matches!(
8245                err,
8246                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
8247            ),
8248            "got {err:?}"
8249        );
8250    }
8251
8252    #[test]
8253    fn validate_nome_rejects_trailing_hyphen() {
8254        // Symmetric arm of the boundary rule, pinned separately so a
8255        // future relaxation that only checks the leading position
8256        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
8257        // and `_with_trailing_hyphen` on the supervisor / aplicacao
8258        // axes.
8259        let c = caixa_with_nome("app-");
8260        let err = c.validate_nome().unwrap_err();
8261        assert!(
8262            matches!(
8263                err,
8264                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
8265            ),
8266            "got {err:?}"
8267        );
8268    }
8269
8270    #[test]
8271    fn validate_nome_rejects_unicode() {
8272        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
8273        // bytes are rejected by the K8s apiserver on every name axis.
8274        let c = caixa_with_nome("café");
8275        let err = c.validate_nome().unwrap_err();
8276        assert!(
8277            matches!(
8278                err,
8279                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
8280            ),
8281            "got {err:?}"
8282        );
8283    }
8284
8285    #[test]
8286    fn validate_nome_rejects_whitespace() {
8287        // The paste-from-sketch / paste-from-spec footgun. Internal
8288        // whitespace is rejected by every K8s name axis.
8289        let c = caixa_with_nome("my app");
8290        let err = c.validate_nome().unwrap_err();
8291        assert!(
8292            matches!(
8293                err,
8294                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
8295            ),
8296            "got {err:?}"
8297        );
8298    }
8299
8300    #[test]
8301    fn validate_nome_rejects_too_long() {
8302        // 64-byte boundary pin: the K8s apiserver rejects any
8303        // `metadata.name` over 63 bytes at admission; the diagnostic
8304        // names both the 63-byte cap and the actual length so the
8305        // author can shorten in one edit. Mirrors `_too_long` on the
8306        // peer member-/cluster-/child-name axes.
8307        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
8308        let c = caixa_with_nome(&over);
8309        let err = c.validate_nome().unwrap_err();
8310        let ManifestError::NomeInvalid { nome, reason } = err else {
8311            panic!("expected NomeInvalid for over-cap :nome");
8312        };
8313        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
8314        assert!(
8315            reason.contains("63") && reason.contains("64"),
8316            "diagnostic must name the cap + actual length, got {reason:?}"
8317        );
8318    }
8319
8320    #[test]
8321    fn nome_max_length_validates() {
8322        // The 63-byte cap exactly — the boundary-accepting case pinned
8323        // alongside `validate_nome_rejects_too_long` so a future cap
8324        // shift surfaces both arms simultaneously. Mirrors
8325        // `membro_caixa_max_length_validates`,
8326        // `placement_cluster_max_length_validates`,
8327        // `child_caixa_max_length_validates`.
8328        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
8329        caixa_with_nome(&at_cap).validate_nome().unwrap();
8330    }
8331
8332    #[test]
8333    fn nome_empty_takes_precedence_over_invalid() {
8334        // Order pin: the empty arm fires before the predicate is
8335        // consulted. Empty < invalid in self-locating-ness — the
8336        // narrower `NomeEmpty` diagnostic doesn't carry a useless
8337        // `nome: ""` reference into the parser-shaped reason. Mirrors
8338        // `membro_caixa_empty_takes_precedence_over_invalid` on the
8339        // peer axis (3f9d7a0).
8340        let c = caixa_with_nome("");
8341        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
8342    }
8343
8344    #[test]
8345    fn nome_invalid_diagnostic_carries_offending_nome() {
8346        // Diagnostic-shape pin: the error names the offending `:nome`
8347        // verbatim with a non-empty parser-shaped reason, so a `feira
8348        // lint` run can render the diagnostic without re-parsing.
8349        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
8350        let c = caixa_with_nome("MyApp");
8351        let err = c.validate_nome().unwrap_err();
8352        let ManifestError::NomeInvalid { nome, reason } = err else {
8353            panic!("expected NomeInvalid variant");
8354        };
8355        assert_eq!(nome, "MyApp");
8356        assert!(
8357            !reason.is_empty(),
8358            "NomeInvalid `reason` must carry the predicate's wording verbatim"
8359        );
8360    }
8361
8362    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
8363    //
8364    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
8365    // via DNS-1123; this second-axis gate caps the joint
8366    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
8367    // canonical [`crate::lareira_chart_name`] helper's doc comment
8368    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
8369    // "the M4 admission webhook will pin the joint-length invariant
8370    // when it lands". These tests pin it at the manifest-validate
8371    // layer instead, fail-before-pass-after on the 56-byte boundary.
8372
8373    #[test]
8374    fn validate_nome_chart_name_budget_accepts_canonical_template() {
8375        // Positive control: the bare `feira init`-style template's
8376        // `:nome` ("demo") sits far below the cap; the gate must not
8377        // regress this baseline. Same shape every peer
8378        // value-shape-gate baseline pin uses.
8379        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8380        c.validate_nome_chart_name_budget().unwrap();
8381    }
8382
8383    #[test]
8384    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
8385        // Positive-set sweep across the canonical author surface every
8386        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
8387        // `worker`, the `checkout-aplicacao` example members, the
8388        // `example-attest` caixa-tatara fixture). Every value sits
8389        // far below the 55-byte per-`:nome` budget. Same shape every
8390        // peer per-axis baseline pin uses.
8391        for nome in [
8392            "hello-rio",
8393            "cart",
8394            "checkout",
8395            "worker",
8396            "example-attest",
8397            "demo",
8398            "a",
8399        ] {
8400            caixa_with_nome(nome)
8401                .validate_nome_chart_name_budget()
8402                .unwrap_or_else(|e| {
8403                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
8404                });
8405        }
8406    }
8407
8408    #[test]
8409    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
8410        // Boundary-accepting case at the 55-byte per-`:nome` budget —
8411        // the joint chart name is exactly 63 bytes, the DNS-1123 label
8412        // cap. Pinned alongside the rejecting-arm test so a future cap
8413        // shift surfaces both arms simultaneously. Mirrors
8414        // `nome_max_length_validates` on the peer bare-`:nome` axis.
8415        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
8416        caixa_with_nome(&at_cap)
8417            .validate_nome_chart_name_budget()
8418            .unwrap();
8419    }
8420
8421    #[test]
8422    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
8423        // Fail-before-pass-after pin on the 56-byte boundary: the
8424        // smallest `:nome` length that overflows the joint chart-name
8425        // cap. The inner [`is_dns_1123_label`] gate
8426        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
8427        // this gate it silently passed the manifest-validate cascade
8428        // and surfaced as a `helm lint` / apiserver rejection on the
8429        // rendered chart name far from the source `caixa.lisp`, with
8430        // no field naming the overflow. With this gate the diagnostic
8431        // names the offending `:nome` verbatim alongside the rendered
8432        // chart name and the budget, so the author can shorten in one
8433        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
8434        // bare-`:nome` axis.
8435        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8436        let c = caixa_with_nome(&over);
8437        let err = c.validate_nome_chart_name_budget().unwrap_err();
8438        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
8439            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
8440        };
8441        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8442        assert_eq!(nome, over);
8443        assert!(
8444            reason.contains("63") && reason.contains("64") && reason.contains("55"),
8445            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
8446             and the per-`:nome` budget (55), got {reason:?}"
8447        );
8448    }
8449
8450    #[test]
8451    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
8452        // The 63-byte `:nome` boundary — passes the bare-`:nome`
8453        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
8454        // joint chart name that overflows the DNS-1123 label cap
8455        // structurally. The most stringent fail-before-pass-after
8456        // surface: every `:nome` in the 56..=63-byte range passed the
8457        // prior cascade and broke at admission.
8458        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
8459        let c = caixa_with_nome(&bare_max);
8460        // The bare-`:nome` gate accepts the 63-byte length.
8461        c.validate_nome().unwrap();
8462        // The new joint-length gate rejects it.
8463        let err = c.validate_nome_chart_name_budget().unwrap_err();
8464        assert!(
8465            matches!(
8466                err,
8467                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
8468                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
8469            ),
8470            "got {err:?}"
8471        );
8472    }
8473
8474    #[test]
8475    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
8476        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
8477        // name appears verbatim in the diagnostic so the author sees
8478        // exactly the string the apiserver / `helm lint` would have
8479        // rejected — no re-derivation required to grep the source.
8480        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
8481        // on the bare-`:nome` axis.
8482        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
8483        let c = caixa_with_nome(&over);
8484        let err = c.validate_nome_chart_name_budget().unwrap_err();
8485        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
8486            panic!("expected NomeChartNameBudgetExceeded variant");
8487        };
8488        assert_eq!(nome, over);
8489        let expected_chart = crate::lareira_chart_name(&over);
8490        assert!(
8491            reason.contains(&expected_chart),
8492            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
8493             got {reason:?}"
8494        );
8495        assert!(
8496            reason.contains("lareira-"),
8497            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
8498        );
8499    }
8500
8501    #[test]
8502    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
8503        // Order pin on the layout cascade: the narrower
8504        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
8505        // joint-length budget. A structurally-malformed `:nome` (here:
8506        // uppercase) surfaces its specific shape error rather than
8507        // the chart-name-budget error, even when the joint length
8508        // would also overflow — the narrower diagnostic is more
8509        // self-locating. Mirrors the cascade-precedence pins peer
8510        // gates already use (e.g. `EntradaParaEmpty` before
8511        // `EntradaParaInvalid`).
8512        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8513        let c = caixa_with_nome(&over);
8514        // The bare-shape gate fires first.
8515        let err = c.validate_nome().unwrap_err();
8516        assert!(
8517            matches!(err, ManifestError::NomeInvalid { .. }),
8518            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
8519        );
8520        // And the layout verify cascade surfaces that diagnostic, not
8521        // the budget arm. Inject a path-exists oracle so the cascade
8522        // gets past the manifest-presence check and into the
8523        // value-shape gates.
8524        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
8525        let err = crate::LayoutInvariants::verify(
8526            &layout,
8527            &c,
8528            std::path::Path::new("/tmp/caixa-test-fake-root"),
8529        )
8530        .unwrap_err();
8531        let issue = err.to_string();
8532        assert!(
8533            issue.contains("DNS-1123") || issue.contains("uppercase"),
8534            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
8535             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
8536        );
8537    }
8538
8539    #[test]
8540    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
8541        // Cross-axis envelope pin: the layout cascade wraps both
8542        // bare-`:nome` and joint-length-`:nome` failures through the
8543        // same [`LayoutError::NomeViolation`] envelope, since both
8544        // arms are on the `:nome` axis. The user's diagnostic stays
8545        // self-locating ("which axis"), and a future consumer that
8546        // dispatches on the layout-error variant (e.g. a `feira lint`
8547        // exit-code mapping) sees a single per-axis envelope. The
8548        // wrapped `issue:` carries the full inner diagnostic.
8549        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8550        let c = caixa_with_nome(&over);
8551        // The bare-shape gate accepts.
8552        c.validate_nome().unwrap();
8553        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
8554        let err = crate::LayoutInvariants::verify(
8555            &layout,
8556            &c,
8557            std::path::Path::new("/tmp/caixa-test-fake-root"),
8558        )
8559        .unwrap_err();
8560        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
8561            panic!("expected LayoutError::NomeViolation, got {err:?}");
8562        };
8563        assert_eq!(caixa, over);
8564        assert!(
8565            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
8566            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
8567        );
8568    }
8569
8570    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
8571
8572    fn caixa_with_versao(versao: &str) -> Caixa {
8573        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8574        c.versao = versao.to_string();
8575        c
8576    }
8577
8578    #[test]
8579    fn validate_versao_accepts_canonical_template() {
8580        // Positive control: the bare `feira init`-style template's
8581        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
8582        // must not regress this baseline shape. A future tightening of
8583        // the accepted set surfaces here as a test failure first.
8584        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8585        c.validate_versao().unwrap();
8586    }
8587
8588    #[test]
8589    fn validate_versao_accepts_canonical_forms() {
8590        // Positive-set sweep: each realistic SemVer-2 shape the
8591        // substrate's downstream consumers accept must pass — bare
8592        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
8593        // build metadata (`+build.42`), the combined form, and the
8594        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
8595        // the peer `:nome` axis (6c992f8).
8596        for versao in [
8597            "0.1.0",
8598            "0.0.0",
8599            "1.0.0",
8600            "0.2.0-rc.1",
8601            "1.0.0-alpha.0",
8602            "1.0.0+build.42",
8603            "1.0.0-rc.1+build.42",
8604            "10.20.30",
8605        ] {
8606            caixa_with_versao(versao)
8607                .validate_versao()
8608                .unwrap_or_else(|e| {
8609                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
8610                });
8611        }
8612    }
8613
8614    #[test]
8615    fn validate_versao_rejects_empty() {
8616        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
8617        // an empty `:versao` (the derive macro stores the raw String);
8618        // the gate's empty arm names the offending axis with a narrower
8619        // diagnostic than the `VersaoInvalid` parse arm would emit.
8620        // Mirrors `validate_nome_rejects_empty` (6c992f8).
8621        let c = caixa_with_versao("");
8622        let err = c.validate_versao().unwrap_err();
8623        assert_eq!(err, ManifestError::VersaoEmpty);
8624    }
8625
8626    #[test]
8627    fn validate_versao_rejects_git_tag_shape() {
8628        // The canonical "I copied the git tag verbatim" footgun —
8629        // `feira publish` *emits* `v<versao>` git tags, so a leaked
8630        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
8631        // shift every downstream consumer's version axis. `semver`
8632        // rejects the leading `v` at parse time; the gate moves the
8633        // diagnostic to the source `caixa.lisp`.
8634        let c = caixa_with_versao("v0.1.0");
8635        let err = c.validate_versao().unwrap_err();
8636        let ManifestError::VersaoInvalid { versao, reason } = err else {
8637            panic!("expected VersaoInvalid for git-tag-shape :versao");
8638        };
8639        assert_eq!(versao, "v0.1.0");
8640        assert!(
8641            !reason.is_empty(),
8642            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
8643        );
8644    }
8645
8646    #[test]
8647    fn validate_versao_rejects_missing_patch() {
8648        // The canonical "I shortened it" footgun — SemVer-2 requires
8649        // three parts. Cargo's `version =` field accepts the shortened
8650        // form as a requirement, conflating the two leaks across the
8651        // typed `:deps :versao` vs top-level `:versao` axes; the gate
8652        // pins the top-level axis to the strict three-part shape.
8653        let c = caixa_with_versao("0.1");
8654        let err = c.validate_versao().unwrap_err();
8655        assert!(
8656            matches!(
8657                err,
8658                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
8659            ),
8660            "got {err:?}"
8661        );
8662    }
8663
8664    #[test]
8665    fn validate_versao_rejects_requirement_shape() {
8666        // The canonical "I leaked a requirement into a version" footgun —
8667        // the typed `:deps :versao` / `:membros :versao` axes accept
8668        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
8669        // concrete `Version`. Without this gate the two typed surfaces
8670        // would silently overlap, and a top-level `^0.1` would surface
8671        // at `helm install` time as a Chart.yaml version rejection far
8672        // from the source `caixa.lisp`.
8673        let c = caixa_with_versao("^0.1");
8674        let err = c.validate_versao().unwrap_err();
8675        assert!(
8676            matches!(
8677                err,
8678                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
8679            ),
8680            "got {err:?}"
8681        );
8682    }
8683
8684    #[test]
8685    fn validate_versao_rejects_docker_tag_shape() {
8686        // The "I confused it with a docker tag" footgun — `latest`,
8687        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
8688        // SemVer rejects at parse time; the gate moves the diagnostic
8689        // to the source `caixa.lisp`.
8690        for bad in ["latest", "main", "stable"] {
8691            let c = caixa_with_versao(bad);
8692            let err = c.validate_versao().unwrap_err();
8693            assert!(
8694                matches!(
8695                    err,
8696                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
8697                ),
8698                "got {err:?} for {bad:?}"
8699            );
8700        }
8701    }
8702
8703    #[test]
8704    fn validate_versao_rejects_four_part_form() {
8705        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
8706        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
8707        // semver crate rejects the extra `.0` at parse time.
8708        let c = caixa_with_versao("0.1.0.0");
8709        let err = c.validate_versao().unwrap_err();
8710        assert!(
8711            matches!(
8712                err,
8713                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
8714            ),
8715            "got {err:?}"
8716        );
8717    }
8718
8719    #[test]
8720    fn versao_empty_takes_precedence_over_invalid() {
8721        // Order pin: the empty arm fires before the parser is consulted.
8722        // Empty < invalid in self-locating-ness — the narrower
8723        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
8724        // reference into the parser-shaped reason. Mirrors
8725        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
8726        // peer axis.
8727        let c = caixa_with_versao("");
8728        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
8729    }
8730
8731    #[test]
8732    fn versao_invalid_diagnostic_carries_offending_versao() {
8733        // Diagnostic-shape pin: the error names the offending `:versao`
8734        // verbatim with a non-empty parser-shaped reason, so a `feira
8735        // lint` run can render the diagnostic without re-parsing.
8736        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
8737        let c = caixa_with_versao("v0.1.0");
8738        let err = c.validate_versao().unwrap_err();
8739        let ManifestError::VersaoInvalid { versao, reason } = err else {
8740            panic!("expected VersaoInvalid variant");
8741        };
8742        assert_eq!(versao, "v0.1.0");
8743        assert!(
8744            !reason.is_empty(),
8745            "VersaoInvalid `reason` must carry the parser's wording verbatim"
8746        );
8747    }
8748
8749    #[test]
8750    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
8751        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
8752        // for `:upgrade-from :from` must also pass `validate_versao` —
8753        // the two `:versao`-typed surfaces (top-level `:versao`,
8754        // `:upgrade-from :from`) consume the *same* `semver::Version`
8755        // parser, so they must agree on the accepted set. Without this
8756        // pin, a future tightening of one axis could silently diverge
8757        // from the other. Mirrors the `:versao` requirement-axis
8758        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
8759        // commits established.
8760        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
8761            // From the canonical UpgradeFromEntry round-trip fixture
8762            // (`upgrade::tests::round_trip_load_module` peers).
8763            let entry = crate::UpgradeFromEntry {
8764                from: versao.to_string(),
8765                instructions: Vec::new(),
8766            };
8767            entry
8768                .validate()
8769                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
8770            caixa_with_versao(versao)
8771                .validate_versao()
8772                .unwrap_or_else(|e| {
8773                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
8774                });
8775        }
8776    }
8777
8778    // ── Caixa::validate_restart_window — supervisor restart-window
8779    //    folds through the shared `supervisor::duration_codec` ────────
8780
8781    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
8782        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
8783        c.kind = CaixaKind::Supervisor;
8784        c.restart_window = window.map(str::to_string);
8785        c
8786    }
8787
8788    #[test]
8789    fn validate_restart_window_accepts_none() {
8790        // The canonical "omit the slot to express no reset" shape — a
8791        // `None` raw string is the absence of the typed
8792        // `:restart-window` slot, which is exactly the SupervisorSpec
8793        // "never reset" semantics. The gate must be a no-op here; a
8794        // future tightening that rejected `None` would force every
8795        // supervisor caixa to authoring-time pin a window even when
8796        // the OTP semantics call for none.
8797        caixa_with_restart_window(None)
8798            .validate_restart_window()
8799            .unwrap();
8800    }
8801
8802    #[test]
8803    fn validate_restart_window_accepts_canonical_forms() {
8804        // Positive-set sweep across the canonical authoring units the
8805        // shared `supervisor::duration_codec::parse` accepts —
8806        // matches the codec-side `parse_accepts_integer_canonical_units`
8807        // pin in supervisor::tests so a future codec-side tightening
8808        // surfaces simultaneously on both axes.
8809        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
8810            caixa_with_restart_window(Some(window))
8811                .validate_restart_window()
8812                .unwrap_or_else(|e| {
8813                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
8814                });
8815        }
8816    }
8817
8818    #[test]
8819    fn validate_restart_window_rejects_fractional_seconds() {
8820        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
8821        // as f64 to 1.5 → renders back as `"1500ms"` on first
8822        // serialize). Prior to the fold + this gate, the inline
8823        // `parse_window_inline` accepted f64 magnitudes and silently
8824        // produced a `Duration::from_secs_f64(1.5)`, divergent from
8825        // the shared codec's integer-magnitude discipline on the
8826        // serde-routed siblings. The gate now surfaces a self-locating
8827        // diagnostic at the manifest layer.
8828        let err = caixa_with_restart_window(Some("1.5s"))
8829            .validate_restart_window()
8830            .unwrap_err();
8831        let ManifestError::RestartWindowMalformed {
8832            restart_window,
8833            reason,
8834        } = err
8835        else {
8836            panic!("expected RestartWindowMalformed for fractional seconds");
8837        };
8838        assert_eq!(restart_window, "1.5s");
8839        assert!(
8840            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
8841            "diagnostic must carry shared-codec wording, got {reason:?}"
8842        );
8843    }
8844
8845    #[test]
8846    fn validate_restart_window_rejects_decimal_shaped_integer() {
8847        // The `"1.0s"` class — numerically `1s` exactly, but the
8848        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
8849        // gets the same canonical-form diagnostic.
8850        let err = caixa_with_restart_window(Some("1.0s"))
8851            .validate_restart_window()
8852            .unwrap_err();
8853        assert!(
8854            matches!(
8855                err,
8856                ManifestError::RestartWindowMalformed { ref restart_window, .. }
8857                    if restart_window == "1.0s"
8858            ),
8859            "got {err:?}"
8860        );
8861    }
8862
8863    #[test]
8864    fn validate_restart_window_rejects_half_unit_minute() {
8865        // `"0.5m"` is the unit-fraction footgun — author writes a
8866        // human-readable half-minute, the prior inline parser silently
8867        // produced `Duration::from_secs_f64(30.0)` and serde
8868        // re-emitted as `"30s"`, rewriting author intent. The gate
8869        // closes the loop at the manifest layer.
8870        let err = caixa_with_restart_window(Some("0.5m"))
8871            .validate_restart_window()
8872            .unwrap_err();
8873        let ManifestError::RestartWindowMalformed {
8874            restart_window,
8875            reason,
8876        } = err
8877        else {
8878            panic!("expected RestartWindowMalformed");
8879        };
8880        assert_eq!(restart_window, "0.5m");
8881        assert!(
8882            reason.contains("\"30s\""),
8883            "diagnostic must point at the canonical-form remediation, got {reason:?}"
8884        );
8885    }
8886
8887    #[test]
8888    fn validate_restart_window_rejects_leading_sign() {
8889        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
8890        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
8891        // and was caught by the `num < 0.0` arm which silently
8892        // returned `None`, dropping the author-supplied window). The
8893        // shared codec's digit-only gate rejects both with a unified
8894        // canonical-form diagnostic; the manifest-layer wrapper names
8895        // the offending value.
8896        for bad in ["+30s", "-30s"] {
8897            let err = caixa_with_restart_window(Some(bad))
8898                .validate_restart_window()
8899                .unwrap_err();
8900            assert!(
8901                matches!(
8902                    err,
8903                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
8904                        if restart_window == bad
8905                ),
8906                "got {err:?} for {bad:?}"
8907            );
8908        }
8909    }
8910
8911    #[test]
8912    fn validate_restart_window_rejects_unknown_unit() {
8913        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
8914        // unit dispatch surfaces an `unknown duration unit` reason;
8915        // the manifest-layer wrapper names the offending value.
8916        let err = caixa_with_restart_window(Some("30x"))
8917            .validate_restart_window()
8918            .unwrap_err();
8919        let ManifestError::RestartWindowMalformed {
8920            restart_window,
8921            reason,
8922        } = err
8923        else {
8924            panic!("expected RestartWindowMalformed for unknown unit");
8925        };
8926        assert_eq!(restart_window, "30x");
8927        assert!(
8928            reason.contains("unknown duration unit"),
8929            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
8930        );
8931    }
8932
8933    #[test]
8934    fn validate_restart_window_rejects_garbage() {
8935        // Pure non-numeric magnitude (`"abc"`) falls through to the
8936        // shared codec's narrower `"bad duration magnitude"` arm. Same
8937        // diagnostic shape as the codec-side
8938        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
8939        let err = caixa_with_restart_window(Some("abc"))
8940            .validate_restart_window()
8941            .unwrap_err();
8942        let ManifestError::RestartWindowMalformed {
8943            restart_window,
8944            reason,
8945        } = err
8946        else {
8947            panic!("expected RestartWindowMalformed for garbage");
8948        };
8949        assert_eq!(restart_window, "abc");
8950        assert!(
8951            reason.contains("bad duration magnitude"),
8952            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
8953        );
8954    }
8955
8956    #[test]
8957    fn validate_restart_window_rejects_empty_string() {
8958        // The empty-after-trim edge case — distinct from the `None`
8959        // canonical "omit the slot" shape. The shared codec's
8960        // digit-only gate refuses an empty magnitude; the manifest
8961        // layer names the offending `""` so the author can grep for
8962        // the literal empty value in their `caixa.lisp` and either
8963        // remove the slot (the canonical "no reset" shape) or pin a
8964        // positive duration.
8965        let err = caixa_with_restart_window(Some(""))
8966            .validate_restart_window()
8967            .unwrap_err();
8968        assert!(
8969            matches!(
8970                err,
8971                ManifestError::RestartWindowMalformed { ref restart_window, .. }
8972                    if restart_window.is_empty()
8973            ),
8974            "got {err:?}"
8975        );
8976    }
8977
8978    #[test]
8979    fn validate_restart_window_diagnostic_carries_offending_value() {
8980        // Diagnostic-shape pin (peer with
8981        // `nome_invalid_diagnostic_carries_offending_nome` /
8982        // `versao_invalid_diagnostic_carries_offending_versao`): the
8983        // error names the offending raw `:restart-window` verbatim
8984        // with a non-empty shared-codec-shaped reason, so a `feira
8985        // lint` run can render the diagnostic without re-parsing.
8986        let err = caixa_with_restart_window(Some("1.5s"))
8987            .validate_restart_window()
8988            .unwrap_err();
8989        let ManifestError::RestartWindowMalformed {
8990            restart_window,
8991            reason,
8992        } = err
8993        else {
8994            panic!("expected RestartWindowMalformed variant");
8995        };
8996        assert_eq!(restart_window, "1.5s");
8997        assert!(
8998            !reason.is_empty(),
8999            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
9000        );
9001    }
9002
9003    #[test]
9004    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
9005        // Behavioral parity pin after the fold (`parse_window_inline`
9006        // deletion): the canonical `"60s"` still produces
9007        // `Duration::from_secs(60)` on the typed view — the fold is
9008        // semantically equivalent to the prior inline parser on the
9009        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
9010        // pin, narrowed to the parser-side contract.
9011        let c = caixa_with_restart_window(Some("60s"));
9012        let view = c.supervisor_view().expect("Supervisor kind has a view");
9013        assert_eq!(
9014            view.restart_window,
9015            Some(std::time::Duration::from_secs(60))
9016        );
9017    }
9018
9019    #[test]
9020    fn supervisor_view_soft_swallows_what_validate_rejects() {
9021        // Parity pin between the view-construction path and the
9022        // manifest-level validator: the same `"1.5s"` that surfaces
9023        // `RestartWindowMalformed` at `validate_restart_window` time
9024        // becomes `restart_window: None` on the typed view (the fold
9025        // preserves the existing best-effort shape of `supervisor_view`).
9026        // The contract is: a layout-verifier / `feira lint` flow that
9027        // cares about the malformed-window axis MUST consult
9028        // `validate_restart_window` — relying solely on the view's
9029        // `None` swallows the diagnostic silently. This pin makes the
9030        // expectation a typed invariant.
9031        let c = caixa_with_restart_window(Some("1.5s"));
9032        let view = c.supervisor_view().expect("Supervisor kind has a view");
9033        assert_eq!(
9034            view.restart_window, None,
9035            "view-construction path soft-swallows the parse error to None"
9036        );
9037        // And the manifest-level validator does NOT soft-swallow:
9038        assert!(
9039            matches!(
9040                c.validate_restart_window().unwrap_err(),
9041                ManifestError::RestartWindowMalformed { ref restart_window, .. }
9042                    if restart_window == "1.5s"
9043            ),
9044            "validator must surface the offending value",
9045        );
9046    }
9047
9048    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
9049
9050    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
9051        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9052        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
9053        c.exe = exe.into_iter().map(String::from).collect();
9054        c.servicos = servicos.into_iter().map(String::from).collect();
9055        c
9056    }
9057
9058    #[test]
9059    fn validate_code_paths_accepts_canonical_template() {
9060        // The bare `Caixa::template` shape is the gate's identity element
9061        // on the canonical authoring shape — `:bibliotecas
9062        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
9063        // that the gate is non-disruptive against every existing caixa.
9064        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9065        c.validate_code_paths().unwrap();
9066    }
9067
9068    #[test]
9069    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
9070        // Positive control sweep: a canonical-shaped path on every slot
9071        // passes. Mirrors the peer
9072        // `behavior::validate_every_slot_relative_is_ok` pin.
9073        let c = caixa_with_code_paths(
9074            vec!["lib/demo.lisp", "lib/helpers.lisp"],
9075            vec!["exe/demo", "exe/tool"],
9076            vec!["servicos/demo.computeunit.yaml"],
9077        );
9078        c.validate_code_paths().unwrap();
9079    }
9080
9081    #[test]
9082    fn validate_code_paths_accepts_all_empty_lists() {
9083        // The empty-list identity element: every Caixa with no declared
9084        // code paths trivially passes (Supervisor / Aplicacao kinds rely
9085        // on this — the OwnCode gate already rejected them before the
9086        // path-shape gate runs in the layout, but the validator itself
9087        // must accept the empty shape).
9088        let c = caixa_with_code_paths(vec![], vec![], vec![]);
9089        c.validate_code_paths().unwrap();
9090    }
9091
9092    #[test]
9093    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
9094        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
9095        let err = c.validate_code_paths().unwrap_err();
9096        assert!(
9097            matches!(
9098                err,
9099                ManifestError::CodePathEmpty {
9100                    slot: ":bibliotecas"
9101                }
9102            ),
9103            "got {err:?}",
9104        );
9105    }
9106
9107    #[test]
9108    fn validate_code_paths_rejects_empty_exe_entry() {
9109        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
9110        let err = c.validate_code_paths().unwrap_err();
9111        assert!(
9112            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
9113            "got {err:?}",
9114        );
9115    }
9116
9117    #[test]
9118    fn validate_code_paths_rejects_empty_servicos_entry() {
9119        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
9120        let err = c.validate_code_paths().unwrap_err();
9121        assert!(
9122            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
9123            "got {err:?}",
9124        );
9125    }
9126
9127    #[test]
9128    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
9129        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
9130        // so an absolute path that resolves on disk silently passes the
9131        // layout's existence check — the canonical sandbox-escape on
9132        // the biblioteca axis.
9133        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
9134        let err = c.validate_code_paths().unwrap_err();
9135        let ManifestError::CodePathAbsolute { slot, path } = err else {
9136            panic!("expected CodePathAbsolute, got {err:?}");
9137        };
9138        assert_eq!(slot, ":bibliotecas");
9139        assert_eq!(path, PathBuf::from("/etc/passwd"));
9140    }
9141
9142    #[test]
9143    fn validate_code_paths_rejects_absolute_exe_entry() {
9144        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
9145        let err = c.validate_code_paths().unwrap_err();
9146        let ManifestError::CodePathAbsolute { slot, path } = err else {
9147            panic!("expected CodePathAbsolute, got {err:?}");
9148        };
9149        assert_eq!(slot, ":exe");
9150        assert_eq!(path, PathBuf::from("/usr/bin/env"));
9151    }
9152
9153    #[test]
9154    fn validate_code_paths_rejects_absolute_servicos_entry() {
9155        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
9156        let err = c.validate_code_paths().unwrap_err();
9157        let ManifestError::CodePathAbsolute { slot, path } = err else {
9158            panic!("expected CodePathAbsolute, got {err:?}");
9159        };
9160        assert_eq!(slot, ":servicos");
9161        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
9162    }
9163
9164    #[test]
9165    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
9166        // Canonical "I want a lib from a sibling caixa" footgun on the
9167        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
9168        // downstream, so a leading `..` traverses to the parent of the
9169        // caixa root with no diagnostic at layout time if the resolved
9170        // target exists.
9171        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
9172        let err = c.validate_code_paths().unwrap_err();
9173        let ManifestError::CodePathParentEscape { slot, path } = err else {
9174            panic!("expected CodePathParentEscape, got {err:?}");
9175        };
9176        assert_eq!(slot, ":bibliotecas");
9177        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
9178    }
9179
9180    #[test]
9181    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
9182        // Mid-path `..` defeats the layout's component-aware
9183        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
9184        // `starts_with(<root>/exe)` is true, but the canonical resolution
9185        // lives outside the caixa root. Caught regardless of where the
9186        // `..` sits — mirrors the peer
9187        // `behavior::validate_rejects_parent_escape_mid_path` pin.
9188        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
9189        let err = c.validate_code_paths().unwrap_err();
9190        let ManifestError::CodePathParentEscape { slot, path } = err else {
9191            panic!("expected CodePathParentEscape, got {err:?}");
9192        };
9193        assert_eq!(slot, ":exe");
9194        assert_eq!(path, PathBuf::from("exe/../../escape"));
9195    }
9196
9197    #[test]
9198    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
9199        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
9200        let err = c.validate_code_paths().unwrap_err();
9201        let ManifestError::CodePathParentEscape { slot, path } = err else {
9202            panic!("expected CodePathParentEscape, got {err:?}");
9203        };
9204        assert_eq!(slot, ":servicos");
9205        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
9206    }
9207
9208    #[test]
9209    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
9210        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
9211        // `:servicos`. A manifest with malformed entries on all three
9212        // surfaces surfaces the `:bibliotecas` defect first, mirroring
9213        // the canonical declaration order
9214        // `Caixa::declared_foreign_code_slots` already establishes for
9215        // the foreign-code-slot diagnostic.
9216        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
9217        let err = c.validate_code_paths().unwrap_err();
9218        assert!(
9219            matches!(
9220                err,
9221                ManifestError::CodePathEmpty {
9222                    slot: ":bibliotecas"
9223                }
9224            ),
9225            "got {err:?}",
9226        );
9227    }
9228
9229    #[test]
9230    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
9231        // Within-slot precedence pin: empty → absolute → parent-escape,
9232        // matching the [`PathShapeViolation`] arm-ordering every peer
9233        // `is_sandboxed_relative_path` caller follows (b0c8389
9234        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
9235        // `:bibliotecas` list whose first entry is empty *and* whose
9236        // later entries are absolute/parent-escape surfaces the empty
9237        // arm first, on the lexicographically-earliest offending entry.
9238        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
9239        let err = c.validate_code_paths().unwrap_err();
9240        assert!(
9241            matches!(
9242                err,
9243                ManifestError::CodePathEmpty {
9244                    slot: ":bibliotecas"
9245                }
9246            ),
9247            "got {err:?}",
9248        );
9249    }
9250
9251    #[test]
9252    fn validate_code_paths_first_offender_per_slot_wins() {
9253        // Within a single slot, the first declaration-order offender
9254        // surfaces — pins that the gate is left-to-right deterministic
9255        // (peer of every `*_first_collision_*` pin on duplicate gates).
9256        let c = caixa_with_code_paths(
9257            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
9258            vec![],
9259            vec![],
9260        );
9261        let err = c.validate_code_paths().unwrap_err();
9262        let ManifestError::CodePathAbsolute { slot, path } = err else {
9263            panic!("expected CodePathAbsolute, got {err:?}");
9264        };
9265        assert_eq!(slot, ":bibliotecas");
9266        assert_eq!(path, PathBuf::from("/etc/escape"));
9267    }
9268
9269    #[test]
9270    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
9271        // Diagnostic-shape pin (peer with
9272        // `nome_invalid_diagnostic_carries_offending_nome` /
9273        // `versao_invalid_diagnostic_carries_offending_versao`): the
9274        // error's Display surfaces both the offending `:slot` tag and
9275        // the offending path verbatim, so a `feira lint` run can render
9276        // the diagnostic without re-parsing.
9277        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
9278        let rendered = c.validate_code_paths().unwrap_err().to_string();
9279        assert!(
9280            rendered.contains(":bibliotecas"),
9281            "diagnostic must name the offending slot: {rendered}",
9282        );
9283        assert!(
9284            rendered.contains("/etc/passwd"),
9285            "diagnostic must quote the offending path: {rendered}",
9286        );
9287    }
9288
9289    #[test]
9290    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
9291        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
9292        // axis. Without the gate `feira build` re-parses the same lib
9293        // twice, wasting work and silently masking the author's intent
9294        // to declare a *second* biblioteca.
9295        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
9296        let err = c.validate_code_paths().unwrap_err();
9297        let ManifestError::CodePathDuplicate { slot, path } = err else {
9298            panic!("expected CodePathDuplicate, got {err:?}");
9299        };
9300        assert_eq!(slot, ":bibliotecas");
9301        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
9302    }
9303
9304    #[test]
9305    fn validate_code_paths_rejects_duplicate_exe_entry() {
9306        // Same footgun on the Binario surface. The future `caixa-flake`
9307        // emitter that materializes each `:exe` entry as a flake
9308        // `packages.<name>` derivation would collide on the duplicate
9309        // package key — surfaced here at the typed-validate layer with a
9310        // self-locating diagnostic instead.
9311        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
9312        let err = c.validate_code_paths().unwrap_err();
9313        let ManifestError::CodePathDuplicate { slot, path } = err else {
9314            panic!("expected CodePathDuplicate, got {err:?}");
9315        };
9316        assert_eq!(slot, ":exe");
9317        assert_eq!(path, PathBuf::from("exe/cli"));
9318    }
9319
9320    #[test]
9321    fn validate_code_paths_rejects_duplicate_servicos_entry() {
9322        // Same footgun on the Servico surface. The peer caixa-helm /
9323        // caixa-flux renderers refuse `:servicos.len() != 1` with the
9324        // narrower `UnsupportedServicoCount` diagnostic, but that
9325        // diagnostic surfaces "too many servicos" without naming
9326        // "duplicate entry" — the typed self-locating framing only lands
9327        // at this gate.
9328        let c = caixa_with_code_paths(
9329            vec![],
9330            vec![],
9331            vec![
9332                "servicos/demo.computeunit.yaml",
9333                "servicos/demo.computeunit.yaml",
9334            ],
9335        );
9336        let err = c.validate_code_paths().unwrap_err();
9337        let ManifestError::CodePathDuplicate { slot, path } = err else {
9338            panic!("expected CodePathDuplicate, got {err:?}");
9339        };
9340        assert_eq!(slot, ":servicos");
9341        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
9342    }
9343
9344    #[test]
9345    fn validate_code_paths_accepts_same_path_across_slots() {
9346        // Per-list scope pin: a `:bibliotecas` entry that happens to
9347        // collide with an `:exe` or `:servicos` entry as a *string* is
9348        // not a duplicate by this gate (each list gets its own HashSet),
9349        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
9350        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
9351        // shape on the dep axis). The structural `starts_with(<exe |
9352        // servicos>_dir)` fence at layout time prevents the realistic
9353        // cross-slot collision case from existing on disk, but the gate's
9354        // per-list scope is correct independent of that downstream fence.
9355        let c = caixa_with_code_paths(
9356            vec!["lib/x.lisp"],
9357            vec!["exe/x"],
9358            vec!["servicos/x.computeunit.yaml"],
9359        );
9360        c.validate_code_paths().unwrap();
9361    }
9362
9363    #[test]
9364    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
9365        // Within-slot ordering pin: structural defects (empty / absolute
9366        // / parent-escape) fire before the duplicate gate on the same
9367        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
9368        // surfaces the narrower `CodePathEmpty` for the empty entry
9369        // first, not the duplicate on the later pair — same arm-ordering
9370        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
9371        // `:autores` 86c769b, `:deps` 359fba5).
9372        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
9373        let err = c.validate_code_paths().unwrap_err();
9374        assert!(
9375            matches!(
9376                err,
9377                ManifestError::CodePathEmpty {
9378                    slot: ":bibliotecas"
9379                }
9380            ),
9381            "got {err:?}",
9382        );
9383    }
9384
9385    #[test]
9386    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
9387        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
9388        // duplicates surface before `:exe` duplicates, matching the
9389        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
9390        // order every peer per-slot diagnostic on this surface follows.
9391        let c = caixa_with_code_paths(
9392            vec!["lib/x.lisp", "lib/x.lisp"],
9393            vec!["exe/y", "exe/y"],
9394            vec![],
9395        );
9396        let err = c.validate_code_paths().unwrap_err();
9397        let ManifestError::CodePathDuplicate { slot, path } = err else {
9398            panic!("expected CodePathDuplicate, got {err:?}");
9399        };
9400        assert_eq!(slot, ":bibliotecas");
9401        assert_eq!(path, PathBuf::from("lib/x.lisp"));
9402    }
9403
9404    #[test]
9405    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
9406        // Diagnostic-shape pin (peer with
9407        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
9408        // on the structural arm): the duplicate-arm Display surfaces both
9409        // the offending `:slot` tag and the offending path verbatim, so a
9410        // `feira lint` run can render the diagnostic without re-parsing.
9411        let c = caixa_with_code_paths(
9412            vec![],
9413            vec![],
9414            vec![
9415                "servicos/demo.computeunit.yaml",
9416                "servicos/demo.computeunit.yaml",
9417            ],
9418        );
9419        let rendered = c.validate_code_paths().unwrap_err().to_string();
9420        assert!(
9421            rendered.contains(":servicos"),
9422            "diagnostic must name the offending slot: {rendered}",
9423        );
9424        assert!(
9425            rendered.contains("servicos/demo.computeunit.yaml"),
9426            "diagnostic must quote the offending path: {rendered}",
9427        );
9428    }
9429
9430    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
9431    //
9432    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
9433    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
9434    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
9435    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
9436    // at parse time — the same downstream consumer the peer `:behavior
9437    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
9438    // `:upgrade-from :state-change :script` (33cc830,
9439    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
9440    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
9441    // nix-built executable surface (`"exe/<name>"` shape per the canonical
9442    // [`crate::LayoutError::ExeOutsideDir`] error message and every
9443    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
9444    // is the `.computeunit.yaml` ComputeUnit-CR axis.
9445
9446    #[test]
9447    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
9448        // Canonical "I dragged the wrong file from the workspace tree"
9449        // footgun on the biblioteca axis. Without the gate `feira build`
9450        // hands the extensionless path to `tatara_lisp::read` and fails
9451        // with a parser-shaped diagnostic far from the source caixa.lisp,
9452        // with no field naming the offending `:bibliotecas` entry.
9453        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
9454            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9455            let err = c.validate_code_paths().unwrap_err();
9456            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9457                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
9458            };
9459            assert_eq!(slot, ":bibliotecas");
9460            assert_eq!(path, PathBuf::from(relpath));
9461        }
9462    }
9463
9464    #[test]
9465    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
9466        // Wrong-extension sweep across common authoring footguns. Same
9467        // sweep posture as the peer
9468        // `behavior::validate_rejects_wrong_extension` (c97815a) and
9469        // `upgrade::tests::state_change_rejects_wrong_extension_script`
9470        // (33cc830) cases.
9471        for relpath in [
9472            "lib/demo.rs",
9473            "lib/demo.txt",
9474            "lib/demo.md",
9475            "lib/demo.json",
9476            "lib/demo.yaml",
9477            "lib/demo.toml",
9478            "lib/demo.lisp.bak",
9479            "lib/demo.lispx",
9480            "lib/demo.lis",
9481        ] {
9482            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9483            let err = c.validate_code_paths().unwrap_err();
9484            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9485                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
9486            };
9487            assert_eq!(slot, ":bibliotecas");
9488            assert_eq!(path, PathBuf::from(relpath));
9489        }
9490    }
9491
9492    #[test]
9493    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
9494        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
9495        // contract. An uppercase `.LISP` shape that the layout's existence
9496        // check would (case-insensitively, on case-insensitive volumes)
9497        // match the on-disk file still mismatches the canonical form the
9498        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
9499        // contract. Mirrors the peer
9500        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
9501        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
9502        // (33cc830) sweeps.
9503        for relpath in [
9504            "lib/demo.LISP",
9505            "lib/demo.Lisp",
9506            "lib/demo.LiSp",
9507            "lib/demo.lISP",
9508        ] {
9509            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9510            let err = c.validate_code_paths().unwrap_err();
9511            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9512                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
9513            };
9514            assert_eq!(slot, ":bibliotecas");
9515            assert_eq!(path, PathBuf::from(relpath));
9516        }
9517    }
9518
9519    #[test]
9520    fn validate_code_paths_accepts_canonical_lisp_shapes() {
9521        // Positive-control sweep through every canonical authoring shape
9522        // every in-tree fixture and the `Caixa::template` scaffold use.
9523        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
9524        // (c97815a) and the lifted predicate's own
9525        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
9526        // (33cc830).
9527        for relpath in [
9528            "lib/demo.lisp",
9529            "lib/handlers.lisp",
9530            "lib/migrations/v01-to-v02.lisp",
9531            "demo.lisp",
9532            "a.lisp",
9533            "./lib/demo.lisp",
9534            "lib/./handlers.lisp",
9535            "lib/migrations/v.0.1.lisp",
9536        ] {
9537            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9538            c.validate_code_paths()
9539                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
9540        }
9541    }
9542
9543    #[test]
9544    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
9545        // The file-type gate is per-slot — only `:bibliotecas` carries the
9546        // tatara-lisp-source contract. An extensionless `:exe` entry
9547        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
9548        // canonical shapes every in-tree fixture uses, and must continue
9549        // to pass validate. Pins that a future tightening that broadens
9550        // the `.lisp` gate to either axis surfaces as a test failure
9551        // rather than as a silent breaking change to existing valid
9552        // manifests.
9553        let c = caixa_with_code_paths(
9554            vec![],
9555            vec!["exe/demo", "exe/tool"],
9556            vec!["servicos/demo.computeunit.yaml"],
9557        );
9558        c.validate_code_paths().unwrap();
9559    }
9560
9561    #[test]
9562    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
9563        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
9564        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
9565        // sandbox-shape diagnostic first (the `.lisp` remediation would
9566        // be misleading when the offending path can never resolve under
9567        // the caixa root anyway). Mirrors the peer
9568        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
9569        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
9570        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
9571        // on `:upgrade-from :state-change :script` (33cc830).
9572        //
9573        // Empty wins (the strictly-smaller-scope structural arm).
9574        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
9575        assert!(
9576            matches!(
9577                c.validate_code_paths().unwrap_err(),
9578                ManifestError::CodePathEmpty {
9579                    slot: ":bibliotecas"
9580                }
9581            ),
9582            "empty must win over non-lisp-extension",
9583        );
9584        // Absolute wins (the path can't resolve under the caixa root).
9585        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
9586        let err = c.validate_code_paths().unwrap_err();
9587        let ManifestError::CodePathAbsolute { slot, .. } = err else {
9588            panic!("absolute must win over non-lisp-extension, got {err:?}");
9589        };
9590        assert_eq!(slot, ":bibliotecas");
9591        // ParentEscape wins (the path escapes the caixa root).
9592        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
9593        let err = c.validate_code_paths().unwrap_err();
9594        let ManifestError::CodePathParentEscape { slot, .. } = err else {
9595            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
9596        };
9597        assert_eq!(slot, ":bibliotecas");
9598    }
9599
9600    #[test]
9601    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
9602        // Within-slot precedence pin: the per-entry file-type shape gate
9603        // fires before the cross-entry duplicate gate, so the narrower
9604        // structural defect dominates the uniqueness diagnostic. A
9605        // `("lib/x.txt" "lib/x.txt")` shape surfaces
9606        // `CodePathNonLispExtension` on the first entry rather than
9607        // `CodePathDuplicate` on the pair — same posture every per-entry
9608        // shape-gate-precedes-duplicate cascade follows on this surface
9609        // (the empty / absolute / parent-escape arms already precede the
9610        // duplicate arm; the lifted file-type arm joins that set).
9611        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
9612        let err = c.validate_code_paths().unwrap_err();
9613        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9614            panic!("expected CodePathNonLispExtension, got {err:?}");
9615        };
9616        assert_eq!(slot, ":bibliotecas");
9617        assert_eq!(path, PathBuf::from("lib/x.txt"));
9618    }
9619
9620    #[test]
9621    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
9622        // Diagnostic-shape pin (peer with
9623        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
9624        // on the sandbox-shape arms and
9625        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
9626        // on the duplicate arm): the file-type-arm Display surfaces both
9627        // the offending `:slot` tag, the offending path verbatim, and the
9628        // expected `.lisp` extension named in the remediation text, so a
9629        // `feira lint` run can render the diagnostic without re-parsing.
9630        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
9631        let rendered = c.validate_code_paths().unwrap_err().to_string();
9632        assert!(
9633            rendered.contains(":bibliotecas"),
9634            "diagnostic must name the offending slot: {rendered}",
9635        );
9636        assert!(
9637            rendered.contains("lib/demo.rs"),
9638            "diagnostic must quote the offending path: {rendered}",
9639        );
9640        assert!(
9641            rendered.contains(".lisp"),
9642            "diagnostic must name the expected extension: {rendered}",
9643        );
9644    }
9645
9646    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
9647    //
9648    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
9649    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
9650    // contract. The peer caixa-helm / caixa-flux renderers consume each
9651    // `:servicos` entry through `serde_yaml::from_str` as a typed
9652    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
9653    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
9654    // axis `Path::extension` can't express on its own.
9655
9656    #[test]
9657    fn validate_code_paths_rejects_no_extension_servicos_entry() {
9658        // Canonical "I dragged the wrong file from the workspace tree"
9659        // footgun on the Servico axis. Without the gate the peer
9660        // caixa-helm / caixa-flux renderers hand the extensionless path
9661        // to `serde_yaml::from_str` and fail with a parser-shaped
9662        // diagnostic far from the source caixa.lisp, with no field
9663        // naming the offending `:servicos` entry.
9664        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
9665            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9666            let err = c.validate_code_paths().unwrap_err();
9667            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9668                panic!(
9669                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9670                     got {err:?}"
9671                );
9672            };
9673            assert_eq!(slot, ":servicos");
9674            assert_eq!(path, PathBuf::from(relpath));
9675        }
9676    }
9677
9678    #[test]
9679    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
9680        // Wrong-extension sweep across common authoring footguns on the
9681        // Servico axis. Bare `.yaml` is the canonical "I forgot the
9682        // `.computeunit` segment" typo; the off-by-one-segment shapes
9683        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
9684        // bare `Path::extension` view but mismatch the typed compound
9685        // suffix the renderers' `serde_yaml::from_str` consumer demands.
9686        // Same sweep-posture as the peer
9687        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
9688        // (64772a9) on the sibling tatara-lisp-source axis.
9689        for relpath in [
9690            "servicos/demo.yaml",
9691            "servicos/demo.yml",
9692            "servicos/demo.json",
9693            "servicos/demo.toml",
9694            "servicos/demo.txt",
9695            "servicos/demo.computeunit.yaml.bak",
9696            "servicos/demo.computeunit.yam",
9697            "servicos/demo.computeunit",
9698            "servicos/demo-computeunit.yaml",
9699            "servicos/demo_computeunit.yaml",
9700        ] {
9701            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9702            let err = c.validate_code_paths().unwrap_err();
9703            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9704                panic!(
9705                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9706                     got {err:?}"
9707                );
9708            };
9709            assert_eq!(slot, ":servicos");
9710            assert_eq!(path, PathBuf::from(relpath));
9711        }
9712    }
9713
9714    #[test]
9715    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
9716        // Case-sensitivity sweep — pins the strict lowercase
9717        // `.computeunit.yaml` contract. A case-folded shape that the
9718        // layout's existence check would (case-insensitively, on
9719        // case-insensitive volumes) match the on-disk file still
9720        // mismatches the canonical form the codec emits, breaking the
9721        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
9722        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
9723        // (64772a9) sweep on the sibling tatara-lisp-source axis.
9724        for relpath in [
9725            "servicos/demo.ComputeUnit.yaml",
9726            "servicos/demo.COMPUTEUNIT.yaml",
9727            "servicos/demo.computeunit.YAML",
9728            "servicos/demo.computeunit.Yaml",
9729            "servicos/demo.COMPUTEUNIT.YAML",
9730        ] {
9731            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9732            let err = c.validate_code_paths().unwrap_err();
9733            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9734                panic!(
9735                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9736                     got {err:?}"
9737                );
9738            };
9739            assert_eq!(slot, ":servicos");
9740            assert_eq!(path, PathBuf::from(relpath));
9741        }
9742    }
9743
9744    #[test]
9745    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
9746        // Degenerate hidden-file shape: a file name exactly equal to the
9747        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
9748        // the structural "Servico declared with no identity" footgun.
9749        // The substrate identifies each ComputeUnit by the file-stem
9750        // segment that precedes `.computeunit.yaml` (the rendered
9751        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
9752        // the M3 `:contratos` membership lookup), so an empty stem
9753        // leaves the Servico unidentifiable. Pinned at the typed-axis
9754        // level so a future regression that drops the `name.len() >
9755        // SUFFIX.len()` bound at the predicate surfaces here, not
9756        // piecemeal as a `lareira-` chart-name collision at render time.
9757        for relpath in ["servicos/.computeunit.yaml"] {
9758            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9759            let err = c.validate_code_paths().unwrap_err();
9760            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9761                panic!(
9762                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9763                     got {err:?}"
9764                );
9765            };
9766            assert_eq!(slot, ":servicos");
9767            assert_eq!(path, PathBuf::from(relpath));
9768        }
9769    }
9770
9771    #[test]
9772    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
9773        // Positive-control sweep through every canonical authoring shape
9774        // every in-tree fixture and the `Caixa::template` scaffold use.
9775        // Mirrors the peer
9776        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
9777        // and the lifted predicate's own
9778        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
9779        // render.rs.
9780        for relpath in [
9781            "servicos/demo.computeunit.yaml",
9782            "servicos/hello-rio.computeunit.yaml",
9783            "servicos/my-service.computeunit.yaml",
9784            "servicos/a.computeunit.yaml",
9785            "./servicos/demo.computeunit.yaml",
9786            "servicos/./demo.computeunit.yaml",
9787            "servicos/sub/nested.computeunit.yaml",
9788            "servicos/v0.1.computeunit.yaml",
9789        ] {
9790            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9791            c.validate_code_paths()
9792                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
9793        }
9794    }
9795
9796    #[test]
9797    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
9798        // The file-type gate is per-slot — only `:servicos` carries the
9799        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
9800        // entry and an extensionless `:exe` entry are the canonical
9801        // shapes every in-tree fixture uses, and must continue to pass
9802        // validate. Peer of
9803        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
9804        // (64772a9) — together pin that the typed
9805        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
9806        // cross-axis leakage in either direction.
9807        let c = caixa_with_code_paths(
9808            vec!["lib/demo.lisp"],
9809            vec!["exe/demo", "exe/tool"],
9810            vec!["servicos/demo.computeunit.yaml"],
9811        );
9812        c.validate_code_paths().unwrap();
9813    }
9814
9815    #[test]
9816    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
9817        // Cross-arm precedence pin: a `:servicos` entry that is *both*
9818        // sandbox-escaping and wrong-extension surfaces the more
9819        // fundamental sandbox-shape diagnostic first (the
9820        // `.computeunit.yaml` remediation would be misleading when the
9821        // offending path can never resolve under the caixa root
9822        // anyway). Mirrors the peer
9823        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
9824        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
9825        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
9826        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
9827        // table establishes.
9828        //
9829        // Empty wins (the strictly-smaller-scope structural arm).
9830        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
9831        assert!(
9832            matches!(
9833                c.validate_code_paths().unwrap_err(),
9834                ManifestError::CodePathEmpty { slot: ":servicos" }
9835            ),
9836            "empty must win over non-computeunit-yaml-extension",
9837        );
9838        // Absolute wins (the path can't resolve under the caixa root).
9839        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
9840        let err = c.validate_code_paths().unwrap_err();
9841        let ManifestError::CodePathAbsolute { slot, .. } = err else {
9842            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
9843        };
9844        assert_eq!(slot, ":servicos");
9845        // ParentEscape wins (the path escapes the caixa root).
9846        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
9847        let err = c.validate_code_paths().unwrap_err();
9848        let ManifestError::CodePathParentEscape { slot, .. } = err else {
9849            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
9850        };
9851        assert_eq!(slot, ":servicos");
9852    }
9853
9854    #[test]
9855    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
9856        // Within-slot precedence pin: the per-entry file-type shape gate
9857        // fires before the cross-entry duplicate gate, so the narrower
9858        // structural defect dominates the uniqueness diagnostic. A
9859        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
9860        // `CodePathNonComputeUnitYamlExtension` on the first entry
9861        // rather than `CodePathDuplicate` on the pair — same posture
9862        // every per-entry shape-gate-precedes-duplicate cascade follows
9863        // on this surface, peer of the 64772a9 `:bibliotecas`
9864        // `("lib/x.txt" "lib/x.txt")` ordering.
9865        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
9866        let err = c.validate_code_paths().unwrap_err();
9867        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9868            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
9869        };
9870        assert_eq!(slot, ":servicos");
9871        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
9872    }
9873
9874    #[test]
9875    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
9876     {
9877        // Diagnostic-shape pin (peer with
9878        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
9879        // on the sibling tatara-lisp-source axis): the file-type-arm
9880        // Display surfaces both the offending `:slot` tag, the
9881        // offending path verbatim, and the expected
9882        // `.computeunit.yaml` compound suffix named in the remediation
9883        // text, so a `feira lint` run can render the diagnostic without
9884        // re-parsing.
9885        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
9886        let rendered = c.validate_code_paths().unwrap_err().to_string();
9887        assert!(
9888            rendered.contains(":servicos"),
9889            "diagnostic must name the offending slot: {rendered}",
9890        );
9891        assert!(
9892            rendered.contains("servicos/demo.yaml"),
9893            "diagnostic must quote the offending path: {rendered}",
9894        );
9895        assert!(
9896            rendered.contains(".computeunit.yaml"),
9897            "diagnostic must name the expected compound suffix: {rendered}",
9898        );
9899    }
9900
9901    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
9902
9903    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
9904        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9905        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
9906        c
9907    }
9908
9909    #[test]
9910    fn validate_etiquetas_accepts_empty_list() {
9911        // The empty-list identity: every caixa with no declared tags
9912        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
9913        // so the gate is non-disruptive against every existing manifest.
9914        let c = caixa_with_etiquetas(vec![]);
9915        c.validate_etiquetas().unwrap();
9916    }
9917
9918    #[test]
9919    fn validate_etiquetas_accepts_canonical_forms() {
9920        // Positive control sweep: a canonical-shaped non-empty distinct
9921        // tag list passes, mirroring the example checkout-aplicacao
9922        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
9923        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
9924        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
9925        c.validate_etiquetas().unwrap();
9926    }
9927
9928    #[test]
9929    fn validate_etiquetas_rejects_empty_entry() {
9930        // Canonical paste-from-blank-doc footgun. Without the gate the
9931        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
9932        // no-op tag indexing nothing in the future caixa-registry.
9933        let c = caixa_with_etiquetas(vec![""]);
9934        let err = c.validate_etiquetas().unwrap_err();
9935        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
9936    }
9937
9938    #[test]
9939    fn validate_etiquetas_rejects_duplicate_entry() {
9940        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
9941        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
9942        // collect at chart render — a "second wins / one silently
9943        // disappears" shape divergent from every peer typed-graph set
9944        // gate. The duplicate-arm names the offending tag verbatim.
9945        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
9946        let err = c.validate_etiquetas().unwrap_err();
9947        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
9948            panic!("expected EtiquetaDuplicate, got {err:?}");
9949        };
9950        assert_eq!(etiqueta, "demo");
9951    }
9952
9953    #[test]
9954    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
9955        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
9956        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
9957        // structural "this entry has no value" defect dominates the
9958        // cross-entry uniqueness diagnostic. Mirrors the peer
9959        // empty-before-duplicate cascades on `:caracteristicas`
9960        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
9961        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
9962        // `MembroDuplicate`).
9963        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
9964        let err = c.validate_etiquetas().unwrap_err();
9965        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
9966    }
9967
9968    #[test]
9969    fn validate_etiquetas_duplicate_reports_first_collision() {
9970        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
9971        // duplicate (the lexicographically-earliest offending position
9972        // — the second `"a"` at index 2 collides with the first `"a"`
9973        // at index 0), not the later `"b"` collision at index 3,
9974        // peer with every other first-collision diagnostic posture on
9975        // this surface (`validate_load_singularity_reports_first_collision`,
9976        // `validate_cleanup_singularity_reports_first_collision`).
9977        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
9978        let err = c.validate_etiquetas().unwrap_err();
9979        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
9980            panic!("expected EtiquetaDuplicate, got {err:?}");
9981        };
9982        assert_eq!(etiqueta, "a");
9983    }
9984
9985    #[test]
9986    fn validate_etiquetas_case_sensitive() {
9987        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
9988        // mirroring the peer `:membros :caixa` / `:children :caixa`
9989        // exact-string-match discipline. The shape gate this routine
9990        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
9991        // grammar) accepts mixed case — crates.io's keyword rule is
9992        // "case-insensitive" at the index layer but admits mixed case
9993        // at the entry layer (the canonical Helm chart `keywords:`
9994        // shape is lowercase by convention, but the grammar admits
9995        // uppercase). Case-sensitivity at the duplicate-set layer
9996        // remains structural — two distinct strings are two distinct
9997        // entries.
9998        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
9999        c.validate_etiquetas().unwrap();
10000    }
10001
10002    #[test]
10003    fn validate_etiquetas_diagnostic_carries_offending_tag() {
10004        // Diagnostic-shape pin (peer with
10005        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
10006        // the error's Display surfaces the offending tag verbatim, so a
10007        // `feira lint` run can render the diagnostic without re-parsing
10008        // and the author can grep their caixa.lisp for the offending
10009        // value.
10010        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
10011        let rendered = c.validate_etiquetas().unwrap_err().to_string();
10012        assert!(
10013            rendered.contains(":etiquetas"),
10014            "diagnostic must name the offending slot: {rendered}",
10015        );
10016        assert!(
10017            rendered.contains("demo"),
10018            "diagnostic must quote the offending tag: {rendered}",
10019        );
10020    }
10021
10022    #[test]
10023    fn validate_etiquetas_rejects_leading_whitespace_entry() {
10024        // Canonical paste-from-aligned-doc footgun. Without the shape
10025        // gate `" mesh"` silently passed validate and landed as a
10026        // YAML plain-style scalar with leading whitespace in the
10027        // rendered Chart.yaml `keywords:` array — every YAML 1.2
10028        // dumper trims leading whitespace from plain-style scalars,
10029        // so the authored space round-tripped inconsistently back
10030        // through `caixa.lisp`. Mirrors the peer
10031        // `validate_autores_rejects_leading_whitespace_entry`.
10032        let c = caixa_with_etiquetas(vec![" mesh"]);
10033        let err = c.validate_etiquetas().unwrap_err();
10034        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10035            panic!("expected EtiquetaInvalid, got {err:?}");
10036        };
10037        assert_eq!(etiqueta, " mesh");
10038        assert!(reason.contains("whitespace"), "got: {reason}");
10039    }
10040
10041    #[test]
10042    fn validate_etiquetas_rejects_embedded_newline_entry() {
10043        // Canonical paste-from-multiline-doc footgun — the author
10044        // pasted a multi-tag block into one `:etiquetas` entry
10045        // instead of splitting into one entry per tag. Without the
10046        // shape gate `"mesh\nhttp"` silently passed validate and
10047        // landed as a YAML-illegal multi-line scalar in the rendered
10048        // Chart.yaml `keywords:` array.
10049        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
10050        let err = c.validate_etiquetas().unwrap_err();
10051        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10052            panic!("expected EtiquetaInvalid, got {err:?}");
10053        };
10054        assert_eq!(etiqueta, "mesh\nhttp");
10055        assert!(reason.contains("newline"), "got: {reason}");
10056    }
10057
10058    #[test]
10059    fn validate_etiquetas_rejects_embedded_comma_entry() {
10060        // Canonical CSV-list-separator-confusion footgun: the author
10061        // confused the CSV-style separator convention with the
10062        // `:etiquetas` list grammar. Without the shape gate
10063        // `"mesh,http,grpc"` silently passed validate and landed as a
10064        // single malformed search tag in the rendered Chart.yaml
10065        // `keywords:` array — Artifact Hub's keyword index would
10066        // either silently drop the tag or index it as
10067        // `mesh,http,grpc` instead of three separate tags.
10068        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
10069        let err = c.validate_etiquetas().unwrap_err();
10070        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10071            panic!("expected EtiquetaInvalid, got {err:?}");
10072        };
10073        assert_eq!(etiqueta, "mesh,http,grpc");
10074        assert!(reason.contains('`'), "got: {reason}");
10075        assert!(reason.contains(','), "got: {reason}");
10076    }
10077
10078    #[test]
10079    fn validate_etiquetas_rejects_embedded_slash_entry() {
10080        // Canonical path-separator-confusion footgun: the author
10081        // confused namespace-path notation with the keyword grammar.
10082        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
10083        let err = c.validate_etiquetas().unwrap_err();
10084        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10085            panic!("expected EtiquetaInvalid, got {err:?}");
10086        };
10087        assert_eq!(etiqueta, "caixa/servico");
10088        assert!(reason.contains('/'), "got: {reason}");
10089    }
10090
10091    #[test]
10092    fn validate_etiquetas_rejects_leading_digit_entry() {
10093        // Canonical paste-from-numbered-list footgun: the author
10094        // copied `1. mesh` from a numbered doc and the `1` leaked
10095        // into the tag.
10096        let c = caixa_with_etiquetas(vec!["1mesh"]);
10097        let err = c.validate_etiquetas().unwrap_err();
10098        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10099            panic!("expected EtiquetaInvalid, got {err:?}");
10100        };
10101        assert_eq!(etiqueta, "1mesh");
10102        assert!(reason.contains("digit"), "got: {reason}");
10103    }
10104
10105    #[test]
10106    fn validate_etiquetas_rejects_leading_hyphen_entry() {
10107        // Canonical kebab-leak footgun.
10108        let c = caixa_with_etiquetas(vec!["-foo"]);
10109        let err = c.validate_etiquetas().unwrap_err();
10110        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10111            panic!("expected EtiquetaInvalid, got {err:?}");
10112        };
10113        assert_eq!(etiqueta, "-foo");
10114        assert!(reason.contains('-'), "got: {reason}");
10115    }
10116
10117    #[test]
10118    fn validate_etiquetas_rejects_non_ascii_entry() {
10119        // Canonical paste-from-Unicode-doc footgun. Every legitimate
10120        // search tag is strict ASCII; raw non-ASCII silently
10121        // round-trips inconsistently across NFC/NFD normalization on
10122        // APFS / case-folding filesystems and breaks the Artifact Hub
10123        // keyword search index lookup.
10124        let c = caixa_with_etiquetas(vec!["café"]);
10125        let err = c.validate_etiquetas().unwrap_err();
10126        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10127            panic!("expected EtiquetaInvalid, got {err:?}");
10128        };
10129        assert_eq!(etiqueta, "café");
10130        assert!(reason.contains("non-ASCII"), "got: {reason}");
10131    }
10132
10133    #[test]
10134    fn validate_etiquetas_rejects_period_entry() {
10135        // Canonical namespace-confusion / version-suffix footgun
10136        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
10137        // excludes `.` from the continuation set even though the
10138        // sibling `:caracteristicas` axis (Cargo's feature-name
10139        // grammar) admits it. Tighter than the sibling axis, peer
10140        // with Cargo's own crates.io keyword shape.
10141        let c = caixa_with_etiquetas(vec!["http.1"]);
10142        let err = c.validate_etiquetas().unwrap_err();
10143        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10144            panic!("expected EtiquetaInvalid, got {err:?}");
10145        };
10146        assert_eq!(etiqueta, "http.1");
10147        assert!(reason.contains('.'), "got: {reason}");
10148    }
10149
10150    #[test]
10151    fn validate_etiquetas_empty_takes_precedence_over_shape() {
10152        // Per-entry empty-first cascade pin: an entry that is both
10153        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
10154        // narrower "this entry has no value" structural defect
10155        // dominates the broader shape-predicate diagnostic). The
10156        // empty arm fires before the shape predicate is consulted,
10157        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
10158        // cascade established on the sibling universal-axis Vec<String>
10159        // surface.
10160        let c = caixa_with_etiquetas(vec![""]);
10161        let err = c.validate_etiquetas().unwrap_err();
10162        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
10163    }
10164
10165    #[test]
10166    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
10167        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
10168        // entry that is malformed surfaces `EtiquetaInvalid` even when
10169        // a later entry would have collided on duplicate. The
10170        // per-entry shape arm fires inside the same loop iteration as
10171        // the empty arm, before the seen-set insert at end-of-iteration
10172        // — structural per-entry defects dominate the cross-entry
10173        // uniqueness diagnostic. Mirrors the peer
10174        // `validate_autores_shape_takes_precedence_over_duplicate`.
10175        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
10176        let err = c.validate_etiquetas().unwrap_err();
10177        assert!(
10178            matches!(err, ManifestError::EtiquetaInvalid { .. }),
10179            "got {err:?}",
10180        );
10181    }
10182
10183    #[test]
10184    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
10185        // Diagnostic-shape pin on the new shape arm (peer with
10186        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
10187        // the rendered Display surfaces both the offending slot name
10188        // and the offending value verbatim, so a `feira lint` run
10189        // points the author at the exact `:etiquetas` entry to fix.
10190        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
10191        let rendered = c.validate_etiquetas().unwrap_err().to_string();
10192        assert!(
10193            rendered.contains(":etiquetas"),
10194            "diagnostic must name the offending slot: {rendered}",
10195        );
10196        assert!(
10197            rendered.contains("mesh\\nhttp"),
10198            "diagnostic must quote the offending value (debug-escaped): {rendered}",
10199        );
10200    }
10201
10202    #[test]
10203    fn validate_etiquetas_rejects_at_21_byte_boundary() {
10204        // The 20-byte cap pin — boundary-exceeding case rejected,
10205        // boundary-accepting case passes. Mirrors the peer
10206        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
10207        // side pin, surfaced at the per-axis caller so the cap
10208        // propagates through validate end-to-end. Constructed as a
10209        // single all-`a` token so only the cap arm fires.
10210        let max_ok = "a".repeat(20);
10211        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
10212        c.validate_etiquetas().unwrap();
10213        let too_long = "a".repeat(21);
10214        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
10215        let err = c.validate_etiquetas().unwrap_err();
10216        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
10217            panic!("expected EtiquetaInvalid, got {err:?}");
10218        };
10219        assert!(reason.contains("20"), "got: {reason}");
10220        assert!(reason.contains("21"), "got: {reason}");
10221    }
10222
10223    #[test]
10224    fn validate_etiquetas_accepts_canonical_shaped_forms() {
10225        // Positive control sweep: every canonical-shaped tag from the
10226        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
10227        // example fixtures plus the substrate-fixed tags caixa-helm
10228        // unions in at chart render. Drift between this list and the
10229        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
10230        // sweep surfaces here — one source of truth for the rule.
10231        let c = caixa_with_etiquetas(vec![
10232            "example",
10233            "aplicacao",
10234            "mesh",
10235            "ecommerce",
10236            "demo",
10237            "infrastructure",
10238            "aws",
10239            "akeyless",
10240            "pangea-native",
10241            "hello-world",
10242            "wasm",
10243            "rust",
10244            "tatara-lisp",
10245            "caixa-servico",
10246            "lareira",
10247        ]);
10248        c.validate_etiquetas().unwrap();
10249    }
10250
10251    // ── validate_autores — universal-axis maintainer shape ────────────
10252
10253    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
10254        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10255        c.autores = autores.into_iter().map(String::from).collect();
10256        c
10257    }
10258
10259    #[test]
10260    fn validate_autores_accepts_empty_list() {
10261        // The empty-list identity: `Caixa::template` emits `:autores ()`,
10262        // so the gate is non-disruptive against every existing manifest.
10263        let c = caixa_with_autores(vec![]);
10264        c.validate_autores().unwrap();
10265    }
10266
10267    #[test]
10268    fn validate_autores_accepts_canonical_forms() {
10269        // Positive control sweep: every canonical-shaped non-empty
10270        // distinct maintainer list passes — the hello-rio / checkout-
10271        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
10272        // multi-author shape downstream packaging surfaces emit.
10273        let c = caixa_with_autores(vec!["pleme-io"]);
10274        c.validate_autores().unwrap();
10275        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
10276        c.validate_autores().unwrap();
10277    }
10278
10279    #[test]
10280    fn validate_autores_rejects_empty_entry() {
10281        // Canonical paste-from-blank-doc footgun. Without the gate the
10282        // empty entry rendered as `maintainers: [{name: "", email: null}]`
10283        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
10284        // to.
10285        let c = caixa_with_autores(vec![""]);
10286        let err = c.validate_autores().unwrap_err();
10287        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
10288    }
10289
10290    #[test]
10291    fn validate_autores_rejects_duplicate_entry() {
10292        // Canonical copy-paste-the-wrong-author footgun. Unlike the
10293        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
10294        // dedups the rendered `keywords:` array), the `maintainers:`
10295        // rendering has *no* dedup — duplicates stack verbatim. The
10296        // duplicate-arm names the offending author verbatim.
10297        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
10298        let err = c.validate_autores().unwrap_err();
10299        let ManifestError::AutorDuplicate { autor } = err else {
10300            panic!("expected AutorDuplicate, got {err:?}");
10301        };
10302        assert_eq!(autor, "pleme-io");
10303    }
10304
10305    #[test]
10306    fn validate_autores_empty_takes_precedence_over_duplicate() {
10307        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
10308        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
10309        // "this entry has no value" defect dominates the cross-entry
10310        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
10311        // cascades on `:etiquetas` (`EtiquetaEmpty` before
10312        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
10313        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
10314        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
10315        // `MembroDuplicate`).
10316        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
10317        let err = c.validate_autores().unwrap_err();
10318        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
10319    }
10320
10321    #[test]
10322    fn validate_autores_duplicate_reports_first_collision() {
10323        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
10324        // duplicate (the lexicographically-earliest offending position
10325        // — the second `"a"` at index 2 collides with the first `"a"`
10326        // at index 0), not the later `"b"` collision at index 3,
10327        // peer with every other first-collision diagnostic posture on
10328        // this surface.
10329        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
10330        let err = c.validate_autores().unwrap_err();
10331        let ManifestError::AutorDuplicate { autor } = err else {
10332            panic!("expected AutorDuplicate, got {err:?}");
10333        };
10334        assert_eq!(autor, "a");
10335    }
10336
10337    #[test]
10338    fn validate_autores_case_sensitive() {
10339        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
10340        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
10341        // / `:children :caixa` exact-string-match discipline.
10342        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
10343        c.validate_autores().unwrap();
10344    }
10345
10346    #[test]
10347    fn validate_autores_diagnostic_carries_offending_author() {
10348        // Diagnostic-shape pin (peer with
10349        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
10350        // error's Display surfaces the offending author verbatim, so a
10351        // `feira lint` run can render the diagnostic without re-parsing
10352        // and the author can grep their caixa.lisp for the offending
10353        // value.
10354        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
10355        let rendered = c.validate_autores().unwrap_err().to_string();
10356        assert!(
10357            rendered.contains(":autores"),
10358            "diagnostic must name the offending slot: {rendered}",
10359        );
10360        assert!(
10361            rendered.contains("pleme-io"),
10362            "diagnostic must quote the offending author: {rendered}",
10363        );
10364    }
10365
10366    #[test]
10367    fn validate_autores_rejects_leading_whitespace_entry() {
10368        // Canonical paste-from-aligned-doc footgun. Without the shape
10369        // gate `" pleme-io"` silently passed validate and landed as a
10370        // YAML plain-style scalar with leading whitespace in the
10371        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
10372        // dumper trims leading whitespace from plain-style scalars, so
10373        // the authored space round-tripped inconsistently back through
10374        // `caixa.lisp`. Mirrors the peer
10375        // `validate_descricao_rejects_leading_whitespace`.
10376        let c = caixa_with_autores(vec![" pleme-io"]);
10377        let err = c.validate_autores().unwrap_err();
10378        let ManifestError::AutorInvalid { autor, reason } = err else {
10379            panic!("expected AutorInvalid, got {err:?}");
10380        };
10381        assert_eq!(autor, " pleme-io");
10382        assert!(reason.contains("whitespace"), "got: {reason}");
10383    }
10384
10385    #[test]
10386    fn validate_autores_rejects_trailing_whitespace_entry() {
10387        // Canonical paste-from-doc footgun.
10388        let c = caixa_with_autores(vec!["pleme-io "]);
10389        let err = c.validate_autores().unwrap_err();
10390        let ManifestError::AutorInvalid { autor, reason } = err else {
10391            panic!("expected AutorInvalid, got {err:?}");
10392        };
10393        assert_eq!(autor, "pleme-io ");
10394        assert!(reason.contains("whitespace"), "got: {reason}");
10395    }
10396
10397    #[test]
10398    fn validate_autores_rejects_embedded_newline_entry() {
10399        // Canonical paste-from-multiline-doc footgun — the author
10400        // pasted a multi-line block of author records into one
10401        // `:autores` entry instead of splitting into one entry per
10402        // author. Without the shape gate `"alice\nbob"` silently
10403        // passed validate and landed as a YAML-illegal multi-line
10404        // scalar in the rendered Chart.yaml `maintainers:` array.
10405        let c = caixa_with_autores(vec!["alice\nbob"]);
10406        let err = c.validate_autores().unwrap_err();
10407        let ManifestError::AutorInvalid { autor, reason } = err else {
10408            panic!("expected AutorInvalid, got {err:?}");
10409        };
10410        assert_eq!(autor, "alice\nbob");
10411        assert!(reason.contains("newline"), "got: {reason}");
10412    }
10413
10414    #[test]
10415    fn validate_autores_rejects_embedded_carriage_return_entry() {
10416        // Canonical paste-from-Windows-CRLF-doc footgun.
10417        let c = caixa_with_autores(vec!["alice\rbob"]);
10418        let err = c.validate_autores().unwrap_err();
10419        let ManifestError::AutorInvalid { autor, reason } = err else {
10420            panic!("expected AutorInvalid, got {err:?}");
10421        };
10422        assert_eq!(autor, "alice\rbob");
10423        assert!(reason.contains("carriage return"), "got: {reason}");
10424    }
10425
10426    #[test]
10427    fn validate_autores_rejects_embedded_tab_entry() {
10428        // Canonical tab-from-aligned-doc footgun.
10429        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
10430        let err = c.validate_autores().unwrap_err();
10431        let ManifestError::AutorInvalid { autor, reason } = err else {
10432            panic!("expected AutorInvalid, got {err:?}");
10433        };
10434        assert_eq!(autor, "Pleme\tContributors");
10435        assert!(reason.contains("tab"), "got: {reason}");
10436    }
10437
10438    #[test]
10439    fn validate_autores_rejects_embedded_control_bytes_entry() {
10440        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
10441        // surface the same control-byte arm.
10442        for entry in [
10443            "alice\x00bob",
10444            "alice\x07bob",
10445            "alice\x1bbob",
10446            "alice\x7fbob",
10447        ] {
10448            let c = caixa_with_autores(vec![entry]);
10449            let err = c.validate_autores().unwrap_err();
10450            let ManifestError::AutorInvalid { autor, reason } = err else {
10451                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
10452            };
10453            assert_eq!(autor, entry);
10454            assert!(
10455                reason.contains("control character"),
10456                "{entry:?} reason: {reason}",
10457            );
10458        }
10459    }
10460
10461    #[test]
10462    fn validate_autores_accepts_unicode_entry() {
10463        // Unicode positive control: realistic maintainer names carry
10464        // Unicode (`François`, `日本語`, `naïve`). The predicate must
10465        // round-trip Unicode losslessly, peer with the
10466        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
10467        // sweep.
10468        let c = caixa_with_autores(vec![
10469            "François Dupont",
10470            "日本語の名前",
10471            "naïve <naive@example.com>",
10472        ]);
10473        c.validate_autores().unwrap();
10474    }
10475
10476    #[test]
10477    fn validate_autores_empty_takes_precedence_over_shape() {
10478        // Per-entry empty-first cascade pin: an entry that is both
10479        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
10480        // "this entry has no value" structural defect dominates the
10481        // broader shape-predicate diagnostic). The empty arm fires
10482        // before the shape predicate is consulted, mirroring the peer
10483        // `validate_repositorio_empty_takes_precedence_over_shape`
10484        // cascade on the universal `Option<String>` siblings — and now
10485        // established on the Vec<String> per-entry surface.
10486        let c = caixa_with_autores(vec![""]);
10487        let err = c.validate_autores().unwrap_err();
10488        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
10489    }
10490
10491    #[test]
10492    fn validate_autores_shape_takes_precedence_over_duplicate() {
10493        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
10494        // entry that is malformed surfaces `AutorInvalid` even when a
10495        // later entry would have collided on duplicate. The per-entry
10496        // shape arm fires inside the same loop iteration as the empty
10497        // arm, before the seen-set insert at end-of-iteration —
10498        // structural per-entry defects dominate the cross-entry
10499        // uniqueness diagnostic.
10500        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
10501        let err = c.validate_autores().unwrap_err();
10502        assert!(
10503            matches!(err, ManifestError::AutorInvalid { .. }),
10504            "got {err:?}",
10505        );
10506    }
10507
10508    #[test]
10509    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
10510        // Diagnostic-shape pin on the new shape arm (peer with
10511        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
10512        // the rendered Display surfaces both the offending slot name
10513        // and the offending value verbatim, so a `feira lint` run
10514        // points the author at the exact `:autores` entry to fix.
10515        let c = caixa_with_autores(vec!["alice\nbob"]);
10516        let rendered = c.validate_autores().unwrap_err().to_string();
10517        assert!(
10518            rendered.contains(":autores"),
10519            "diagnostic must name the offending slot: {rendered}",
10520        );
10521        assert!(
10522            rendered.contains("alice\\nbob"),
10523            "diagnostic must quote the offending value (debug-escaped): {rendered}",
10524        );
10525    }
10526
10527    #[test]
10528    fn validate_autores_rejects_at_129_byte_boundary() {
10529        // The 128-byte cap pin — boundary-exceeding case rejected,
10530        // boundary-accepting case passes. Mirrors the peer
10531        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
10532        // substrate-side pin, surfaced at the per-axis caller so the
10533        // cap propagates through validate end-to-end. Constructed as
10534        // a single all-`a` token so only the cap arm fires.
10535        let max_ok = "a".repeat(128);
10536        let c = caixa_with_autores(vec![max_ok.as_str()]);
10537        c.validate_autores().unwrap();
10538        let too_long = "a".repeat(129);
10539        let c = caixa_with_autores(vec![too_long.as_str()]);
10540        let err = c.validate_autores().unwrap_err();
10541        let ManifestError::AutorInvalid { reason, .. } = err else {
10542            panic!("expected AutorInvalid, got {err:?}");
10543        };
10544        assert!(reason.contains("128"), "got: {reason}");
10545        assert!(reason.contains("129"), "got: {reason}");
10546    }
10547
10548    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
10549
10550    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
10551        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10552        c.repositorio = repositorio.map(String::from);
10553        c
10554    }
10555
10556    #[test]
10557    fn validate_repositorio_accepts_none() {
10558        // The omit-the-slot identity: `:repositorio` is optional. The
10559        // gate is a no-op when the author didn't declare a value —
10560        // every caixa without a `:repositorio` line trivially passes,
10561        // and the substrate-side renderers fall back to their
10562        // documented placeholder (`caixa-helm`'s `home: None`,
10563        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
10564        // URL). Mirrors the peer `validate_restart_window_accepts_none`
10565        // posture on the other `Option<String>` Caixa slot.
10566        let c = caixa_with_repositorio(None);
10567        c.validate_repositorio().unwrap();
10568    }
10569
10570    #[test]
10571    fn validate_repositorio_accepts_canonical_forms() {
10572        // Positive control sweep across every documented `:repositorio`
10573        // authoring shape — the same union the shared
10574        // `crate::render::is_git_repo_url` predicate accepts and the
10575        // peer `:deps :fonte :repo` axis already routes through.
10576        // Covers the `github:` shorthand (the canonical pleme-io
10577        // convention used in the `:repositorio` field of every
10578        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
10579        // `examples/`), the `https://…` URL the README quickstart uses,
10580        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
10581        // `file://` URL schemes the shared predicate documents.
10582        for repo in [
10583            "github:pleme-io/hello-rio",
10584            "github:pleme-io/checkout",
10585            "https://github.com/pleme-io/hello-rio",
10586            "ssh://git@github.com/pleme-io/hello-rio.git",
10587            "git://github.com/pleme-io/hello-rio.git",
10588            "git@github.com:pleme-io/hello-rio.git",
10589            "file:///srv/pleme/hello-rio",
10590        ] {
10591            let c = caixa_with_repositorio(Some(repo));
10592            c.validate_repositorio()
10593                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
10594        }
10595    }
10596
10597    #[test]
10598    fn validate_repositorio_rejects_empty_some() {
10599        // Canonical paste-from-blank-doc footgun. The narrower
10600        // [`ManifestError::RepositorioEmpty`] arm fires before the
10601        // shape predicate is consulted, mirroring the empty-first
10602        // cascade every peer per-axis identity gate uses
10603        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
10604        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
10605        // the empty `Some("")` silently passed the renderer's
10606        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
10607        // on `None`) and landed as `home: ""` in `Chart.yaml` /
10608        // `url: ""` in the FluxCD `GitRepository`.
10609        let c = caixa_with_repositorio(Some(""));
10610        let err = c.validate_repositorio().unwrap_err();
10611        assert!(
10612            matches!(err, ManifestError::RepositorioEmpty),
10613            "got {err:?}",
10614        );
10615    }
10616
10617    #[test]
10618    fn validate_repositorio_rejects_whitespace() {
10619        // Paste-from-doc whitespace footgun. The shared
10620        // `is_git_repo_url` predicate refuses any whitespace byte; a
10621        // trailing space in a `:repositorio` value silently broke
10622        // `git clone '<value> '` at clone time. The diagnostic names
10623        // the offending value verbatim.
10624        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
10625        let err = c.validate_repositorio().unwrap_err();
10626        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
10627            panic!("expected RepositorioInvalid, got {err:?}");
10628        };
10629        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
10630    }
10631
10632    #[test]
10633    fn validate_repositorio_rejects_control_char() {
10634        // Paste-from-multiline-doc CRLF footgun — control characters
10635        // at the URL boundary are a class of subprocess-arg injection
10636        // and break git's URL parser at every porcelain entry point.
10637        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
10638        let err = c.validate_repositorio().unwrap_err();
10639        assert!(
10640            matches!(err, ManifestError::RepositorioInvalid { .. }),
10641            "got {err:?}",
10642        );
10643    }
10644
10645    #[test]
10646    fn validate_repositorio_rejects_leading_dash() {
10647        // Canonical CLI-argument-injection footgun: `git clone <repo>`
10648        // interprets a leading `-` as a CLI flag, so a
10649        // `-upload-pack=…` value escapes the subprocess argument
10650        // boundary. The shared predicate refuses every leading-`-`
10651        // shape at validate time.
10652        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
10653        let err = c.validate_repositorio().unwrap_err();
10654        assert!(
10655            matches!(err, ManifestError::RepositorioInvalid { .. }),
10656            "got {err:?}",
10657        );
10658    }
10659
10660    #[test]
10661    fn validate_repositorio_rejects_missing_colon_separator() {
10662        // The bare `org/repo` ambiguity footgun — `git clone` reads
10663        // a no-`:` form as a relative filesystem path rather than the
10664        // GitHub-shorthand expansion the author probably intended.
10665        // The shared predicate refuses every shape without a `:`
10666        // separator.
10667        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
10668        let err = c.validate_repositorio().unwrap_err();
10669        assert!(
10670            matches!(err, ManifestError::RepositorioInvalid { .. }),
10671            "got {err:?}",
10672        );
10673    }
10674
10675    #[test]
10676    fn validate_repositorio_rejects_fragment_anchor() {
10677        // Paste-from-browser-address-bar footgun on the
10678        // `:repositorio` axis — an author copies a GitHub permalink
10679        // to a README section / line-permalink and forgets to trim
10680        // the `#fragment` tail. The shared `is_git_repo_url`
10681        // predicate refuses the byte at the URL-grammar layer
10682        // (libcurl strips the fragment before opening the
10683        // transport, so the byte rides verbatim into the rendered
10684        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
10685        // fields but is silently dropped on the wire — two
10686        // manifest variants whose values differ only in their
10687        // fragment anchor lock to two distinct rendered artifacts
10688        // for the byte-identical clone, defeating the THEORY.md
10689        // §V.2 render-determinism contract on the `:repositorio`
10690        // axis the peer `:fonte :repo` axis already closes).
10691        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
10692        let err = c.validate_repositorio().unwrap_err();
10693        let ManifestError::RepositorioInvalid {
10694            repositorio,
10695            reason,
10696        } = err
10697        else {
10698            panic!("expected RepositorioInvalid, got {err:?}");
10699        };
10700        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
10701        assert!(
10702            reason.contains("must not contain `#`"),
10703            "reason must surface the fragment-`#` arm, got {reason:?}"
10704        );
10705    }
10706
10707    #[test]
10708    fn validate_repositorio_rejects_query_string() {
10709        // Paste-from-browser-address-bar footgun on the
10710        // `:repositorio` axis (peer with the a68f818 fragment-`#`
10711        // arm on the same axis). An author copies a GitHub tab
10712        // deep-link out of the address bar and forgets to trim
10713        // the `?tab=…` query tail. The shared `is_git_repo_url`
10714        // predicate refuses the byte at the URL-grammar layer
10715        // (GitHub / GitLab / Bitbucket silently ignore the
10716        // `?query` tail and serve the same repo regardless, so
10717        // the byte rides verbatim into the rendered `Chart.yaml`
10718        // `home:` and FluxCD `GitRepository` `url:` fields but
10719        // is silently masked at the wire — two manifest variants
10720        // whose values differ only in their query tail lock to
10721        // two distinct rendered artifacts for the byte-identical
10722        // clone, defeating the THEORY.md §V.2 render-determinism
10723        // contract on the `:repositorio` axis the peer `:fonte
10724        // :repo` axis already closes).
10725        let c = caixa_with_repositorio(Some(
10726            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
10727        ));
10728        let err = c.validate_repositorio().unwrap_err();
10729        let ManifestError::RepositorioInvalid {
10730            repositorio,
10731            reason,
10732        } = err
10733        else {
10734            panic!("expected RepositorioInvalid, got {err:?}");
10735        };
10736        assert_eq!(
10737            repositorio,
10738            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
10739        );
10740        assert!(
10741            reason.contains("must not contain `?`"),
10742            "reason must surface the query-`?` arm, got {reason:?}"
10743        );
10744    }
10745
10746    #[test]
10747    fn validate_repositorio_rejects_embedded_backslash() {
10748        // Windows-file-path-confusion footgun on the `:repositorio`
10749        // axis (peer with the prior fragment-`#` / query-`?` arms on
10750        // the same axis, and peer with the new dep-level `:fonte :repo`
10751        // backslash arm on the URL-grammar trajectory). An author
10752        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
10753        // hello-rio` into the `:repositorio` slot, expecting the
10754        // `lareira-<nome>` chart's `home:` field and the FluxCD
10755        // `GitRepository` `url:` field to render the canonical local
10756        // file-URI. The shared `is_git_repo_url` predicate refuses
10757        // the byte at the URL-grammar layer (libcurl silently
10758        // translates `\` → `/` on some platforms and refuses it on
10759        // others, so the byte rides verbatim into the rendered
10760        // artifacts but is silently rewritten or rejected at the wire
10761        // — two manifest variants whose values differ only in
10762        // backslash-vs-forward-slash lock to two distinct rendered
10763        // artifacts for the byte-identical clone, defeating the
10764        // THEORY.md §V.2 render-determinism contract on the
10765        // `:repositorio` axis the peer `:fonte :repo` axis already
10766        // closes).
10767        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
10768        let err = c.validate_repositorio().unwrap_err();
10769        let ManifestError::RepositorioInvalid {
10770            repositorio,
10771            reason,
10772        } = err
10773        else {
10774            panic!("expected RepositorioInvalid, got {err:?}");
10775        };
10776        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
10777        assert!(
10778            reason.contains("must not contain `\\`"),
10779            "reason must surface the backslash-`\\` arm, got {reason:?}"
10780        );
10781    }
10782
10783    #[test]
10784    fn validate_repositorio_rejects_uri_template_placeholder() {
10785        // URI Template (RFC 6570) placeholder footgun on the
10786        // `:repositorio` axis (peer with the prior fragment-`#` /
10787        // query-`?` / backslash-`\` arms on the same axis, and peer
10788        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
10789        // URL-grammar trajectory). An author pastes a quick-start
10790        // README snippet / OpenAPI `servers:` URL / Helm chart
10791        // `home:` template carrying unresolved `{org}` / `{repo}`
10792        // placeholders into the `:repositorio` slot, expecting the
10793        // substrate to resolve the placeholder downstream. The
10794        // shared `is_git_repo_url` predicate refuses the byte at the
10795        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
10796        // `%7B` / `%7D` on the wire, so the byte round-trips
10797        // inconsistently between the rendered `Chart.yaml home:` /
10798        // FluxCD `GitRepository url:` and the resolver's `git clone`
10799        // invocation, defeating the THEORY.md §V.2 render-
10800        // determinism contract on the `:repositorio` axis the peer
10801        // `:fonte :repo` axis already closes; every git porcelain
10802        // entry-point additionally fetches a nonexistent literal-
10803        // `{placeholder}`-named path far from the source caixa.lisp).
10804        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
10805        let err = c.validate_repositorio().unwrap_err();
10806        let ManifestError::RepositorioInvalid {
10807            repositorio,
10808            reason,
10809        } = err
10810        else {
10811            panic!("expected RepositorioInvalid, got {err:?}");
10812        };
10813        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
10814        assert!(
10815            reason.contains("must not contain `{`"),
10816            "reason must surface the open-brace `{{` arm, got {reason:?}"
10817        );
10818        assert!(
10819            reason.contains("URI Template") || reason.contains("RFC 6570"),
10820            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
10821        );
10822    }
10823
10824    #[test]
10825    fn validate_repositorio_empty_takes_precedence_over_shape() {
10826        // Empty-first cascade pin: the empty `Some("")` surfaces the
10827        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
10828        // `RepositorioInvalid`, mirroring the peer
10829        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
10830        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
10831        // `is_git_repo_url` predicate also rejects the empty input
10832        // (defensively, with its own `"must not be empty"` reason),
10833        // but the manifest-layer empty arm runs first to surface the
10834        // narrower diagnostic verbatim.
10835        let c = caixa_with_repositorio(Some(""));
10836        let err = c.validate_repositorio().unwrap_err();
10837        assert!(
10838            matches!(err, ManifestError::RepositorioEmpty),
10839            "got {err:?}",
10840        );
10841    }
10842
10843    #[test]
10844    fn validate_repositorio_diagnostic_carries_offending_value() {
10845        // Diagnostic-shape pin (peer with
10846        // `validate_autores_diagnostic_carries_offending_author`): the
10847        // error's Display surfaces the offending value + slot name
10848        // verbatim, so a `feira lint` run can render the diagnostic
10849        // without re-parsing and the author can grep their caixa.lisp
10850        // for the offending `:repositorio` value.
10851        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
10852        let rendered = c.validate_repositorio().unwrap_err().to_string();
10853        assert!(
10854            rendered.contains(":repositorio"),
10855            "diagnostic must name the offending slot: {rendered}",
10856        );
10857        assert!(
10858            rendered.contains("pleme-io/hello-rio"),
10859            "diagnostic must quote the offending value: {rendered}",
10860        );
10861    }
10862
10863    // ── validate_descricao — universal-axis Chart.yaml description shape ──
10864
10865    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
10866        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10867        c.descricao = descricao.map(String::from);
10868        c
10869    }
10870
10871    #[test]
10872    fn validate_descricao_accepts_none() {
10873        // The omit-the-slot identity: `:descricao` is optional. The
10874        // gate is a no-op when the author didn't declare a value —
10875        // every caixa without a `:descricao` line trivially passes,
10876        // and the substrate-side renderers fall back to their
10877        // documented `caixa.nome`-derived placeholder. Mirrors the
10878        // peer `validate_repositorio_accepts_none` posture on the
10879        // sibling `Option<String>` Caixa slot.
10880        let c = caixa_with_descricao(None);
10881        c.validate_descricao().unwrap();
10882    }
10883
10884    #[test]
10885    fn validate_descricao_accepts_canonical_summary() {
10886        // Positive control: the canonical pleme-io descricao shape —
10887        // a short free-form prose summary — passes the gate. Covers
10888        // the fixture shapes the `caixa-helm` / `caixa-flux` /
10889        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
10890        // wasip2 caixa Servico."`, `"Checkout flow."`).
10891        for desc in [
10892            "Canonical Rust→wasm32-wasip2 caixa Servico.",
10893            "Checkout flow.",
10894            "AWS provider caixa for tatara-lisp",
10895            "FIXME — describe this caixa",
10896            "x",
10897        ] {
10898            let c = caixa_with_descricao(Some(desc));
10899            c.validate_descricao()
10900                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
10901        }
10902    }
10903
10904    #[test]
10905    fn validate_descricao_rejects_empty_some() {
10906        // Canonical paste-from-blank-doc footgun. Without this gate
10907        // the empty `Some("")` silently passed the renderer's
10908        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
10909        // on `None`) and landed as `description: ""` in `Chart.yaml`
10910        // and a blank `README.md` header. Mirrors the peer
10911        // [`ManifestError::RepositorioEmpty`] empty-arm on the
10912        // sibling `Option<String>` Caixa slot.
10913        let c = caixa_with_descricao(Some(""));
10914        let err = c.validate_descricao().unwrap_err();
10915        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
10916    }
10917
10918    #[test]
10919    fn validate_descricao_rejects_leading_whitespace() {
10920        // Paste-from-aligned-doc footgun: a leading ASCII space the
10921        // bare empty-arm gate accepted, the shape predicate now
10922        // refuses. The diagnostic carries the offending value
10923        // verbatim (with the leading space preserved) so the author
10924        // can grep their caixa.lisp for the exact `:descricao` line
10925        // and fix the round-trip-inconsistent leading whitespace.
10926        // Mirrors the peer
10927        // `validate_licenca_rejects_leading_whitespace` arm on the
10928        // sibling `:licenca` axis.
10929        let c = caixa_with_descricao(Some(" Checkout flow."));
10930        let err = c.validate_descricao().unwrap_err();
10931        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
10932            panic!("expected DescricaoInvalid, got {err:?}");
10933        };
10934        assert_eq!(descricao, " Checkout flow.");
10935        assert!(reason.contains("whitespace"), "got: {reason:?}");
10936    }
10937
10938    #[test]
10939    fn validate_descricao_rejects_trailing_whitespace() {
10940        // Paste-from-doc footgun: a trailing ASCII space the bare
10941        // empty-arm gate accepted, the shape predicate now refuses.
10942        let c = caixa_with_descricao(Some("Checkout flow. "));
10943        let err = c.validate_descricao().unwrap_err();
10944        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
10945            panic!("expected DescricaoInvalid, got {err:?}");
10946        };
10947        assert_eq!(descricao, "Checkout flow. ");
10948        assert!(reason.contains("whitespace"), "got: {reason:?}");
10949    }
10950
10951    #[test]
10952    fn validate_descricao_rejects_embedded_newline() {
10953        // Paste-from-multiline-doc footgun: an embedded LF the bare
10954        // empty-arm gate accepted, the shape predicate now refuses.
10955        // Without this gate the embedded newline silently landed in
10956        // the rendered Chart.yaml as a multi-line YAML block scalar,
10957        // and every chart-aware UI (`helm list`, `helm search`,
10958        // Artifact Hub) renders the description in a single-line
10959        // column so the embedded newline is silently dropped at
10960        // every downstream consumer.
10961        let c = caixa_with_descricao(Some("Checkout\nflow."));
10962        let err = c.validate_descricao().unwrap_err();
10963        assert!(
10964            matches!(err, ManifestError::DescricaoInvalid { .. }),
10965            "got {err:?}",
10966        );
10967        assert!(err.to_string().contains("newline"), "got {err}");
10968    }
10969
10970    #[test]
10971    fn validate_descricao_rejects_embedded_carriage_return() {
10972        // Paste-from-Windows-CRLF-doc footgun.
10973        let c = caixa_with_descricao(Some("Checkout\rflow."));
10974        let err = c.validate_descricao().unwrap_err();
10975        assert!(
10976            matches!(err, ManifestError::DescricaoInvalid { .. }),
10977            "got {err:?}",
10978        );
10979        assert!(err.to_string().contains("carriage return"), "got {err}");
10980    }
10981
10982    #[test]
10983    fn validate_descricao_rejects_embedded_tab() {
10984        // Tab-from-aligned-doc footgun.
10985        let c = caixa_with_descricao(Some("Checkout\tflow."));
10986        let err = c.validate_descricao().unwrap_err();
10987        assert!(
10988            matches!(err, ManifestError::DescricaoInvalid { .. }),
10989            "got {err:?}",
10990        );
10991        assert!(err.to_string().contains("tab"), "got {err}");
10992    }
10993
10994    #[test]
10995    fn validate_descricao_rejects_embedded_control_bytes() {
10996        // Paste-from-binary-blob footgun: every other control byte
10997        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
10998        // the peer SPDX-expression control-byte arm.
10999        for s in [
11000            "Checkout\x00flow.",
11001            "Checkout\x07flow.",
11002            "Checkout\x1bflow.",
11003            "Checkout\x7fflow.",
11004        ] {
11005            let c = caixa_with_descricao(Some(s));
11006            let err = c.validate_descricao().unwrap_err();
11007            assert!(
11008                matches!(err, ManifestError::DescricaoInvalid { .. }),
11009                "{s:?} got {err:?}",
11010            );
11011            assert!(
11012                err.to_string().contains("control character"),
11013                "{s:?} got {err}",
11014            );
11015        }
11016    }
11017
11018    #[test]
11019    fn validate_descricao_accepts_unicode_prose() {
11020        // Positive control: Unicode prose is accepted — the
11021        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
11022        // and `Caixa::template`'s `"FIXME — describe this caixa"`
11023        // scaffold every `feira init` emits must continue to pass.
11024        for s in [
11025            "Canonical Rust→wasm32-wasip2 caixa Servico.",
11026            "FIXME — describe this caixa",
11027            "Caixa pour le projet tâche",
11028            "日本語の説明",
11029        ] {
11030            let c = caixa_with_descricao(Some(s));
11031            c.validate_descricao()
11032                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
11033        }
11034    }
11035
11036    #[test]
11037    fn validate_descricao_empty_takes_precedence_over_shape() {
11038        // Cascade pin: a `Some("")` surfaces the narrower
11039        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
11040        // shape-predicate arm. Mirrors the peer
11041        // `validate_licenca_empty_takes_precedence_over_shape` pin
11042        // on the sibling `:licenca` axis.
11043        let c = caixa_with_descricao(Some(""));
11044        let err = c.validate_descricao().unwrap_err();
11045        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
11046    }
11047
11048    #[test]
11049    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
11050        // Diagnostic-shape pin: the error's Display surfaces both
11051        // the `:descricao` slot name and the offending value
11052        // verbatim, so a `feira lint` run can render the diagnostic
11053        // without re-parsing and the author can grep their caixa.lisp
11054        // for the offending `:descricao` line. Mirrors the peer
11055        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
11056        // pin (ee2e888) on the sibling `:licenca` axis.
11057        // The `{descricao:?}` Debug format escapes embedded control
11058        // bytes; the quoted offending value surfaces as
11059        // `"Checkout\nflow."` (literal backslash-n) in the rendered
11060        // diagnostic. The author can grep their caixa.lisp for the
11061        // literal `Checkout` summary prefix.
11062        let c = caixa_with_descricao(Some("Checkout\nflow."));
11063        let rendered = c.validate_descricao().unwrap_err().to_string();
11064        assert!(
11065            rendered.contains(":descricao"),
11066            "diagnostic must name the offending slot: {rendered}",
11067        );
11068        assert!(
11069            rendered.contains("Checkout\\nflow."),
11070            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11071        );
11072    }
11073
11074    #[test]
11075    fn validate_descricao_template_passes() {
11076        // Round-trip pin: the bare `Caixa::template` shape carries
11077        // `:descricao "FIXME — describe this caixa"` (a non-empty
11078        // sentinel), so the template-derived Caixa passes the gate by
11079        // construction. A future template-shape change that omits or
11080        // empties `:descricao` would surface here as a regression.
11081        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11082        c.validate_descricao().unwrap();
11083    }
11084
11085    #[test]
11086    fn validate_descricao_diagnostic_names_offending_slot() {
11087        // Diagnostic-shape pin (peer with
11088        // `validate_repositorio_diagnostic_carries_offending_value`):
11089        // the error's Display surfaces the `:descricao` slot name
11090        // verbatim, so a `feira lint` run can render the diagnostic
11091        // without re-parsing and the author can grep their caixa.lisp
11092        // for the offending `:descricao` line.
11093        let c = caixa_with_descricao(Some(""));
11094        let rendered = c.validate_descricao().unwrap_err().to_string();
11095        assert!(
11096            rendered.contains(":descricao"),
11097            "diagnostic must name the offending slot: {rendered}",
11098        );
11099    }
11100
11101    // ── validate_licenca — universal-axis chart README license shape ──
11102
11103    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
11104        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11105        c.licenca = licenca.map(String::from);
11106        c
11107    }
11108
11109    #[test]
11110    fn validate_licenca_accepts_none() {
11111        // The omit-the-slot identity: `:licenca` is optional. The
11112        // gate is a no-op when the author didn't declare a value —
11113        // every caixa without a `:licenca` line trivially passes,
11114        // and the substrate-side `caixa-helm` renderer falls back to
11115        // the documented `"MIT"` placeholder. Mirrors the peer
11116        // `validate_descricao_accepts_none` posture on the sibling
11117        // `Option<String>` Caixa slot.
11118        let c = caixa_with_licenca(None);
11119        c.validate_licenca().unwrap();
11120    }
11121
11122    #[test]
11123    fn validate_licenca_accepts_canonical_expressions() {
11124        // Positive control: every canonical SPDX expression shape
11125        // pleme-io carries in its existing fixtures + the canonical
11126        // SPDX dual-license / with-exception / `+`-suffix / grouped /
11127        // user-defined-reference shapes all pass the gate. Covers
11128        // the single-license, `OR`-compound, `AND`-compound,
11129        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
11130        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
11131        // production the SPDX 2.1 expression grammar admits that
11132        // sits within the alphabet floor the
11133        // `is_spdx_expression_shape` predicate enforces.
11134        for lic in [
11135            "MIT",
11136            "Apache-2.0",
11137            "Apache-2.0 OR MIT",
11138            "Apache-2.0 AND MIT",
11139            "BSD-3-Clause",
11140            "MPL-2.0",
11141            "GPL-3.0-or-later",
11142            "GPL-2.0+",
11143            "Apache-2.0 WITH LLVM-exception",
11144            "(MIT OR Apache-2.0) AND BSD-3-Clause",
11145            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
11146            "LicenseRef-MyLicense",
11147            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
11148            "x",
11149        ] {
11150            let c = caixa_with_licenca(Some(lic));
11151            c.validate_licenca()
11152                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
11153        }
11154    }
11155
11156    #[test]
11157    fn validate_licenca_rejects_trailing_whitespace() {
11158        // Paste-from-doc whitespace footgun. A trailing space in the
11159        // `:licenca` value would silently break a downstream SPDX
11160        // parser that splits on exact `AND` / `OR` / `WITH` keyword
11161        // boundaries. The shape predicate refuses every trailing
11162        // whitespace byte by construction. Peer with
11163        // `validate_repositorio_rejects_whitespace` and
11164        // `validate_edicao_rejects_trailing_whitespace`.
11165        let c = caixa_with_licenca(Some("MIT "));
11166        let err = c.validate_licenca().unwrap_err();
11167        let ManifestError::LicencaInvalid { licenca, .. } = err else {
11168            panic!("expected LicencaInvalid, got {err:?}");
11169        };
11170        assert_eq!(licenca, "MIT ");
11171    }
11172
11173    #[test]
11174    fn validate_licenca_rejects_leading_whitespace() {
11175        // Symmetric paste-from-doc whitespace footgun on the leading
11176        // boundary — the gate refuses every shape that starts with a
11177        // space byte by construction. Peer with
11178        // `validate_edicao_rejects_leading_whitespace`.
11179        let c = caixa_with_licenca(Some(" MIT"));
11180        let err = c.validate_licenca().unwrap_err();
11181        assert!(
11182            matches!(err, ManifestError::LicencaInvalid { .. }),
11183            "got {err:?}",
11184        );
11185    }
11186
11187    #[test]
11188    fn validate_licenca_rejects_control_char() {
11189        // Paste-from-multiline-doc CRLF footgun — control characters
11190        // at the value boundary land as a malformed line in the
11191        // rendered chart `README.md` `## License` section. Peer with
11192        // `validate_repositorio_rejects_control_char` and
11193        // `validate_edicao_rejects_control_char`.
11194        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
11195            let c = caixa_with_licenca(Some(lic));
11196            let err = c.validate_licenca().unwrap_err();
11197            assert!(
11198                matches!(err, ManifestError::LicencaInvalid { .. }),
11199                "expected LicencaInvalid on {lic:?}, got {err:?}",
11200            );
11201        }
11202    }
11203
11204    #[test]
11205    fn validate_licenca_rejects_tab() {
11206        // Tab-from-aligned-doc footgun — SPDX expressions use a
11207        // single ASCII space between tokens; a tab breaks every
11208        // downstream SPDX parser that splits on exact `" "`
11209        // boundaries.
11210        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
11211        let err = c.validate_licenca().unwrap_err();
11212        assert!(
11213            matches!(err, ManifestError::LicencaInvalid { .. }),
11214            "got {err:?}",
11215        );
11216    }
11217
11218    #[test]
11219    fn validate_licenca_rejects_non_ascii() {
11220        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
11221        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
11222        // ".")` production. The shape predicate refuses every
11223        // non-ASCII byte by construction; peer with
11224        // `validate_edicao_rejects_non_ascii_lookalike`.
11225        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
11226            let c = caixa_with_licenca(Some(lic));
11227            let err = c.validate_licenca().unwrap_err();
11228            assert!(
11229                matches!(err, ManifestError::LicencaInvalid { .. }),
11230                "expected LicencaInvalid on {lic:?}, got {err:?}",
11231            );
11232        }
11233    }
11234
11235    #[test]
11236    fn validate_licenca_rejects_underscore() {
11237        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
11238        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
11239        // snake-case identifier conventions that don't apply to the
11240        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
11241        // "-" / "."`). The shape predicate refuses every underscore
11242        // byte by construction.
11243        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
11244            let c = caixa_with_licenca(Some(lic));
11245            let err = c.validate_licenca().unwrap_err();
11246            assert!(
11247                matches!(err, ManifestError::LicencaInvalid { .. }),
11248                "expected LicencaInvalid on {lic:?}, got {err:?}",
11249            );
11250        }
11251    }
11252
11253    #[test]
11254    fn validate_licenca_rejects_comma_separator() {
11255        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
11256        // SPDX expressions compose multiple licenses via `AND` / `OR`
11257        // keywords, not the comma separator. The shape predicate
11258        // refuses every comma byte by construction.
11259        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
11260            let c = caixa_with_licenca(Some(lic));
11261            let err = c.validate_licenca().unwrap_err();
11262            assert!(
11263                matches!(err, ManifestError::LicencaInvalid { .. }),
11264                "expected LicencaInvalid on {lic:?}, got {err:?}",
11265            );
11266        }
11267    }
11268
11269    #[test]
11270    fn validate_licenca_rejects_slash_dual_license() {
11271        // Slash-dual-license colloquial idiom footgun — the
11272        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
11273        // `package.license` field but non-SPDX; the SPDX equivalent
11274        // is `MIT OR Apache-2.0`. The shape predicate refuses every
11275        // forward-slash byte by construction.
11276        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
11277            let c = caixa_with_licenca(Some(lic));
11278            let err = c.validate_licenca().unwrap_err();
11279            assert!(
11280                matches!(err, ManifestError::LicencaInvalid { .. }),
11281                "expected LicencaInvalid on {lic:?}, got {err:?}",
11282            );
11283        }
11284    }
11285
11286    #[test]
11287    fn validate_licenca_rejects_semicolon_separator() {
11288        // Semicolon-list-separator confusion footgun — adjacent to
11289        // the comma-separator idiom, every list-separator-belongs-
11290        // to-list-grammar confusion lands here.
11291        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
11292        let err = c.validate_licenca().unwrap_err();
11293        assert!(
11294            matches!(err, ManifestError::LicencaInvalid { .. }),
11295            "got {err:?}",
11296        );
11297    }
11298
11299    #[test]
11300    fn validate_licenca_empty_takes_precedence_over_shape() {
11301        // Empty-first cascade pin: the empty `Some("")` surfaces the
11302        // narrower `LicencaEmpty` not the shape-predicate-wrapped
11303        // `LicencaInvalid`, mirroring the peer
11304        // `validate_edicao_empty_takes_precedence_over_shape` and
11305        // `validate_repositorio_empty_takes_precedence_over_shape`
11306        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
11307        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
11308        // The shape predicate also refuses the empty input
11309        // (defensively — `"must not be empty"`), but the manifest-
11310        // layer empty arm runs first to surface the narrower
11311        // diagnostic verbatim.
11312        let c = caixa_with_licenca(Some(""));
11313        let err = c.validate_licenca().unwrap_err();
11314        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
11315    }
11316
11317    #[test]
11318    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
11319        // Diagnostic-shape pin on the shape-predicate arm (peer with
11320        // `validate_edicao_invalid_diagnostic_carries_offending_value`
11321        // and `validate_repositorio_diagnostic_carries_offending_value`):
11322        // the error's Display surfaces the offending value + slot
11323        // name verbatim, so a `feira lint` run can render the
11324        // diagnostic without re-parsing and the author can grep
11325        // their caixa.lisp for the offending `:licenca` value.
11326        let c = caixa_with_licenca(Some("Apache_2.0"));
11327        let rendered = c.validate_licenca().unwrap_err().to_string();
11328        assert!(
11329            rendered.contains(":licenca"),
11330            "diagnostic must name the offending slot: {rendered}",
11331        );
11332        assert!(
11333            rendered.contains("Apache_2.0"),
11334            "diagnostic must quote the offending value: {rendered}",
11335        );
11336    }
11337
11338    #[test]
11339    fn validate_licenca_rejects_empty_some() {
11340        // Canonical paste-from-blank-doc footgun. Without this gate
11341        // the empty `Some("")` silently passed the renderer's
11342        // `Option::unwrap_or_else(|| "MIT".into())` (which only
11343        // fires on `None`) and landed as a bare trailing period in
11344        // the rendered chart `README.md` `## License` section.
11345        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
11346        // arm on the sibling `Option<String>` Caixa slot.
11347        let c = caixa_with_licenca(Some(""));
11348        let err = c.validate_licenca().unwrap_err();
11349        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
11350    }
11351
11352    #[test]
11353    fn validate_licenca_template_passes() {
11354        // Round-trip pin: the bare `Caixa::template` shape (whether
11355        // it carries `:licenca` or omits it) passes the gate by
11356        // construction. A future template-shape change that
11357        // introduced `(:licenca "")` would surface here as a
11358        // regression. Mirrors the peer
11359        // `validate_descricao_template_passes` pin.
11360        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11361        c.validate_licenca().unwrap();
11362    }
11363
11364    #[test]
11365    fn validate_licenca_diagnostic_names_offending_slot() {
11366        // Diagnostic-shape pin (peer with
11367        // `validate_descricao_diagnostic_names_offending_slot`):
11368        // the error's Display surfaces the `:licenca` slot name
11369        // verbatim, so a `feira lint` run can render the diagnostic
11370        // without re-parsing and the author can grep their caixa.lisp
11371        // for the offending `:licenca` line.
11372        let c = caixa_with_licenca(Some(""));
11373        let rendered = c.validate_licenca().unwrap_err().to_string();
11374        assert!(
11375            rendered.contains(":licenca"),
11376            "diagnostic must name the offending slot: {rendered}",
11377        );
11378    }
11379
11380    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
11381
11382    #[test]
11383    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
11384        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
11385        // pin: [`Caixa::licenca`] must return the `:licenca` typed
11386        // byte-string verbatim as an `Option<&str>`, byte-equal to the
11387        // raw `self.licenca.as_deref()` access across every
11388        // representative value in the accept-set — `None` (the "omit
11389        // the slot to defer to the caixa-helm renderer's `MIT`
11390        // fallback" arm every existing fixture without a `:licenca`
11391        // line carries), `Some("")` (a past-the-guard sentinel that
11392        // pins the accessor doesn't perform a silent
11393        // `Some("") → None` collapse on the empty arm — validate
11394        // rejects `Some("")` through `LicencaEmpty` but the accessor
11395        // must ship the raw slot verbatim so a validate-time gate
11396        // regression surfaces at the caixa-helm emit boundary rather
11397        // than being silently absorbed into the fallback), `Some("MIT")`
11398        // (the canonical single-license shape every `feira init`
11399        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
11400        // canonical `OR`-compound shape the peer
11401        // `validate_licenca_accepts_canonical_expressions` positive
11402        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
11403        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
11404        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
11405        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
11406        // guard sentinels — validate rejects each through
11407        // `LicencaInvalid` but the accessor must ship the raw slot
11408        // verbatim).
11409        //
11410        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
11411        // accessor pin on the substrate primitive — opens the "outer
11412        // [`Caixa`] `Option<&str>` scalar" projection pattern the
11413        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
11414        // future lifts fold on. Sibling in shape to the peer per-`:placement`
11415        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11416        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11417        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11418        // axes, extended onto the outer top-level [`Caixa`] universal-
11419        // axis surface. Pins against a future silent detour that
11420        // returned an owned `Option<String>` (which would type-check
11421        // but silently allocate on every accessor call, breaking the
11422        // zero-cost projection every peer sibling accessor carries), a
11423        // `Some("") → None` collapse (which would silently absorb the
11424        // `LicencaEmpty` refusal case at the accessor boundary and the
11425        // caixa-helm emit path would silently fall back to `"MIT"` on
11426        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
11427        // `None → Some("MIT")` collapse (which would silently reify
11428        // the caixa-helm renderer's `"MIT"` fallback at the accessor
11429        // boundary and every downstream consumer keying off the
11430        // `Option::is_none()` discriminator would lose the "author
11431        // omitted the slot" signal).
11432        for licenca in [
11433            None,
11434            Some(""),
11435            Some("MIT"),
11436            Some("Apache-2.0 OR MIT"),
11437            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
11438            Some("MIT "),
11439            Some(" MIT"),
11440            Some("MIT\n"),
11441            Some("Apache_2.0"),
11442            Some("MIT,Apache-2.0"),
11443        ] {
11444            let c = caixa_with_licenca(licenca);
11445            assert_eq!(
11446                c.licenca(),
11447                licenca,
11448                "Caixa::licenca must return :licenca verbatim (got {:?}, \
11449                 expected {licenca:?})",
11450                c.licenca(),
11451            );
11452            assert_eq!(
11453                c.licenca(),
11454                c.licenca.as_deref(),
11455                "Caixa::licenca must byte-equal the raw \
11456                 `self.licenca.as_deref()` field access across every \
11457                 value in the Option<&str> accept-set",
11458            );
11459        }
11460    }
11461
11462    #[test]
11463    fn validate_licenca_empty_arm_routes_through_accessor() {
11464        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
11465        // must key off [`Caixa::licenca`], not the raw
11466        // `self.licenca.as_deref()` field access. Structurally: a
11467        // `Caixa { licenca: Some(""), .. }` must surface the
11468        // `LicencaEmpty` refusal exactly, and a
11469        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
11470        // single-license form) must pass validate. The pair jointly
11471        // pins the accessor + validate-gate composition: any future
11472        // silent detour that had the accessor return `None` on the
11473        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
11474        // silently absorb the `LicencaEmpty` refusal at the accessor
11475        // boundary and the validate gate would accept a struct-literal
11476        // `Caixa { licenca: Some(""), .. }` — the composition pin
11477        // catches that at caixa-core build time.
11478        //
11479        // Peer of the per-`:politicas :circuit-breaker`
11480        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
11481        // accessor-composition pin
11482        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
11483        // on the sibling per-M3-mesh-slot required-`u32` axis — same
11484        // "the validate / shape-gate predicate must route through the
11485        // substrate-primitive typed dispatch" discipline extended onto
11486        // the outer top-level [`Caixa`] universal-axis
11487        // `Option<&str>`-composition surface.
11488        let c = caixa_with_licenca(Some(""));
11489        assert!(
11490            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
11491            "validate_licenca must reject licenca == Some(\"\") with \
11492             LicencaEmpty — the accessor and the validate gate must \
11493             route through the same substrate-primitive typed dispatch \
11494             on the :licenca empty arm",
11495        );
11496        let c = caixa_with_licenca(Some("MIT"));
11497        assert!(
11498            c.validate_licenca().is_ok(),
11499            "validate_licenca must accept licenca == Some(\"MIT\") \
11500             (the canonical single-license SPDX shape)",
11501        );
11502    }
11503
11504    #[test]
11505    fn licenca_projects_option_str_by_borrow() {
11506        // The by-borrow pin: [`Caixa::licenca`] returns
11507        // `Option<&str>` by borrow — the `&str` borrows the underlying
11508        // `String` storage of the `Option<String>` slot and the
11509        // accessor must not allocate a fresh `String` on every call.
11510        // Peer of the per-`:placement`
11511        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11512        // borrow pin on the peer per-M3-mesh-slot
11513        // `Option<&str>`-return axis, extended onto the outer top-
11514        // level [`Caixa`] universal-axis `Option<&str>` shape — the
11515        // accessor's returned `&str` must borrow from `&self` (the
11516        // returned reference's lifetime is tied to `&self`), and
11517        // calling the accessor twice on the same [`Caixa`] must yield
11518        // the same `Option<&str>` verbatim (idempotent, no side
11519        // effects on `&self`).
11520        //
11521        // Pins against a future silent detour that returned an owned
11522        // `Option<String>` (which would type-check but silently
11523        // allocate on every call, breaking the zero-cost projection
11524        // every peer sibling accessor carries), or a one-arm-only
11525        // accessor that returned a saturating value on some sentinel
11526        // input (breaking the pass-through invariant the sibling
11527        // required-scalar accessors carry).
11528        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
11529            let c = caixa_with_licenca(licenca);
11530            let first = c.licenca();
11531            let second = c.licenca();
11532            assert_eq!(
11533                first, second,
11534                "Caixa::licenca must be idempotent — two successive \
11535                 calls on the same &self must return the same \
11536                 Option<&str>",
11537            );
11538            assert_eq!(
11539                first, licenca,
11540                "Caixa::licenca must return :licenca verbatim by \
11541                 borrow — got {first:?}, expected {licenca:?}",
11542            );
11543        }
11544    }
11545
11546    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
11547
11548    #[test]
11549    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
11550        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
11551        // pin: [`Caixa::repositorio`] must return the `:repositorio`
11552        // typed byte-string verbatim as an `Option<&str>`, byte-equal
11553        // to the raw `self.repositorio.as_deref()` access across every
11554        // representative value in the accept-set — `None` (the "omit
11555        // the slot to defer to the per-renderer placeholder" arm every
11556        // existing fixture without a `:repositorio` line carries),
11557        // `Some("")` (a past-the-guard sentinel that pins the accessor
11558        // doesn't perform a silent `Some("") → None` collapse on the
11559        // empty arm — validate rejects `Some("")` through
11560        // `RepositorioEmpty` but the accessor must ship the raw slot
11561        // verbatim so a validate-time gate regression surfaces at the
11562        // caixa-helm / caixa-flux emit boundary rather than being
11563        // silently absorbed into the per-renderer fallback),
11564        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
11565        // shorthand every existing manifest fixture across
11566        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
11567        // `Some("https://github.com/pleme-io/checkout")` (the canonical
11568        // `https://` URL the README quickstart uses),
11569        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
11570        // `Some("git://github.com/pleme-io/checkout.git")` /
11571        // `Some("git@github.com:pleme-io/checkout.git")` /
11572        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
11573        // github scheme the shared `is_git_repo_url` predicate
11574        // documents), and five past-the-guard sentinels for the
11575        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
11576        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
11577        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
11578        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
11579        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
11580        // sentinels pin the accessor doesn't silently absorb the
11581        // refusal cases into a fallback).
11582        //
11583        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
11584        // accessor pin on the substrate primitive — sibling of the peer
11585        // [`Caixa::licenca`] (6d5bc28) pin
11586        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
11587        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
11588        // projection pin pattern this pin folds on. Sibling in shape to
11589        // the peer per-`:placement`
11590        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11591        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11592        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11593        // axes, extended onto the outer top-level [`Caixa`] universal-
11594        // axis surface. Pins against a future silent detour that
11595        // returned an owned `Option<String>` (which would type-check
11596        // but silently allocate on every accessor call, breaking the
11597        // zero-cost projection every peer sibling accessor carries), a
11598        // `Some("") → None` collapse (which would silently absorb the
11599        // `RepositorioEmpty` refusal case at the accessor boundary and
11600        // the caixa-helm `Chart.yaml` `home:` fold would silently
11601        // render a `home: null` / omitted field on a struct-literal
11602        // `Caixa { repositorio: Some(""), .. }`), or a
11603        // `None → Some(<default>)` collapse (which would silently reify
11604        // the per-renderer fallback at the accessor boundary and every
11605        // downstream consumer keying off the `Option::is_none()`
11606        // discriminator would lose the "author omitted the slot"
11607        // signal).
11608        for repositorio in [
11609            None,
11610            Some(""),
11611            Some("github:pleme-io/hello-rio"),
11612            Some("https://github.com/pleme-io/checkout"),
11613            Some("ssh://git@github.com/pleme-io/checkout.git"),
11614            Some("git://github.com/pleme-io/checkout.git"),
11615            Some("git@github.com:pleme-io/checkout.git"),
11616            Some("file:///opt/mirrors/pleme-io/checkout"),
11617            Some("pleme-io/checkout"),
11618            Some("-upload-pack=evil"),
11619            Some("github:pleme-io/checkout?ref=main"),
11620            Some("github:pleme-io/checkout#main"),
11621            Some("github:pleme-io/{tpl}"),
11622        ] {
11623            let c = caixa_with_repositorio(repositorio);
11624            assert_eq!(
11625                c.repositorio(),
11626                repositorio,
11627                "Caixa::repositorio must return :repositorio verbatim \
11628                 (got {:?}, expected {repositorio:?})",
11629                c.repositorio(),
11630            );
11631            assert_eq!(
11632                c.repositorio(),
11633                c.repositorio.as_deref(),
11634                "Caixa::repositorio must byte-equal the raw \
11635                 `self.repositorio.as_deref()` field access across every \
11636                 value in the Option<&str> accept-set",
11637            );
11638        }
11639    }
11640
11641    #[test]
11642    fn validate_repositorio_empty_arm_routes_through_accessor() {
11643        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
11644        // gate must key off [`Caixa::repositorio`], not the raw
11645        // `self.repositorio.as_deref()` field access. Structurally: a
11646        // `Caixa { repositorio: Some(""), .. }` must surface the
11647        // `RepositorioEmpty` refusal exactly, and a
11648        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
11649        // (the canonical `github:` shorthand form) must pass validate.
11650        // The pair jointly pins the accessor + validate-gate
11651        // composition: any future silent detour that had the accessor
11652        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
11653        // collapse) would silently absorb the `RepositorioEmpty` refusal
11654        // at the accessor boundary and the validate gate would accept a
11655        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
11656        // composition pin catches that at caixa-core build time.
11657        //
11658        // Peer of the [`Caixa::licenca`] (6d5bc28)
11659        // `validate_licenca_empty_arm_routes_through_accessor`
11660        // composition pin on the sibling outer top-level [`Caixa`]
11661        // `Option<&str>` universal-axis surface — same "the validate /
11662        // shape-gate predicate must route through the substrate-
11663        // primitive typed dispatch" discipline extended onto the second
11664        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
11665        // composition surface.
11666        let c = caixa_with_repositorio(Some(""));
11667        assert!(
11668            matches!(
11669                c.validate_repositorio(),
11670                Err(ManifestError::RepositorioEmpty),
11671            ),
11672            "validate_repositorio must reject repositorio == Some(\"\") \
11673             with RepositorioEmpty — the accessor and the validate gate \
11674             must route through the same substrate-primitive typed \
11675             dispatch on the :repositorio empty arm",
11676        );
11677        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
11678        assert!(
11679            c.validate_repositorio().is_ok(),
11680            "validate_repositorio must accept repositorio == \
11681             Some(\"github:pleme-io/hello-rio\") (the canonical \
11682             `github:` shorthand git-repo-URL shape)",
11683        );
11684    }
11685
11686    #[test]
11687    fn repositorio_projects_option_str_by_borrow() {
11688        // The by-borrow pin: [`Caixa::repositorio`] returns
11689        // `Option<&str>` by borrow — the `&str` borrows the underlying
11690        // `String` storage of the `Option<String>` slot and the
11691        // accessor must not allocate a fresh `String` on every call.
11692        // Peer of the per-`:placement`
11693        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
11694        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
11695        // `Option<&str>`-return axes, extended onto the second outer
11696        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
11697        // the accessor's returned `&str` must borrow from `&self` (the
11698        // returned reference's lifetime is tied to `&self`), and
11699        // calling the accessor twice on the same [`Caixa`] must yield
11700        // the same `Option<&str>` verbatim (idempotent, no side effects
11701        // on `&self`).
11702        //
11703        // Pins against a future silent detour that returned an owned
11704        // `Option<String>` (which would type-check but silently
11705        // allocate on every call, breaking the zero-cost projection
11706        // every peer sibling accessor carries), or a one-arm-only
11707        // accessor that returned a saturating value on some sentinel
11708        // input (breaking the pass-through invariant the sibling
11709        // required-scalar accessors carry).
11710        for repositorio in [
11711            None,
11712            Some(""),
11713            Some("github:pleme-io/hello-rio"),
11714            Some("https://github.com/pleme-io/checkout"),
11715        ] {
11716            let c = caixa_with_repositorio(repositorio);
11717            let first = c.repositorio();
11718            let second = c.repositorio();
11719            assert_eq!(
11720                first, second,
11721                "Caixa::repositorio must be idempotent — two successive \
11722                 calls on the same &self must return the same \
11723                 Option<&str>",
11724            );
11725            assert_eq!(
11726                first, repositorio,
11727                "Caixa::repositorio must return :repositorio verbatim by \
11728                 borrow — got {first:?}, expected {repositorio:?}",
11729            );
11730        }
11731    }
11732
11733    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
11734
11735    #[test]
11736    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
11737        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
11738        // return the author-declared `:repositorio` byte-string verbatim
11739        // on the `Some` arm — no scheme rewrite, no trailing-slash
11740        // canonicalization, no `github:` → `https://github.com/`
11741        // desugaring. The resolved-URL composer is the projection of
11742        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
11743        // the `String`-return arity every substrate-side field-fill
11744        // consumer keys off; on the `Some` arm the projection is
11745        // `str::to_owned` verbatim, so every accept-set value the
11746        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
11747        // across_permutations` pin covers (`https://…`, `github:…`,
11748        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
11749        // guard sentinel `pleme-io/…`) must survive the accessor
11750        // byte-equal. Pins against a future silent detour that rewrote
11751        // the `github:` shorthand to the `https://github.com/` full URL
11752        // at the accessor boundary (which would silently split the
11753        // resolved-URL surface from the raw [`Caixa::repositorio`]
11754        // accessor's documented pass-through invariant), or a trailing-
11755        // slash normalization (which would silently break the
11756        // FluxCD `GitRepository` `spec.url` byte-exact match every
11757        // downstream consumer keys the source-controller reconcile off).
11758        for repositorio in [
11759            "github:pleme-io/hello-rio",
11760            "https://github.com/pleme-io/checkout",
11761            "ssh://git@github.com/pleme-io/checkout.git",
11762            "git://github.com/pleme-io/checkout.git",
11763            "git@github.com:pleme-io/checkout.git",
11764            "file:///opt/mirrors/pleme-io/checkout",
11765        ] {
11766            let c = caixa_with_repositorio(Some(repositorio));
11767            assert_eq!(
11768                c.canonical_git_url(),
11769                repositorio,
11770                "Caixa::canonical_git_url on the Some arm must return \
11771                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
11772                c.canonical_git_url(),
11773            );
11774        }
11775    }
11776
11777    #[test]
11778    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
11779        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
11780        // `None` arm must emit the substrate's canonical pleme-org github
11781        // URL derived from `caixa.nome()` — `https://github.com/<org>/
11782        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
11783        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
11784        // is the exact byte-image of the prior inline
11785        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
11786        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
11787        // re-derived open-coded. Pins against a future silent detour
11788        // that migrated the `<org>` segment to a different constant (a
11789        // fork rebranding that split off a new
11790        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
11791        // to migrate onto), a scheme change (`https://` → `git://` or
11792        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
11793        // override (which would break the substrate-wide single-source-
11794        // of-truth guarantee this method encodes).
11795        let c = caixa_with_repositorio(None);
11796        let expected = format!(
11797            "https://github.com/{org}/{nome}",
11798            org = crate::DEFAULT_PLEME_GIT_ORG,
11799            nome = c.nome(),
11800        );
11801        assert_eq!(
11802            c.canonical_git_url(),
11803            expected,
11804            "Caixa::canonical_git_url on the None arm must fold through \
11805             the substrate's canonical pleme-org github URL fallback \
11806             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
11807             {:?}, expected {expected:?}",
11808            c.canonical_git_url(),
11809        );
11810    }
11811
11812    #[test]
11813    fn canonical_git_url_byte_matches_manual_composition() {
11814        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
11815        // byte-identically to the manual open-coded
11816        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
11817        //  format!("https://github.com/{org}/{nome}", ...))` composition
11818        // every prior substrate-side caller re-derived. Guards the
11819        // paired-site convergence just applied at caixa-flux's
11820        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
11821        // now routes through this accessor): a future implementation of
11822        // this method that reordered the format arguments, swapped the
11823        // `<org>` constant for a different one, or interposed a
11824        // canonicalization pass on the `Some` arm surfaces here as a
11825        // caixa-core build-time test failure rather than as a downstream
11826        // FluxCD `GitRepository` reconcile mismatch far from this
11827        // method's source.
11828        for repositorio in [
11829            None,
11830            Some("github:pleme-io/hello-rio"),
11831            Some("https://github.com/pleme-io/checkout"),
11832            Some("ssh://git@github.com/pleme-io/checkout.git"),
11833        ] {
11834            let c = caixa_with_repositorio(repositorio);
11835            let manual = c.repositorio().map_or_else(
11836                || {
11837                    format!(
11838                        "https://github.com/{org}/{nome}",
11839                        org = crate::DEFAULT_PLEME_GIT_ORG,
11840                        nome = c.nome(),
11841                    )
11842                },
11843                str::to_owned,
11844            );
11845            assert_eq!(
11846                c.canonical_git_url(),
11847                manual,
11848                "Caixa::canonical_git_url must byte-equal the manual \
11849                 open-coded `repositorio().map(str::to_owned)\
11850                 .unwrap_or_else(|| format!(...))` composition across \
11851                 every representative :repositorio input — got {:?}, \
11852                 expected {manual:?}",
11853                c.canonical_git_url(),
11854            );
11855        }
11856    }
11857
11858    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
11859
11860    #[test]
11861    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
11862        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
11863        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
11864        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
11865        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
11866        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
11867        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
11868        // the `0.0.0` boundary case. Every accept-set value the peer
11869        // validate gate lets through must survive the resolved-tag
11870        // projection byte-equal.
11871        for versao in [
11872            "0.1.0",
11873            "0.0.0",
11874            "1.0.0",
11875            "1.2.3-rc.1",
11876            "1.2.3+build.42",
11877            "1.2.3-rc.1+build.42",
11878        ] {
11879            let c = caixa_with_versao(versao);
11880            let expected = format!(
11881                "{prefix}{versao}",
11882                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11883            );
11884            assert_eq!(
11885                c.publish_tag(),
11886                expected,
11887                "Caixa::publish_tag must compose \
11888                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
11889                 :versao ({versao:?}) verbatim — got {got:?}, \
11890                 expected {expected:?}",
11891                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11892                got = c.publish_tag(),
11893            );
11894        }
11895    }
11896
11897    #[test]
11898    fn publish_tag_starts_with_default_publish_tag_prefix() {
11899        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
11900        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
11901        // byte-string on every input, guarding a hypothetical future
11902        // implementation that migrated the prefix segment to an inline
11903        // literal (`"v"`) that would silently drift from any rebrand of
11904        // the lifted constant. Peer to the sibling caixa-flux
11905        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
11906        // test which pins the same prefix invariant at the reader-side
11907        // `GitRefSpec::Tag` emit site.
11908        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
11909            let c = caixa_with_versao(versao);
11910            let tag = c.publish_tag();
11911            assert!(
11912                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
11913                "Caixa::publish_tag emission {tag:?} must start with \
11914                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
11915                 ({prefix:?})",
11916                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11917            );
11918        }
11919    }
11920
11921    #[test]
11922    fn publish_tag_byte_matches_manual_composition() {
11923        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
11924        // identically to the manual open-coded
11925        // `format!("{prefix}{versao}", prefix =
11926        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
11927        //  caixa.versao())` composition every prior substrate-side
11928        // caller re-derived. Guards the paired-site convergence just
11929        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
11930        // `git_ref` composer (which now routes through this accessor):
11931        // a future implementation of this method that reordered the
11932        // format arguments, swapped the `<prefix>` constant for a
11933        // different one, or interposed a canonicalization pass on the
11934        // `:versao` axis surfaces here as a caixa-core build-time test
11935        // failure rather than as a downstream FluxCD `GitRepository`
11936        // reconcile mismatch far from this method's source.
11937        for versao in [
11938            "0.1.0",
11939            "0.0.0",
11940            "1.2.3-rc.1",
11941            "1.2.3+build.42",
11942            "1.2.3-rc.1+build.42",
11943        ] {
11944            let c = caixa_with_versao(versao);
11945            let manual = format!(
11946                "{prefix}{versao}",
11947                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11948                versao = c.versao(),
11949            );
11950            assert_eq!(
11951                c.publish_tag(),
11952                manual,
11953                "Caixa::publish_tag must byte-equal the manual \
11954                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
11955                 composition across every representative :versao input \
11956                 — got {got:?}, expected {manual:?}",
11957                got = c.publish_tag(),
11958            );
11959        }
11960    }
11961
11962    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
11963
11964    #[test]
11965    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
11966        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
11967        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
11968        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
11969        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
11970        // set sweep documents — single-word, hyphen-joined, version-
11971        // suffixed, single-char, two-char, digit-start, retry-suffixed.
11972        // Every accept-set value the peer validate gate lets through must
11973        // survive the resolved-chart-name projection byte-equal.
11974        for nome in [
11975            "checkout",
11976            "cart-v2",
11977            "a",
11978            "db",
11979            "3rd-party-shim",
11980            "payment-retry",
11981            "0",
11982        ] {
11983            let c = caixa_with_nome(nome);
11984            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
11985            assert_eq!(
11986                c.lareira_chart_name(),
11987                expected,
11988                "Caixa::lareira_chart_name must compose \
11989                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
11990                 :nome ({nome:?}) verbatim — got {got:?}, \
11991                 expected {expected:?}",
11992                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
11993                got = c.lareira_chart_name(),
11994            );
11995        }
11996    }
11997
11998    #[test]
11999    fn lareira_chart_name_starts_with_lifted_prefix() {
12000        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
12001        // must begin with the canonical
12002        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
12003        // input, guarding a hypothetical future implementation that
12004        // migrated the prefix segment to an inline literal (`"lareira-"`)
12005        // that would silently drift from any rebrand of the lifted
12006        // constant. Peer to the sibling
12007        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
12008        // the co-resident resolved-publish-tag composer's prefix axis.
12009        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
12010            let c = caixa_with_nome(nome);
12011            let chart = c.lareira_chart_name();
12012            assert!(
12013                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
12014                "Caixa::lareira_chart_name emission {chart:?} must start \
12015                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
12016                 ({prefix:?})",
12017                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
12018            );
12019        }
12020    }
12021
12022    #[test]
12023    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
12024        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
12025        // byte-identically to the manual open-coded
12026        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
12027        // composition every prior substrate-side caller re-derived.
12028        // Guards the paired-site convergence just applied at caixa-helm's
12029        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
12030        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
12031        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
12032        // composer (all of which now route through this accessor): a
12033        // future implementation of this method that reordered the
12034        // composition arguments, swapped the `<prefix>` constant for a
12035        // different one, or interposed a canonicalization pass on the
12036        // `:nome` axis surfaces here as a caixa-core build-time test
12037        // failure rather than as a downstream Helm chart-render / FluxCD
12038        // reconcile / tatara Process-CR mismatch far from this method's
12039        // source.
12040        for nome in [
12041            "checkout",
12042            "cart-v2",
12043            "a",
12044            "db",
12045            "3rd-party-shim",
12046            "payment-retry",
12047        ] {
12048            let c = caixa_with_nome(nome);
12049            let manual = crate::lareira_chart_name(c.nome());
12050            assert_eq!(
12051                c.lareira_chart_name(),
12052                manual,
12053                "Caixa::lareira_chart_name must byte-equal the manual \
12054                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
12055                 composition across every representative :nome input — \
12056                 got {got:?}, expected {manual:?}",
12057                got = c.lareira_chart_name(),
12058            );
12059        }
12060    }
12061
12062    // ── Caixa::oci_chart_ref — resolved-OCI-chart-ref composer ────────
12063
12064    #[test]
12065    fn oci_chart_ref_composes_scheme_and_lareira_chart_name_on_all_shapes() {
12066        // Fail-before-pass-after pin: [`Caixa::oci_chart_ref`] must
12067        // compose [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied
12068        // `registry` + [`crate::lareira_chart_name`]-of-[`Caixa::nome`]
12069        // across the full paired `(registry, :nome)` accept-set — every
12070        // representative registry the substrate-side emitters carry
12071        // (`ghcr.io/pleme-io/charts`, the canonical CAIXA-SDLC §II
12072        // ArtifactHub-tier registry; `ghcr.io/pleme-io`, the bare-org
12073        // arm the sibling `oci_chart_ref_pins_byte_shape_against_prior_
12074        // inline_format` render-side pin exercises; `registry.example.
12075        // com`, an off-org shape; `localhost:5000`, the local-dev shape
12076        // every `feira chart` iteration path lands under) × every DNS-
12077        // 1123 `:nome` shape the peer `validate_nome_accepts_canonical_
12078        // forms` positive-set sweep documents (single-word, hyphen-
12079        // joined, single-char, two-char, digit-start, retry-suffixed).
12080        // Every accept-set pair the peer validate gates let through must
12081        // survive the resolved-OCI-ref projection byte-equal.
12082        for registry in [
12083            "ghcr.io/pleme-io/charts",
12084            "ghcr.io/pleme-io",
12085            "registry.example.com",
12086            "localhost:5000",
12087        ] {
12088            for nome in [
12089                "checkout",
12090                "cart-v2",
12091                "a",
12092                "db",
12093                "3rd-party-shim",
12094                "payment-retry",
12095                "0",
12096            ] {
12097                let c = caixa_with_nome(nome);
12098                let expected = format!(
12099                    "{scheme}{registry}/{chart}",
12100                    scheme = crate::OCI_SCHEME_PREFIX,
12101                    chart = crate::lareira_chart_name(nome),
12102                );
12103                assert_eq!(
12104                    c.oci_chart_ref(registry),
12105                    expected,
12106                    "Caixa::oci_chart_ref must compose \
12107                     OCI_SCHEME_PREFIX ({scheme:?}) + registry ({registry:?}) + \
12108                     lareira_chart_name(:nome ({nome:?})) verbatim — got {got:?}, \
12109                     expected {expected:?}",
12110                    scheme = crate::OCI_SCHEME_PREFIX,
12111                    got = c.oci_chart_ref(registry),
12112                );
12113            }
12114        }
12115    }
12116
12117    #[test]
12118    fn oci_chart_ref_starts_with_lifted_scheme_prefix() {
12119        // Scheme-prefix-shape pin: every [`Caixa::oci_chart_ref`]
12120        // emission must begin with the canonical
12121        // [`crate::OCI_SCHEME_PREFIX`] byte-string on every input, guarding
12122        // a hypothetical future implementation that migrated the scheme
12123        // segment to an inline literal (`"oci://"`) that would silently
12124        // drift from any rebrand of the lifted constant. Peer to the
12125        // sibling [`publish_tag_starts_with_default_publish_tag_prefix`]
12126        // + [`lareira_chart_name_starts_with_lifted_prefix`] pins on the
12127        // co-resident resolved-publish-tag / resolved-chart-name
12128        // composers' prefix axes.
12129        for registry in [
12130            "ghcr.io/pleme-io/charts",
12131            "ghcr.io/pleme-io",
12132            "localhost:5000",
12133        ] {
12134            for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
12135                let c = caixa_with_nome(nome);
12136                let ref_ = c.oci_chart_ref(registry);
12137                assert!(
12138                    ref_.starts_with(crate::OCI_SCHEME_PREFIX),
12139                    "Caixa::oci_chart_ref emission {ref_:?} must start \
12140                     with the lifted crate::OCI_SCHEME_PREFIX ({scheme:?}) \
12141                     — registry ({registry:?}), :nome ({nome:?})",
12142                    scheme = crate::OCI_SCHEME_PREFIX,
12143                );
12144            }
12145        }
12146    }
12147
12148    #[test]
12149    fn oci_chart_ref_byte_matches_canonical_helper_composition() {
12150        // Byte-parity pin: [`Caixa::oci_chart_ref`] must render byte-
12151        // identically to the manual open-coded
12152        // `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step
12153        // composition every prior substrate-side caller re-derived.
12154        // Guards the paired-site convergence just applied at caixa-
12155        // tatara's [`derive_chart_ref`] helper (which now routes through
12156        // this accessor): a future implementation of this method that
12157        // reordered the composition arguments, swapped the `<scheme>`
12158        // constant for a different one, migrated the `<chart>` segment
12159        // off the paired [`crate::lareira_chart_name`] composer, or
12160        // interposed a canonicalization pass on either input axis
12161        // surfaces here as a caixa-core build-time test failure rather
12162        // than as a downstream `helm install` / FluxCD OCI-source
12163        // reconcile / tatara `Process`-CR mismatch far from this
12164        // method's source. Sibling to the peer
12165        // [`lareira_chart_name_byte_matches_canonical_helper_composition`]
12166        // / [`publish_tag_byte_matches_manual_composition`] /
12167        // [`canonical_git_url_byte_matches_manual_composition`] byte-
12168        // parity pins that carry the same discipline on the co-resident
12169        // resolved-chart-name / resolved-publish-tag / resolved-git-URL
12170        // composers.
12171        for registry in [
12172            "ghcr.io/pleme-io/charts",
12173            "ghcr.io/pleme-io",
12174            "registry.example.com",
12175            "localhost:5000",
12176        ] {
12177            for nome in [
12178                "checkout",
12179                "cart-v2",
12180                "a",
12181                "db",
12182                "3rd-party-shim",
12183                "payment-retry",
12184            ] {
12185                let c = caixa_with_nome(nome);
12186                let manual = crate::oci_chart_ref(registry, c.nome());
12187                assert_eq!(
12188                    c.oci_chart_ref(registry),
12189                    manual,
12190                    "Caixa::oci_chart_ref must byte-equal the manual \
12191                     open-coded `caixa_core::oci_chart_ref(registry, \
12192                     caixa.nome())` composition across every representative \
12193                     (registry, :nome) pair — registry ({registry:?}), \
12194                     :nome ({nome:?}), got {got:?}, expected {manual:?}",
12195                    got = c.oci_chart_ref(registry),
12196                );
12197            }
12198        }
12199    }
12200
12201    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
12202
12203    #[test]
12204    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
12205        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
12206        // pin: [`Caixa::descricao`] must return the `:descricao` typed
12207        // byte-string verbatim as an `Option<&str>`, byte-equal to the
12208        // raw `self.descricao.as_deref()` access across every
12209        // representative value in the accept-set — `None` (the "omit
12210        // the slot to defer to the per-renderer `caixa.nome`-derived
12211        // fallback" arm every existing fixture without a `:descricao`
12212        // line carries), `Some("")` (a past-the-guard sentinel that
12213        // pins the accessor doesn't perform a silent `Some("") → None`
12214        // collapse on the empty arm — validate rejects `Some("")`
12215        // through `DescricaoEmpty` but the accessor must ship the raw
12216        // slot verbatim so a validate-time gate regression surfaces at
12217        // the caixa-helm / caixa-feira emit boundary rather than being
12218        // silently absorbed into the per-renderer `caixa.nome`-derived
12219        // fallback), `Some("Checkout flow.")` (the canonical one-line
12220        // prose descriptor the peer
12221        // `validate_descricao_accepts_canonical_value` positive sweep
12222        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
12223        // Servico.")` (the multi-byte Unicode continuation-byte shape
12224        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
12225        // multi-glyph Unicode shape the peer
12226        // `is_chart_description_shape` predicate accepts), and five
12227        // past-the-guard sentinels for the `DescricaoInvalid` refusal
12228        // cases (`Some(" Checkout flow.")` leading-whitespace,
12229        // `Some("Checkout flow. ")` trailing-whitespace,
12230        // `Some("Checkout\nflow.")` embedded-LF,
12231        // `Some("Checkout\tflow.")` embedded-TAB, and
12232        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
12233        // the accessor doesn't silently absorb the refusal cases into
12234        // a fallback).
12235        //
12236        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
12237        // accessor pin on the substrate primitive — sibling of the peer
12238        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
12239        // (cc7332d) pins that opened the "outer [`Caixa`]
12240        // `Option<&str>` scalar" projection pin pattern this pin folds
12241        // on. Sibling in shape to the peer per-`:placement`
12242        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12243        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12244        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12245        // axes, extended onto the outer top-level [`Caixa`] universal-
12246        // axis surface. Pins against a future silent detour that
12247        // returned an owned `Option<String>` (which would type-check
12248        // but silently allocate on every accessor call, breaking the
12249        // zero-cost projection every peer sibling accessor carries), a
12250        // `Some("") → None` collapse (which would silently absorb the
12251        // `DescricaoEmpty` refusal case at the accessor boundary and
12252        // the caixa-helm `Chart.yaml` `description:` fold would
12253        // silently render a `caixa.nome`-derived fallback on a
12254        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
12255        // `None → Some(<default>)` collapse (which would silently
12256        // reify the per-renderer `caixa.nome`-derived fallback at the
12257        // accessor boundary and every downstream consumer keying off
12258        // the `Option::is_none()` discriminator would lose the "author
12259        // omitted the slot" signal).
12260        for descricao in [
12261            None,
12262            Some(""),
12263            Some("Checkout flow."),
12264            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
12265            Some("→ — · ✓"),
12266            Some(" Checkout flow."),
12267            Some("Checkout flow. "),
12268            Some("Checkout\nflow."),
12269            Some("Checkout\tflow."),
12270            Some("Checkout\x00flow."),
12271        ] {
12272            let c = caixa_with_descricao(descricao);
12273            assert_eq!(
12274                c.descricao(),
12275                descricao,
12276                "Caixa::descricao must return :descricao verbatim (got \
12277                 {:?}, expected {descricao:?})",
12278                c.descricao(),
12279            );
12280            assert_eq!(
12281                c.descricao(),
12282                c.descricao.as_deref(),
12283                "Caixa::descricao must byte-equal the raw \
12284                 `self.descricao.as_deref()` field access across every \
12285                 value in the Option<&str> accept-set",
12286            );
12287        }
12288    }
12289
12290    #[test]
12291    fn validate_descricao_empty_arm_routes_through_accessor() {
12292        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
12293        // gate must key off [`Caixa::descricao`], not the raw
12294        // `self.descricao.as_deref()` field access. Structurally: a
12295        // `Caixa { descricao: Some(""), .. }` must surface the
12296        // `DescricaoEmpty` refusal exactly, and a
12297        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
12298        // canonical one-line-prose form) must pass validate. The pair
12299        // jointly pins the accessor + validate-gate composition: any
12300        // future silent detour that had the accessor return `None` on
12301        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
12302        // silently absorb the `DescricaoEmpty` refusal at the accessor
12303        // boundary and the validate gate would accept a struct-literal
12304        // `Caixa { descricao: Some(""), .. }` — the composition pin
12305        // catches that at caixa-core build time.
12306        //
12307        // Peer of the [`Caixa::licenca`] (6d5bc28)
12308        // `validate_licenca_empty_arm_routes_through_accessor` and
12309        // [`Caixa::repositorio`] (cc7332d)
12310        // `validate_repositorio_empty_arm_routes_through_accessor`
12311        // composition pins on the sibling outer top-level [`Caixa`]
12312        // `Option<&str>` universal-axis surface — same "the validate /
12313        // shape-gate predicate must route through the substrate-
12314        // primitive typed dispatch" discipline extended onto the third
12315        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
12316        // composition surface.
12317        let c = caixa_with_descricao(Some(""));
12318        assert!(
12319            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
12320            "validate_descricao must reject descricao == Some(\"\") \
12321             with DescricaoEmpty — the accessor and the validate gate \
12322             must route through the same substrate-primitive typed \
12323             dispatch on the :descricao empty arm",
12324        );
12325        let c = caixa_with_descricao(Some("Checkout flow."));
12326        assert!(
12327            c.validate_descricao().is_ok(),
12328            "validate_descricao must accept descricao == \
12329             Some(\"Checkout flow.\") (the canonical one-line-prose \
12330             chart-description shape)",
12331        );
12332    }
12333
12334    #[test]
12335    fn descricao_projects_option_str_by_borrow() {
12336        // The by-borrow pin: [`Caixa::descricao`] returns
12337        // `Option<&str>` by borrow — the `&str` borrows the underlying
12338        // `String` storage of the `Option<String>` slot and the
12339        // accessor must not allocate a fresh `String` on every call.
12340        // Peer of the [`Caixa::licenca`] (6d5bc28) and
12341        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
12342        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
12343        // the per-`:placement`
12344        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
12345        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
12346        // return axis, extended onto the third outer top-level
12347        // [`Caixa`] universal-axis `Option<&str>` shape — the
12348        // accessor's returned `&str` must borrow from `&self` (the
12349        // returned reference's lifetime is tied to `&self`), and
12350        // calling the accessor twice on the same [`Caixa`] must yield
12351        // the same `Option<&str>` verbatim (idempotent, no side
12352        // effects on `&self`).
12353        //
12354        // Pins against a future silent detour that returned an owned
12355        // `Option<String>` (which would type-check but silently
12356        // allocate on every call, breaking the zero-cost projection
12357        // every peer sibling accessor carries), or a one-arm-only
12358        // accessor that returned a saturating value on some sentinel
12359        // input (breaking the pass-through invariant the sibling
12360        // required-scalar accessors carry).
12361        for descricao in [
12362            None,
12363            Some(""),
12364            Some("Checkout flow."),
12365            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
12366        ] {
12367            let c = caixa_with_descricao(descricao);
12368            let first = c.descricao();
12369            let second = c.descricao();
12370            assert_eq!(
12371                first, second,
12372                "Caixa::descricao must be idempotent — two successive \
12373                 calls on the same &self must return the same \
12374                 Option<&str>",
12375            );
12376            assert_eq!(
12377                first, descricao,
12378                "Caixa::descricao must return :descricao verbatim by \
12379                 borrow — got {first:?}, expected {descricao:?}",
12380            );
12381        }
12382    }
12383
12384    // ── validate_edicao — universal-axis language-edition shape ──
12385
12386    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
12387        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12388        c.edicao = edicao.map(String::from);
12389        c
12390    }
12391
12392    #[test]
12393    fn validate_edicao_accepts_none() {
12394        // The omit-the-slot identity: `:edicao` is optional. The
12395        // gate is a no-op when the author didn't declare a value —
12396        // every caixa without an `:edicao` line trivially passes,
12397        // and the substrate-side build pipeline falls back to the
12398        // documented default edition. Mirrors the peer
12399        // `validate_licenca_accepts_none` posture on the sibling
12400        // `Option<String>` Caixa slot.
12401        let c = caixa_with_edicao(None);
12402        c.validate_edicao().unwrap();
12403    }
12404
12405    #[test]
12406    fn validate_edicao_accepts_canonical_value() {
12407        // Positive control: the canonical `"2026"` edition every
12408        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
12409        // `caixa-mesh`) carries by construction passes the gate.
12410        // Future-introduced sibling editions (`"2027"`, `"2030"`,
12411        // `"2049"`) that match the same 4-digit ASCII decimal year
12412        // shape must also trivially pass — the structural shape
12413        // predicate accepts every well-formed year regardless of
12414        // whether the substrate yet understands the specific value
12415        // (a future known-edition allowlist tightens that).
12416        for ed in ["2026", "2027", "2030", "2049"] {
12417            let c = caixa_with_edicao(Some(ed));
12418            c.validate_edicao()
12419                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
12420        }
12421    }
12422
12423    #[test]
12424    fn validate_edicao_rejects_empty_some() {
12425        // Canonical paste-from-blank-doc footgun. Without this gate
12426        // the empty `Some("")` silently lands as `(:edicao "")` in
12427        // the rendered caixa.lisp and a future renderer-side
12428        // consumer's `Option::unwrap_or_else` (which only fires on
12429        // `None`) skips its fallback. Mirrors the peer
12430        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
12431        // `Option<String>` Caixa slot.
12432        let c = caixa_with_edicao(Some(""));
12433        let err = c.validate_edicao().unwrap_err();
12434        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
12435    }
12436
12437    #[test]
12438    fn validate_edicao_rejects_free_form_non_year() {
12439        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
12440        // `"nightly"` shapes carry no operational meaning on the
12441        // substrate's build-time edition selector. Until this gate
12442        // landed the bare empty-arm check let every such value
12443        // through and broke far from the source caixa.lisp. Peer
12444        // with the shape-predicate cascade
12445        // `validate_repositorio_rejects_missing_colon_separator`
12446        // establishes past its own empty arm.
12447        for ed in ["x", "latest", "nightly", "stable"] {
12448            let c = caixa_with_edicao(Some(ed));
12449            let err = c.validate_edicao().unwrap_err();
12450            assert!(
12451                matches!(err, ManifestError::EdicaoInvalid { .. }),
12452                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12453            );
12454        }
12455    }
12456
12457    #[test]
12458    fn validate_edicao_rejects_trailing_whitespace() {
12459        // Paste-from-doc whitespace footgun. A trailing space in
12460        // the `:edicao` value would silently break the substrate's
12461        // build-time edition match-table lookup at the rendered
12462        // artifact's edition-selector consumer. The shape predicate
12463        // refuses every whitespace byte by construction (any byte
12464        // outside `0-9` fails `is_ascii_digit`). Peer with
12465        // `validate_repositorio_rejects_whitespace`.
12466        let c = caixa_with_edicao(Some("2026 "));
12467        let err = c.validate_edicao().unwrap_err();
12468        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
12469            panic!("expected EdicaoInvalid, got {err:?}");
12470        };
12471        assert_eq!(edicao, "2026 ");
12472    }
12473
12474    #[test]
12475    fn validate_edicao_rejects_leading_whitespace() {
12476        // Symmetric paste-from-doc whitespace footgun on the leading
12477        // boundary — the gate refuses every shape with a non-digit
12478        // byte by construction.
12479        let c = caixa_with_edicao(Some(" 2026"));
12480        let err = c.validate_edicao().unwrap_err();
12481        assert!(
12482            matches!(err, ManifestError::EdicaoInvalid { .. }),
12483            "got {err:?}",
12484        );
12485    }
12486
12487    #[test]
12488    fn validate_edicao_rejects_control_char() {
12489        // Paste-from-multiline-doc CRLF footgun — control characters
12490        // at the value boundary break the substrate's build-time
12491        // edition-selector parser. Peer with
12492        // `validate_repositorio_rejects_control_char`.
12493        let c = caixa_with_edicao(Some("2026\n"));
12494        let err = c.validate_edicao().unwrap_err();
12495        assert!(
12496            matches!(err, ManifestError::EdicaoInvalid { .. }),
12497            "got {err:?}",
12498        );
12499    }
12500
12501    #[test]
12502    fn validate_edicao_rejects_non_ascii_lookalike() {
12503        // Fullwidth-keyboard look-alike footgun — `"2026"` is
12504        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
12505        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
12506        // edition selector wants an ASCII year, and the gate
12507        // refuses every non-ASCII shape by construction (length in
12508        // bytes is 12 ≠ 4, *and* every byte falls outside
12509        // `is_ascii_digit`'s `0-9` range).
12510        let c = caixa_with_edicao(Some("2026"));
12511        let err = c.validate_edicao().unwrap_err();
12512        assert!(
12513            matches!(err, ManifestError::EdicaoInvalid { .. }),
12514            "got {err:?}",
12515        );
12516    }
12517
12518    #[test]
12519    fn validate_edicao_rejects_version_tag_prefix() {
12520        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
12521        // / `"r2026"` are familiar shapes from git-tag / Rust
12522        // edition / release-tag conventions that don't apply to
12523        // the year-shaped edition axis. The shape predicate refuses
12524        // every leading non-digit prefix.
12525        for ed in ["v2026", "e2026", "r2026"] {
12526            let c = caixa_with_edicao(Some(ed));
12527            let err = c.validate_edicao().unwrap_err();
12528            assert!(
12529                matches!(err, ManifestError::EdicaoInvalid { .. }),
12530                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12531            );
12532        }
12533    }
12534
12535    #[test]
12536    fn validate_edicao_rejects_decimal_shape() {
12537        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
12538        // `"2026.0"` are familiar shapes from semver / float
12539        // conventions that don't apply to the year-shaped edition
12540        // axis. The shape predicate refuses every non-digit byte
12541        // (`.` falls outside `is_ascii_digit`).
12542        for ed in ["2026.1", "2026.0", "2026.0.1"] {
12543            let c = caixa_with_edicao(Some(ed));
12544            let err = c.validate_edicao().unwrap_err();
12545            assert!(
12546                matches!(err, ManifestError::EdicaoInvalid { .. }),
12547                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12548            );
12549        }
12550    }
12551
12552    #[test]
12553    fn validate_edicao_rejects_wrong_length_numeric() {
12554        // Wrong-length numeric footgun — `"26"` (truncated) /
12555        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
12556        // (zero-padded too wide) all parse as integers but don't
12557        // name a 4-digit year. The shape predicate refuses every
12558        // value whose length isn't exactly 4 bytes.
12559        for ed in ["26", "202", "20260", "00026", "9"] {
12560            let c = caixa_with_edicao(Some(ed));
12561            let err = c.validate_edicao().unwrap_err();
12562            assert!(
12563                matches!(err, ManifestError::EdicaoInvalid { .. }),
12564                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12565            );
12566        }
12567    }
12568
12569    #[test]
12570    fn validate_edicao_empty_takes_precedence_over_shape() {
12571        // Empty-first cascade pin: the empty `Some("")` surfaces
12572        // the narrower `EdicaoEmpty` not the shape-predicate-
12573        // wrapped `EdicaoInvalid`, mirroring the peer
12574        // `validate_repositorio_empty_takes_precedence_over_shape`
12575        // (`RepositorioEmpty` → `RepositorioInvalid`),
12576        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
12577        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
12578        // cascades. The shape predicate also refuses the empty
12579        // input (defensively — `s.len() != 4`), but the
12580        // manifest-layer empty arm runs first to surface the
12581        // narrower diagnostic verbatim.
12582        let c = caixa_with_edicao(Some(""));
12583        let err = c.validate_edicao().unwrap_err();
12584        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
12585    }
12586
12587    #[test]
12588    fn validate_edicao_template_passes() {
12589        // Round-trip pin: the bare `Caixa::template` shape (which
12590        // carries `:edicao "2026"` verbatim) passes the gate by
12591        // construction. A future template-shape change that
12592        // introduced `(:edicao "")` or a non-year value would
12593        // surface here as a regression. Mirrors the peer
12594        // `validate_licenca_template_passes` pin.
12595        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12596        c.validate_edicao().unwrap();
12597    }
12598
12599    #[test]
12600    fn validate_edicao_diagnostic_names_offending_slot() {
12601        // Diagnostic-shape pin (peer with
12602        // `validate_licenca_diagnostic_names_offending_slot`): the
12603        // error's Display surfaces the `:edicao` slot name verbatim,
12604        // so a `feira lint` run can render the diagnostic without
12605        // re-parsing and the author can grep their caixa.lisp for
12606        // the offending `:edicao` line.
12607        let c = caixa_with_edicao(Some(""));
12608        let rendered = c.validate_edicao().unwrap_err().to_string();
12609        assert!(
12610            rendered.contains(":edicao"),
12611            "diagnostic must name the offending slot: {rendered}",
12612        );
12613    }
12614
12615    #[test]
12616    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
12617        // Diagnostic-shape pin on the shape-predicate arm (peer
12618        // with `validate_repositorio_diagnostic_carries_offending_value`):
12619        // the error's Display surfaces the offending value + slot
12620        // name verbatim, so a `feira lint` run can render the
12621        // diagnostic without re-parsing and the author can grep
12622        // their caixa.lisp for the offending `:edicao` value.
12623        let c = caixa_with_edicao(Some("v2026"));
12624        let rendered = c.validate_edicao().unwrap_err().to_string();
12625        assert!(
12626            rendered.contains(":edicao"),
12627            "diagnostic must name the offending slot: {rendered}",
12628        );
12629        assert!(
12630            rendered.contains("v2026"),
12631            "diagnostic must quote the offending value: {rendered}",
12632        );
12633    }
12634
12635    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
12636
12637    #[test]
12638    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
12639        // The canonical per-`Caixa` `:edicao` language-edition scalar
12640        // pin: [`Caixa::edicao`] must return the `:edicao` typed
12641        // byte-string verbatim as an `Option<&str>`, byte-equal to the
12642        // raw `self.edicao.as_deref()` access across every representative
12643        // value in the accept-set — `None` (the "omit the slot to defer
12644        // to the substrate's default edition" arm every existing
12645        // [`caixa-resolver`] fixture without an `:edicao` line carries),
12646        // `Some("")` (a past-the-guard sentinel that pins the accessor
12647        // doesn't perform a silent `Some("") → None` collapse on the
12648        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
12649        // but the accessor must ship the raw slot verbatim so a
12650        // validate-time gate regression surfaces at any future edition-
12651        // aware consumer's boundary rather than being silently absorbed
12652        // into the substrate's default edition), `Some("2026")` (the
12653        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
12654        // template scaffolds via [`Caixa::template`] and every
12655        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
12656        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
12657        // carries by construction), `Some("2018")` / `Some("2021")` /
12658        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
12659        // peer with Cargo's `[package] edition` grammar every future-
12660        // introduced sibling to `"2026"` will follow), and eight
12661        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
12662        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
12663        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
12664        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
12665        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
12666        // length-numeric, `Some("latest")` free-form-non-year — the
12667        // sentinels pin the accessor doesn't silently absorb the
12668        // refusal cases into a substrate-default-edition fallback).
12669        //
12670        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
12671        // return scalar accessor pin on the substrate primitive —
12672        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
12673        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
12674        // (3f16e2f) pins that opened the "outer [`Caixa`]
12675        // `Option<&str>` scalar" projection pin pattern this pin folds
12676        // on. Sibling in shape to the peer per-`:placement`
12677        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12678        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12679        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12680        // axes, extended onto the outer top-level [`Caixa`] universal-
12681        // axis surface's last unlifted `Option<String>` slot. Pins
12682        // against a future silent detour that returned an owned
12683        // `Option<String>` (which would type-check but silently
12684        // allocate on every accessor call, breaking the zero-cost
12685        // projection every peer sibling accessor carries), a
12686        // `Some("") → None` collapse (which would silently absorb the
12687        // `EdicaoEmpty` refusal case at the accessor boundary and any
12688        // future edition-aware consumer would silently fall back to
12689        // the substrate's default edition on a struct-literal
12690        // `Caixa { edicao: Some(""), .. }`), or a
12691        // `None → Some("2026")` collapse (which would silently reify
12692        // the substrate's default edition at the accessor boundary
12693        // and every downstream consumer keying off the
12694        // `Option::is_none()` discriminator would lose the "author
12695        // omitted the slot" signal).
12696        for edicao in [
12697            None,
12698            Some(""),
12699            Some("2026"),
12700            Some("2018"),
12701            Some("2021"),
12702            Some("2024"),
12703            Some("2026 "),
12704            Some(" 2026"),
12705            Some("2026\n"),
12706            Some("2026"),
12707            Some("v2026"),
12708            Some("2026.1"),
12709            Some("26"),
12710            Some("latest"),
12711        ] {
12712            let c = caixa_with_edicao(edicao);
12713            assert_eq!(
12714                c.edicao(),
12715                edicao,
12716                "Caixa::edicao must return :edicao verbatim (got {:?}, \
12717                 expected {edicao:?})",
12718                c.edicao(),
12719            );
12720            assert_eq!(
12721                c.edicao(),
12722                c.edicao.as_deref(),
12723                "Caixa::edicao must byte-equal the raw \
12724                 `self.edicao.as_deref()` field access across every \
12725                 value in the Option<&str> accept-set",
12726            );
12727        }
12728    }
12729
12730    #[test]
12731    fn validate_edicao_empty_arm_routes_through_accessor() {
12732        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
12733        // must key off [`Caixa::edicao`], not the raw
12734        // `self.edicao.as_deref()` field access. Structurally: a
12735        // `Caixa { edicao: Some(""), .. }` must surface the
12736        // `EdicaoEmpty` refusal exactly, and a
12737        // `Caixa { edicao: Some("2026"), .. }` (the canonical
12738        // 4-digit-ASCII-decimal-year form) must pass validate. The
12739        // pair jointly pins the accessor + validate-gate composition:
12740        // any future silent detour that had the accessor return `None`
12741        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
12742        // would silently absorb the `EdicaoEmpty` refusal at the
12743        // accessor boundary and the validate gate would accept a
12744        // struct-literal `Caixa { edicao: Some(""), .. }` — the
12745        // composition pin catches that at caixa-core build time.
12746        //
12747        // Peer of the [`Caixa::licenca`] (6d5bc28)
12748        // `validate_licenca_empty_arm_routes_through_accessor`,
12749        // [`Caixa::repositorio`] (cc7332d)
12750        // `validate_repositorio_empty_arm_routes_through_accessor`,
12751        // and [`Caixa::descricao`] (3f16e2f)
12752        // `validate_descricao_empty_arm_routes_through_accessor`
12753        // composition pins on the sibling outer top-level [`Caixa`]
12754        // `Option<&str>` universal-axis surface — same "the validate /
12755        // shape-gate predicate must route through the substrate-
12756        // primitive typed dispatch" discipline extended onto the
12757        // fourth and final outer top-level [`Caixa`] universal-axis
12758        // `Option<&str>`-composition surface, closing the accessor-
12759        // composition family.
12760        let c = caixa_with_edicao(Some(""));
12761        assert!(
12762            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
12763            "validate_edicao must reject edicao == Some(\"\") with \
12764             EdicaoEmpty — the accessor and the validate gate must \
12765             route through the same substrate-primitive typed dispatch \
12766             on the :edicao empty arm",
12767        );
12768        let c = caixa_with_edicao(Some("2026"));
12769        assert!(
12770            c.validate_edicao().is_ok(),
12771            "validate_edicao must accept edicao == Some(\"2026\") \
12772             (the canonical 4-digit-ASCII-decimal-year shape)",
12773        );
12774    }
12775
12776    #[test]
12777    fn edicao_projects_option_str_by_borrow() {
12778        // The by-borrow pin: [`Caixa::edicao`] returns
12779        // `Option<&str>` by borrow — the `&str` borrows the underlying
12780        // `String` storage of the `Option<String>` slot and the
12781        // accessor must not allocate a fresh `String` on every call.
12782        // Peer of the [`Caixa::licenca`] (6d5bc28),
12783        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
12784        // (3f16e2f) by-borrow pins on the peer outer top-level
12785        // [`Caixa`] `Option<&str>`-return axes, and of the
12786        // per-`:placement`
12787        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
12788        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
12789        // return axis, extended onto the fourth and final outer top-
12790        // level [`Caixa`] universal-axis `Option<&str>` shape — the
12791        // accessor's returned `&str` must borrow from `&self` (the
12792        // returned reference's lifetime is tied to `&self`), and
12793        // calling the accessor twice on the same [`Caixa`] must yield
12794        // the same `Option<&str>` verbatim (idempotent, no side
12795        // effects on `&self`).
12796        //
12797        // Pins against a future silent detour that returned an owned
12798        // `Option<String>` (which would type-check but silently
12799        // allocate on every call, breaking the zero-cost projection
12800        // every peer sibling accessor carries), or a one-arm-only
12801        // accessor that returned a saturating value on some sentinel
12802        // input (breaking the pass-through invariant the sibling
12803        // required-scalar accessors carry).
12804        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
12805            let c = caixa_with_edicao(edicao);
12806            let first = c.edicao();
12807            let second = c.edicao();
12808            assert_eq!(
12809                first, second,
12810                "Caixa::edicao must be idempotent — two successive \
12811                 calls on the same &self must return the same \
12812                 Option<&str>",
12813            );
12814            assert_eq!(
12815                first, edicao,
12816                "Caixa::edicao must return :edicao verbatim by \
12817                 borrow — got {first:?}, expected {edicao:?}",
12818            );
12819        }
12820    }
12821
12822    #[test]
12823    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
12824        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
12825        // label caixa-identity scalar pin: [`Caixa::nome`] must return
12826        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
12827        // the raw field access across every representative value in
12828        // the accept-set — the canonical `"demo"` template baseline
12829        // (the same `feira init`-scaffolded default the sibling
12830        // `validate_nome_accepts_canonical_template` positive-control
12831        // gate pins), plus every sibling per-typed-slot atom accessor's
12832        // canonical positive-arm byte-string (`"catalog"` per
12833        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
12834        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
12835        // `caixa-helm`/`caixa-flux` cross-crate integration-test
12836        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
12837        // canonical example), plus every past-the-guard sentinel for
12838        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
12839        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
12840        // the bare DNS-1123 63-byte cap but overflows the joint
12841        // `lareira-<nome>` chart-name budget the sibling
12842        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
12843        //
12844        // The past-the-guard sentinels pin the accessor doesn't
12845        // silently absorb the refusal cases into a template-derived
12846        // fallback (a future `.nome().is_empty().then(|| "demo")`
12847        // collapse would silently absorb the `NomeEmpty` refusal at
12848        // the accessor boundary and the validate gate would accept a
12849        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
12850        // catches that at caixa-core build time).
12851        //
12852        // First outer top-level [`Caixa`] `&str`-return required-
12853        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
12854        // required-scalar" projection pattern the sibling per-`Caixa`
12855        // `:versao` future lift folds on. Sibling in shape to the peer
12856        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
12857        // required-`String`-carry accessor pin on the sibling per-
12858        // sub-struct required-axis, extended onto the outer top-level
12859        // [`Caixa`] universal-axis required-`String`-carry axis.
12860        for nome in [
12861            "demo",
12862            "catalog",
12863            "cart",
12864            "hello-rio",
12865            "checkout",
12866            "",
12867            "Bad_Name",
12868            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
12869        ] {
12870            let c = caixa_with_nome(nome);
12871            assert_eq!(
12872                c.nome(),
12873                nome,
12874                "Caixa::nome must return :nome verbatim (got {}, \
12875                 expected {nome})",
12876                c.nome(),
12877            );
12878            assert_eq!(
12879                c.nome(),
12880                c.nome.as_str(),
12881                "Caixa::nome must byte-equal the raw .nome field \
12882                 access across every value in the String accept-set",
12883            );
12884        }
12885    }
12886
12887    #[test]
12888    fn validate_nome_empty_arm_routes_through_accessor() {
12889        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
12890        // key off [`Caixa::nome`], not the raw `.nome` field access.
12891        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
12892        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
12893        // template baseline (the peer positive-arm the sibling
12894        // `validate_nome_accepts_canonical_template` gate carves out)
12895        // must pass validate. The pair jointly pins the accessor +
12896        // validate-gate composition: any future silent detour that
12897        // had the accessor return a fresh `"demo"` on the empty arm
12898        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
12899        // would silently absorb the `NomeEmpty` refusal at the
12900        // accessor boundary and the validate gate would accept a
12901        // struct-literal `Caixa { nome: "".into(), .. }` — the
12902        // composition pin catches that at caixa-core build time.
12903        //
12904        // Peer of the sibling per-`Caixa`
12905        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
12906        // / `validate_repositorio_empty_arm_routes_through_accessor`
12907        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
12908        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
12909        // (2641cbd) composition pins on the sibling outer top-level
12910        // [`Caixa`] `Option<&str>` axes — same "the validate /
12911        // shape-gate predicate must route through the substrate-
12912        // primitive typed dispatch" discipline extended onto the peer
12913        // outer top-level [`Caixa`] required-`&str` composition axis.
12914        let c = caixa_with_nome("");
12915        assert!(
12916            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
12917            "validate_nome must reject nome == \"\" with NomeEmpty — \
12918             the accessor and the validate gate must route through the \
12919             same substrate-primitive typed dispatch on the :nome \
12920             empty-arm",
12921        );
12922        let c = caixa_with_nome("demo");
12923        assert!(
12924            c.validate_nome().is_ok(),
12925            "validate_nome must accept nome == \"demo\" (the canonical \
12926             DNS-1123-label template baseline)",
12927        );
12928    }
12929
12930    #[test]
12931    fn nome_projects_str_by_borrow() {
12932        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
12933        // — the `&str` borrows the underlying `String` storage of the
12934        // required `nome` slot and the accessor must not allocate a
12935        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
12936        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
12937        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
12938        // by-borrow pins on the peer outer top-level [`Caixa`]
12939        // `Option<&str>`-return axes, extended onto the first outer
12940        // top-level [`Caixa`] required-`&str`-return axis — the
12941        // accessor's returned `&str` must borrow from `&self` (the
12942        // returned reference's lifetime is tied to `&self`), and
12943        // calling the accessor twice on the same [`Caixa`] must yield
12944        // the same `&str` verbatim (idempotent, no side effects on
12945        // `&self`).
12946        //
12947        // Pins against a future silent detour that returned an owned
12948        // `String` (which would type-check but silently allocate on
12949        // every call, breaking the zero-cost projection every peer
12950        // sibling accessor carries), an accidental
12951        // `.nome.to_lowercase()` detour that returned a fresh
12952        // allocation through an already-DNS-1123-lowercase-only
12953        // string (breaking a future `const fn` regression), or a
12954        // one-arm-only accessor that returned a canonicalized value
12955        // on some sentinel input (breaking the pass-through invariant
12956        // the sibling required-scalar accessors carry).
12957        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
12958            let c = caixa_with_nome(nome);
12959            let first = c.nome();
12960            let second = c.nome();
12961            assert_eq!(
12962                first, second,
12963                "Caixa::nome must be idempotent — two successive calls \
12964                 on the same &self must return the same &str",
12965            );
12966            assert_eq!(
12967                first, nome,
12968                "Caixa::nome must return :nome verbatim by borrow — \
12969                 got {first}, expected {nome}",
12970            );
12971        }
12972    }
12973
12974    #[test]
12975    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
12976        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
12977        // pinned-version scalar pin: [`Caixa::versao`] must return the
12978        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
12979        // raw `.versao` field access across every representative value
12980        // in the accept-set — the canonical `"0.1.0"` template baseline
12981        // (the same `feira init`-scaffolded default the sibling
12982        // `validate_versao_accepts_canonical_template` positive-control
12983        // gate pins), plus every canonical SemVer-2 shape the sibling
12984        // `validate_versao_accepts_canonical_forms` positive-arm sweep
12985        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
12986        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
12987        // `"10.20.30"`), plus every past-the-guard sentinel for the
12988        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
12989        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
12990        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
12991        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
12992        // `"latest"` the docker-tag-shape footgun — the sentinels pin
12993        // the accessor doesn't silently absorb the refusal cases into a
12994        // template-derived fallback like `"0.1.0"`).
12995        //
12996        // The past-the-guard sentinels pin the accessor doesn't silently
12997        // absorb the refusal cases into a template-derived fallback (a
12998        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
12999        // silently absorb the `VersaoEmpty` refusal at the accessor
13000        // boundary and the validate gate would accept a struct-literal
13001        // `Caixa { versao: "".into(), .. }` — the pin catches that at
13002        // caixa-core build time).
13003        //
13004        // Second outer top-level [`Caixa`] `&str`-return required-scalar
13005        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
13006        // scalar" projection pattern the sibling per-`Caixa`
13007        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
13008        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
13009        // (4127bb6) / per-`:children`
13010        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
13011        // / per-`:upgrade-from`
13012        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
13013        // struct `:versao`-shaped `&str`-return accessor pins on the
13014        // sibling per-typed-slot version-carrier axes, extended onto the
13015        // second outer top-level [`Caixa`] universal-axis required-
13016        // `String`-carry axis so the two universal-axis identity-
13017        // carrying scalars every `defcaixa` form supplies (`:nome` +
13018        // `:versao`) share the same "one typed dispatch per axis" pin
13019        // discipline.
13020        for versao in [
13021            "0.1.0",
13022            "0.0.0",
13023            "1.0.0",
13024            "0.2.0-rc.1",
13025            "1.0.0-alpha.0",
13026            "1.0.0+build.42",
13027            "1.0.0-rc.1+build.42",
13028            "10.20.30",
13029            "",
13030            "v0.1.0",
13031            "0.1",
13032            "^0.1",
13033            "0.1.0.0",
13034            "latest",
13035        ] {
13036            let c = caixa_with_versao(versao);
13037            assert_eq!(
13038                c.versao(),
13039                versao,
13040                "Caixa::versao must return :versao verbatim (got {}, \
13041                 expected {versao})",
13042                c.versao(),
13043            );
13044            assert_eq!(
13045                c.versao(),
13046                c.versao.as_str(),
13047                "Caixa::versao must byte-equal the raw .versao field \
13048                 access across every value in the String accept-set",
13049            );
13050        }
13051    }
13052
13053    #[test]
13054    fn validate_versao_empty_arm_routes_through_accessor() {
13055        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
13056        // must key off [`Caixa::versao`], not the raw `.versao` field
13057        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
13058        // surface the `VersaoEmpty` refusal exactly, and the canonical
13059        // `"0.1.0"` template baseline (the peer positive-arm the sibling
13060        // `validate_versao_accepts_canonical_template` gate carves out)
13061        // must pass validate. The pair jointly pins the accessor +
13062        // validate-gate composition: any future silent detour that had
13063        // the accessor return a fresh `"0.1.0"` on the empty arm
13064        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
13065        // would silently absorb the `VersaoEmpty` refusal at the
13066        // accessor boundary and the validate gate would accept a
13067        // struct-literal `Caixa { versao: "".into(), .. }` — the
13068        // composition pin catches that at caixa-core build time.
13069        //
13070        // Peer of the sibling per-`Caixa`
13071        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
13072        // composition pin on the sibling outer top-level [`Caixa`]
13073        // required-`&str` universal-axis surface — same "the validate /
13074        // shape-gate predicate must route through the substrate-
13075        // primitive typed dispatch" discipline extended onto the peer
13076        // outer top-level [`Caixa`] required-`&str` universal-axis
13077        // pinned-version composition axis, closing the second
13078        // coordinate of the "one canonical typed dispatch per per-Caixa
13079        // required-`&str` universal-axis" discipline.
13080        let c = caixa_with_versao("");
13081        assert!(
13082            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
13083            "validate_versao must reject versao == \"\" with VersaoEmpty — \
13084             the accessor and the validate gate must route through the \
13085             same substrate-primitive typed dispatch on the :versao \
13086             empty-arm",
13087        );
13088        let c = caixa_with_versao("0.1.0");
13089        assert!(
13090            c.validate_versao().is_ok(),
13091            "validate_versao must accept versao == \"0.1.0\" (the \
13092             canonical SemVer-2 template baseline)",
13093        );
13094    }
13095
13096    #[test]
13097    fn versao_projects_str_by_borrow() {
13098        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
13099        // — the `&str` borrows the underlying `String` storage of the
13100        // required `versao` slot and the accessor must not allocate a
13101        // fresh `String` on every call. Peer of the [`Caixa::nome`]
13102        // (e6b7d97) by-borrow pin on the sibling outer top-level
13103        // [`Caixa`] required-`&str`-return axis, extended onto the
13104        // second outer top-level [`Caixa`] required-`&str`-return
13105        // universal-axis pinned-version surface — the accessor's
13106        // returned `&str` must borrow from `&self` (the returned
13107        // reference's lifetime is tied to `&self`), and calling the
13108        // accessor twice on the same [`Caixa`] must yield the same
13109        // `&str` verbatim (idempotent, no side effects on `&self`).
13110        //
13111        // Pins against a future silent detour that returned an owned
13112        // `String` (which would type-check but silently allocate on
13113        // every call, breaking the zero-cost projection every peer
13114        // sibling accessor carries), an accidental
13115        // `semver::Version::parse(&self.versao).unwrap().to_string()`
13116        // detour that returned a canonicalized fresh allocation through
13117        // an already-canonical byte-string (breaking a future `const fn`
13118        // regression and silently absorbing the `VersaoInvalid` refusal
13119        // at the accessor boundary), or a one-arm-only accessor that
13120        // returned a canonicalized value on some sentinel input
13121        // (breaking the pass-through invariant the sibling required-
13122        // scalar accessors carry).
13123        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
13124            let c = caixa_with_versao(versao);
13125            let first = c.versao();
13126            let second = c.versao();
13127            assert_eq!(
13128                first, second,
13129                "Caixa::versao must be idempotent — two successive \
13130                 calls on the same &self must return the same &str",
13131            );
13132            assert_eq!(
13133                first, versao,
13134                "Caixa::versao must return :versao verbatim by borrow \
13135                 — got {first}, expected {versao}",
13136            );
13137        }
13138    }
13139
13140    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
13141        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13142        c.kind = kind;
13143        c
13144    }
13145
13146    #[test]
13147    fn kind_returns_kind_variant_verbatim_across_permutations() {
13148        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
13149        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
13150        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
13151        // the raw `.kind` field access across every variant in the
13152        // closed accept-set (`Biblioteca` — the library kind that
13153        // exports lisp forms; `Binario` — the nix-built executable kind
13154        // under `exe/`; `Servico` — the wasm-component daemon kind
13155        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
13156        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
13157        // composition kind).
13158        //
13159        // Pins against a future silent detour that re-derived the kind
13160        // from a peer axis (an accidental fallback to
13161        // `if !servicos.is_empty() { Servico } else if
13162        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
13163        // collapse that read the code-surface / mesh-slot columns into
13164        // the kind discriminator), a variant remap the operator
13165        // authors on one consumer without the other, or a stale-derive
13166        // detour that substituted [`CaixaKind::Biblioteca`] as the
13167        // default when the field held any other variant (which would
13168        // silently collapse the distinction between "author explicitly
13169        // declared `:kind Servico`" and "author declared any other
13170        // kind" every downstream renderer-dispatch site depends on).
13171        //
13172        // First outer top-level [`Caixa`] `Copy`-return required-enum-
13173        // discriminant accessor pin — opens the "outer [`Caixa`]
13174        // `Copy`-return required-discriminant" projection pattern.
13175        // Sibling in shape to the peer per-`:supervisor`
13176        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
13177        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
13178        // (921fe1b), and per-`:children`
13179        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
13180        // `Copy`-return closed-set-enum discriminant accessor pins on
13181        // the sibling nested-spec typed-slot discriminator axes,
13182        // extended here to the outer top-level [`Caixa`] universal-
13183        // axis surface.
13184        for kind in [
13185            CaixaKind::Biblioteca,
13186            CaixaKind::Binario,
13187            CaixaKind::Servico,
13188            CaixaKind::Supervisor,
13189            CaixaKind::Aplicacao,
13190        ] {
13191            let c = caixa_with_kind(kind);
13192            assert_eq!(
13193                c.kind(),
13194                kind,
13195                "Caixa::kind must return :kind verbatim (got {:?}, \
13196                 expected {kind:?})",
13197                c.kind(),
13198            );
13199            assert_eq!(
13200                c.kind(),
13201                c.kind,
13202                "Caixa::kind accessor and .kind field access must \
13203                 byte-equal — the accessor is the substrate-primitive \
13204                 typed dispatch every downstream kind-gate consumer \
13205                 must route through",
13206            );
13207        }
13208    }
13209
13210    #[test]
13211    fn require_kind_reads_through_lifted_kind_accessor() {
13212        // Two-consumer coherence pin: the [`crate::render::require_kind`]
13213        // entry-gate predicate (the canonical two-line
13214        // `require_kind(caixa, Servico)?` prelude every per-Servico /
13215        // per-Aplicacao renderer runs at its entry-point) and the
13216        // sibling [`crate::render::KindMismatch`] error carrier's
13217        // `actual:` field (which names the offending caixa's variant
13218        // in the diagnostic) must both key off the lifted accessor, so
13219        // any future rebrand on the typed slot's reader shape lands at
13220        // exactly one place. Pins the two-site coherence by exercising
13221        // every off-diagonal `(actual, expected)` pair across the
13222        // closed accept-set — the `KindMismatch { actual, expected }`
13223        // surfaced on the mismatch arm must byte-equal the pair the
13224        // accessor returns for each side.
13225        //
13226        // Peer of the sibling per-`:placement`
13227        // `validate_placement_reads_through_lifted_estrategia_accessor`
13228        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
13229        // `Copy`-return discriminant axis — same "the entry-gate
13230        // predicate and the error carrier's `actual:` field must route
13231        // through the substrate-primitive typed dispatch" discipline
13232        // extended onto the outer top-level [`Caixa`] universal-axis
13233        // discriminant surface.
13234        for expected in [
13235            CaixaKind::Biblioteca,
13236            CaixaKind::Binario,
13237            CaixaKind::Servico,
13238            CaixaKind::Supervisor,
13239            CaixaKind::Aplicacao,
13240        ] {
13241            for actual in [
13242                CaixaKind::Biblioteca,
13243                CaixaKind::Binario,
13244                CaixaKind::Servico,
13245                CaixaKind::Supervisor,
13246                CaixaKind::Aplicacao,
13247            ] {
13248                let c = caixa_with_kind(actual);
13249                let result = crate::render::require_kind(&c, expected);
13250                if expected == actual {
13251                    assert!(
13252                        result.is_ok(),
13253                        "require_kind must accept when actual == expected \
13254                         (actual={actual:?}, expected={expected:?})",
13255                    );
13256                } else {
13257                    let err = result.expect_err("require_kind must reject when actual != expected");
13258                    assert_eq!(
13259                        err.actual,
13260                        c.kind(),
13261                        "KindMismatch.actual must byte-equal Caixa::kind() \
13262                         — the error carrier's `actual:` field reads \
13263                         through the lifted accessor",
13264                    );
13265                    assert_eq!(
13266                        err.expected, expected,
13267                        "KindMismatch.expected must byte-equal the \
13268                         expected variant passed to require_kind",
13269                    );
13270                }
13271            }
13272        }
13273    }
13274
13275    #[test]
13276    fn aplicacao_view_kind_gate_routes_through_accessor() {
13277        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
13278        // must key off [`Caixa::kind`], not the raw `.kind` field
13279        // access. Structurally: a `Caixa { kind: X, .. }` for any
13280        // non-`Aplicacao` variant must fold to `None` on the
13281        // `aplicacao_view` composer (the "kind mismatch → no typed
13282        // view" contract every downstream Aplicacao consumer keys off
13283        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
13284        // `Some(_)`. The pair jointly pins the accessor + view-gate
13285        // composition: any future silent detour that had the accessor
13286        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
13287        // input would silently absorb the kind-mismatch case at the
13288        // accessor boundary and every per-Aplicacao renderer would
13289        // silently render a non-Aplicacao caixa's mesh slots — the
13290        // composition pin catches that at caixa-core build time.
13291        //
13292        // Peer of the sibling per-`Caixa`
13293        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
13294        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
13295        // composition pins on the sibling outer top-level [`Caixa`]
13296        // required-`&str` universal-axis surfaces — same "the
13297        // composer / validate gate must route through the substrate-
13298        // primitive typed dispatch" discipline extended onto the
13299        // outer top-level [`Caixa`] `Copy`-return required-
13300        // discriminant composition axis.
13301        for kind in [
13302            CaixaKind::Biblioteca,
13303            CaixaKind::Binario,
13304            CaixaKind::Servico,
13305            CaixaKind::Supervisor,
13306        ] {
13307            let c = caixa_with_kind(kind);
13308            assert!(
13309                c.aplicacao_view().is_none(),
13310                "aplicacao_view must return None on non-Aplicacao \
13311                 kind {kind:?} — the composer's kind-gate must route \
13312                 through Caixa::kind()",
13313            );
13314        }
13315        let c = caixa_with_kind(CaixaKind::Aplicacao);
13316        assert!(
13317            c.aplicacao_view().is_some(),
13318            "aplicacao_view must return Some on kind Aplicacao — \
13319             the composer's kind-gate must accept the matching arm \
13320             through Caixa::kind()",
13321        );
13322    }
13323
13324    #[test]
13325    fn supervisor_view_kind_gate_routes_through_accessor() {
13326        // Composition pin (mirror of the sibling
13327        // `aplicacao_view_kind_gate_routes_through_accessor` on the
13328        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
13329        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
13330        // field access. A `Caixa { kind: X, .. }` for any non-
13331        // `Supervisor` variant must fold to `None` on the
13332        // `supervisor_view` composer, and a `Caixa { kind:
13333        // Supervisor, .. }` must fold to `Some(_)`. Same peer
13334        // composition pin discipline on the second `_view` composer
13335        // axis.
13336        for kind in [
13337            CaixaKind::Biblioteca,
13338            CaixaKind::Binario,
13339            CaixaKind::Servico,
13340            CaixaKind::Aplicacao,
13341        ] {
13342            let c = caixa_with_kind(kind);
13343            assert!(
13344                c.supervisor_view().is_none(),
13345                "supervisor_view must return None on non-Supervisor \
13346                 kind {kind:?} — the composer's kind-gate must route \
13347                 through Caixa::kind()",
13348            );
13349        }
13350        let mut c = caixa_with_kind(CaixaKind::Supervisor);
13351        // A Supervisor caixa needs a strategy + at least one child to
13352        // fold to a Some(_) that also validates; the composer itself
13353        // requires only the kind arm, so bare kind flip is enough to
13354        // pin the `Some(_)` return, but we populate the minimum
13355        // supervisor shape so a future strengthening of the composer
13356        // to reject an empty spec doesn't false-positive this pin.
13357        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
13358        c.children = vec![crate::supervisor::ChildSpec {
13359            caixa: "child".into(),
13360            versao: "^0.1".into(),
13361            restart: crate::supervisor::RestartPolicy::Permanent,
13362        }];
13363        assert!(
13364            c.supervisor_view().is_some(),
13365            "supervisor_view must return Some on kind Supervisor — \
13366             the composer's kind-gate must accept the matching arm \
13367             through Caixa::kind()",
13368        );
13369    }
13370
13371    #[test]
13372    fn kind_projects_by_copy() {
13373        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
13374        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
13375        // `&self` (the returned value is owned, `Copy`-projected from
13376        // the underlying [`CaixaKind`] storage; two calls on the same
13377        // [`Caixa`] must yield byte-equal values). Peer of the peer
13378        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
13379        // `SupervisorSpec::estrategia` / per-`:children`
13380        // `ChildSpec::restart` `Copy`-return discriminant accessor
13381        // pins on the sibling nested-spec typed-slot discriminator
13382        // axes, extended onto the first outer top-level [`Caixa`]
13383        // required-`Copy`-return axis — pins against a future silent
13384        // detour that returned `&CaixaKind` (which would type-check
13385        // but silently constrain every consumer's callsite to a
13386        // borrow-shaped dispatch, breaking the zero-cost `Copy`
13387        // projection every peer sibling accessor carries).
13388        for kind in [
13389            CaixaKind::Biblioteca,
13390            CaixaKind::Binario,
13391            CaixaKind::Servico,
13392            CaixaKind::Supervisor,
13393            CaixaKind::Aplicacao,
13394        ] {
13395            let c = caixa_with_kind(kind);
13396            let first: CaixaKind = c.kind();
13397            let second: CaixaKind = c.kind();
13398            assert_eq!(
13399                first, second,
13400                "Caixa::kind must be idempotent — two successive \
13401                 calls on the same &self must return the same \
13402                 CaixaKind variant",
13403            );
13404            assert_eq!(
13405                first, kind,
13406                "Caixa::kind must return :kind verbatim by Copy — \
13407                 got {first:?}, expected {kind:?}",
13408            );
13409        }
13410    }
13411
13412    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
13413
13414    #[test]
13415    fn autores_returns_autores_slice_verbatim_across_permutations() {
13416        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
13417        // name-list slice pin: [`Caixa::autores`] must return the
13418        // `:autores` typed [`Vec<String>`] list verbatim as a
13419        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
13420        // access across every representative value in the accept-set —
13421        // `[]` (the "no maintainers declared" arm every existing
13422        // fixture without an `:autores` line carries), `[""]` (a past-
13423        // the-guard sentinel that pins the accessor doesn't perform a
13424        // silent `[""] → []` collapse on the empty-entry arm — validate
13425        // rejects `[""]` through `AutorEmpty` but the accessor must
13426        // ship the raw slot verbatim so a validate-time gate regression
13427        // surfaces at the caixa-helm emit boundary rather than being
13428        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
13429        // canonical single-maintainer form every `feira init` template
13430        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
13431        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
13432        // (the canonical RFC-5322 `<name> <email>` form the
13433        // `is_chart_maintainer_name_shape` predicate accepts), and
13434        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
13435        // sentinel — validate rejects through `AutorDuplicate` but the
13436        // accessor must ship the raw slot verbatim).
13437        //
13438        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
13439        // pin on the substrate primitive — opens the "outer [`Caixa`]
13440        // `&[T]` slice" projection pattern the sibling per-`Caixa`
13441        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
13442        // / `:servicos` / `:upgrade-from` / `:children` future lifts
13443        // fold on. Sibling in shape to the peer per-`:supervisor`
13444        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
13445        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
13446        // (a6e18d7), per-`:membros`
13447        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
13448        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
13449        // (0dcc926), and per-`:upgrade-from :instructions`
13450        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
13451        // `&[T]`-return slice accessor pins on the sibling per-M2 /
13452        // per-M3 typed-slot list axes, extended onto the outer top-
13453        // level [`Caixa`] universal-axis surface. Pins against a future
13454        // silent detour that returned an owned `Vec<String>` (which
13455        // would type-check but silently clone on every accessor call,
13456        // breaking the zero-cost projection every peer sibling slice
13457        // accessor carries), a `[""] → []` collapse (which would
13458        // silently absorb the `AutorEmpty` refusal case at the accessor
13459        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
13460        // would silently absorb the `AutorDuplicate` refusal case at
13461        // the accessor boundary and the caixa-helm `maintainers:` fold
13462        // would silently render a dedupped list on a struct-literal
13463        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
13464        for autores in [
13465            vec![],
13466            vec![""],
13467            vec!["pleme-io"],
13468            vec!["alice", "bob"],
13469            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
13470            vec!["pleme-io", "pleme-io"],
13471        ] {
13472            let c = caixa_with_autores(autores.clone());
13473            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
13474            assert_eq!(
13475                c.autores(),
13476                expected.as_slice(),
13477                "Caixa::autores must return :autores verbatim (got {:?}, \
13478                 expected {expected:?})",
13479                c.autores(),
13480            );
13481            assert_eq!(
13482                c.autores(),
13483                c.autores.as_slice(),
13484                "Caixa::autores must byte-equal the raw \
13485                 `self.autores.as_slice()` field access across every \
13486                 value in the Vec<String> accept-set",
13487            );
13488        }
13489    }
13490
13491    #[test]
13492    fn validate_autores_empty_entry_arm_routes_through_accessor() {
13493        // Composition pin: [`Caixa::validate_autores`]'s per-entry
13494        // empty-arm gate must key off [`Caixa::autores`], not the raw
13495        // `&self.autores` field-borrow walk. Structurally: a
13496        // `Caixa { autores: vec!["".into()], .. }` must surface the
13497        // `AutorEmpty` refusal exactly, and a
13498        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
13499        // canonical single-maintainer form) must pass validate. The
13500        // pair jointly pins the accessor + validate-gate composition:
13501        // any future silent detour that had the accessor return an
13502        // empty slice on the `[""]` arm (a
13503        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
13504        // would silently absorb the `AutorEmpty` refusal at the
13505        // accessor boundary and the validate gate would accept a
13506        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
13507        // the composition pin catches that at caixa-core build time.
13508        //
13509        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
13510        // accessor-composition pin
13511        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
13512        // sibling `Option<&str>`-composition axis and the
13513        // per-`:politicas :circuit-breaker`
13514        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
13515        // accessor-composition pin
13516        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
13517        // on the sibling required-`u32`-composition axis — same "the
13518        // validate / shape-gate predicate must route through the
13519        // substrate-primitive typed dispatch" discipline extended onto
13520        // the outer top-level [`Caixa`] universal-axis `&[T]`-
13521        // composition surface.
13522        let c = caixa_with_autores(vec![""]);
13523        assert!(
13524            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
13525            "validate_autores must reject autores == vec![\"\"] with \
13526             AutorEmpty — the accessor and the validate gate must \
13527             route through the same substrate-primitive typed dispatch \
13528             on the :autores per-entry empty arm",
13529        );
13530        let c = caixa_with_autores(vec!["pleme-io"]);
13531        assert!(
13532            c.validate_autores().is_ok(),
13533            "validate_autores must accept autores == vec![\"pleme-io\"] \
13534             (the canonical single-maintainer shape every `feira init` \
13535             template scaffolds)",
13536        );
13537    }
13538
13539    #[test]
13540    fn autores_projects_slice_by_borrow() {
13541        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
13542        // borrow — the returned slice borrows the underlying
13543        // `Vec<String>` storage of the `:autores` slot and the
13544        // accessor must not clone the backing `Vec` on every call.
13545        // Peer of the per-`:membros`
13546        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
13547        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
13548        // (0dcc926) / per-`:placement`
13549        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
13550        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
13551        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
13552        // typed-slot `&[T]`-return axes, extended onto the outer top-
13553        // level [`Caixa`] universal-axis `&[String]` shape — the
13554        // accessor's returned slice must borrow from `&self` (the
13555        // returned reference's lifetime is tied to `&self`), and
13556        // calling the accessor twice on the same [`Caixa`] must yield
13557        // slices that are pointer-equal (the underlying byte-buffer is
13558        // the storage `Vec`'s allocation, not a fresh copy) as well as
13559        // value-equal (idempotent, no side effects on `&self`).
13560        //
13561        // Pins against a future silent detour that returned an owned
13562        // `Vec<String>` (which would type-check but silently clone on
13563        // every call, breaking the zero-cost projection every peer
13564        // sibling slice accessor carries), a `&Vec<String>` return
13565        // (which would leak the backing `Vec`'s grow/push/reserve
13566        // surface no downstream consumer reaches for), or a one-arm-
13567        // only accessor that returned a saturating value on some
13568        // sentinel input (breaking the pass-through invariant the
13569        // sibling slice accessors carry).
13570        for autores in [
13571            vec![],
13572            vec!["pleme-io"],
13573            vec!["alice", "bob"],
13574            vec!["pleme-io", "pleme-io"],
13575        ] {
13576            let c = caixa_with_autores(autores.clone());
13577            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
13578            let first = c.autores();
13579            let second = c.autores();
13580            assert_eq!(
13581                first, second,
13582                "Caixa::autores must be idempotent — two successive \
13583                 calls on the same &self must return the same \
13584                 &[String]",
13585            );
13586            assert_eq!(
13587                first.as_ptr(),
13588                second.as_ptr(),
13589                "Caixa::autores must borrow the underlying Vec<String> \
13590                 storage — two successive calls must return slices \
13591                 with the same backing pointer (a fresh Vec<String> \
13592                 clone would change the pointer on every call)",
13593            );
13594            assert_eq!(
13595                first,
13596                expected.as_slice(),
13597                "Caixa::autores must return :autores verbatim by \
13598                 borrow — got {first:?}, expected {expected:?}",
13599            );
13600        }
13601    }
13602
13603    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
13604
13605    #[test]
13606    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
13607        // The canonical per-`Caixa` `:etiquetas` universal-axis
13608        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
13609        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
13610        // as a `&[String]`, byte-equal to the raw
13611        // `self.etiquetas.as_slice()` access across every representative
13612        // value in the accept-set — `[]` (the "no tags declared" arm
13613        // every existing fixture without an `:etiquetas` line carries),
13614        // `[""]` (a past-the-guard sentinel that pins the accessor
13615        // doesn't perform a silent `[""] → []` collapse on the empty-
13616        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
13617        // but the accessor must ship the raw slot verbatim so a
13618        // validate-time gate regression surfaces at the caixa-helm emit
13619        // boundary rather than being silently absorbed into a keyword-
13620        // drop), `["demo"]` (the canonical single-tag form every
13621        // `feira init` template scaffolds), `["example", "aplicacao",
13622        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
13623        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
13624        // (a past-the-guard duplicate sentinel — validate rejects
13625        // through `EtiquetaDuplicate` but the accessor must ship the
13626        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
13627        // at chart-render time isn't silently promoted into the
13628        // accessor boundary and struct-literal
13629        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
13630        // fixtures continue to expose the duplicate at the accessor).
13631        //
13632        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
13633        // pin on the substrate primitive — folds on the "outer
13634        // [`Caixa`] `&[T]` slice" projection pattern
13635        // `autores_returns_autores_slice_verbatim_across_permutations`
13636        // (b5d813f) opened, sibling in shape and idiom. Pins against a
13637        // future silent detour that returned an owned `Vec<String>`
13638        // (which would type-check but silently clone on every accessor
13639        // call, breaking the zero-cost projection every peer sibling
13640        // slice accessor carries), a `[""] → []` collapse (which would
13641        // silently absorb the `EtiquetaEmpty` refusal case at the
13642        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
13643        // (which would silently absorb the `EtiquetaDuplicate` refusal
13644        // case at the accessor boundary — the caixa-helm chart-render
13645        // `BTreeSet::collect` dedup is downstream of the accessor and
13646        // must not be silently promoted into it).
13647        for etiquetas in [
13648            vec![],
13649            vec![""],
13650            vec!["demo"],
13651            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
13652            vec!["demo", "demo"],
13653        ] {
13654            let c = caixa_with_etiquetas(etiquetas.clone());
13655            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
13656            assert_eq!(
13657                c.etiquetas(),
13658                expected.as_slice(),
13659                "Caixa::etiquetas must return :etiquetas verbatim (got \
13660                 {:?}, expected {expected:?})",
13661                c.etiquetas(),
13662            );
13663            assert_eq!(
13664                c.etiquetas(),
13665                c.etiquetas.as_slice(),
13666                "Caixa::etiquetas must byte-equal the raw \
13667                 `self.etiquetas.as_slice()` field access across every \
13668                 value in the Vec<String> accept-set",
13669            );
13670        }
13671    }
13672
13673    #[test]
13674    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
13675        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
13676        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
13677        // `&self.etiquetas` field-borrow walk. Structurally: a
13678        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
13679        // `EtiquetaEmpty` refusal exactly, and a
13680        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
13681        // single-tag form) must pass validate. The pair jointly pins
13682        // the accessor + validate-gate composition: any future silent
13683        // detour that had the accessor return an empty slice on the
13684        // `[""]` arm (a
13685        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
13686        // silently absorb the `EtiquetaEmpty` refusal at the accessor
13687        // boundary and the validate gate would accept a struct-literal
13688        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
13689        // pin catches that at caixa-core build time.
13690        //
13691        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
13692        // through_accessor` (b5d813f) accessor-composition pin on the
13693        // sibling `&[T]`-composition axis — same "the validate / shape-
13694        // gate predicate must route through the substrate-primitive
13695        // typed dispatch" discipline extended onto the sibling outer
13696        // top-level [`Caixa`] `&[T]`-composition surface.
13697        let c = caixa_with_etiquetas(vec![""]);
13698        assert!(
13699            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
13700            "validate_etiquetas must reject etiquetas == vec![\"\"] \
13701             with EtiquetaEmpty — the accessor and the validate gate \
13702             must route through the same substrate-primitive typed \
13703             dispatch on the :etiquetas per-entry empty arm",
13704        );
13705        let c = caixa_with_etiquetas(vec!["demo"]);
13706        assert!(
13707            c.validate_etiquetas().is_ok(),
13708            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
13709             (the canonical single-tag shape every `feira init` \
13710             template scaffolds)",
13711        );
13712    }
13713
13714    #[test]
13715    fn etiquetas_projects_slice_by_borrow() {
13716        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
13717        // by borrow — the returned slice borrows the underlying
13718        // `Vec<String>` storage of the `:etiquetas` slot and the
13719        // accessor must not clone the backing `Vec` on every call.
13720        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13721        // (b5d813f) by-borrow pin on the sibling outer top-level
13722        // [`Caixa`] `&[String]`-return axis — the accessor's returned
13723        // slice must borrow from `&self` (the returned reference's
13724        // lifetime is tied to `&self`), and calling the accessor twice
13725        // on the same [`Caixa`] must yield slices that are pointer-
13726        // equal (the underlying byte-buffer is the storage `Vec`'s
13727        // allocation, not a fresh copy) as well as value-equal
13728        // (idempotent, no side effects on `&self`).
13729        //
13730        // Pins against a future silent detour that returned an owned
13731        // `Vec<String>` (which would type-check but silently clone on
13732        // every call, breaking the zero-cost projection every peer
13733        // sibling slice accessor carries), a `&Vec<String>` return
13734        // (which would leak the backing `Vec`'s grow/push/reserve
13735        // surface no downstream consumer reaches for), or a one-arm-
13736        // only accessor that returned a saturating value on some
13737        // sentinel input (breaking the pass-through invariant the
13738        // sibling slice accessors carry).
13739        for etiquetas in [
13740            vec![],
13741            vec!["demo"],
13742            vec!["example", "aplicacao", "mesh"],
13743            vec!["demo", "demo"],
13744        ] {
13745            let c = caixa_with_etiquetas(etiquetas.clone());
13746            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
13747            let first = c.etiquetas();
13748            let second = c.etiquetas();
13749            assert_eq!(
13750                first, second,
13751                "Caixa::etiquetas must be idempotent — two successive \
13752                 calls on the same &self must return the same \
13753                 &[String]",
13754            );
13755            assert_eq!(
13756                first.as_ptr(),
13757                second.as_ptr(),
13758                "Caixa::etiquetas must borrow the underlying \
13759                 Vec<String> storage — two successive calls must \
13760                 return slices with the same backing pointer (a fresh \
13761                 Vec<String> clone would change the pointer on every \
13762                 call)",
13763            );
13764            assert_eq!(
13765                first,
13766                expected.as_slice(),
13767                "Caixa::etiquetas must return :etiquetas verbatim by \
13768                 borrow — got {first:?}, expected {expected:?}",
13769            );
13770        }
13771    }
13772
13773    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
13774
13775    #[test]
13776    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
13777        // The canonical per-`Caixa` `:bibliotecas` universal-axis
13778        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
13779        // must return the `:bibliotecas` typed [`Vec<String>`] list
13780        // verbatim as a `&[String]`, byte-equal to the raw
13781        // `self.bibliotecas.as_slice()` access across every
13782        // representative value in the accept-set — `[]` (the "no
13783        // libraries declared" arm every `:kind` other than `Biblioteca`
13784        // + every `Biblioteca` relying on the canonical
13785        // `lib/<nome>.lisp` implicit-default path carries; the
13786        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
13787        // fires exactly on this empty-slot + `Biblioteca`-kind
13788        // combination), `[""]` (a past-the-guard sentinel that pins
13789        // the accessor doesn't perform a silent `[""] → []` collapse
13790        // on the empty-entry arm — validate rejects `[""]` through
13791        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
13792        // must ship the raw slot verbatim so a validate-time gate
13793        // regression surfaces at the `feira build` phase-1 parse
13794        // boundary rather than being silently absorbed into a
13795        // library-drop), `["lib/demo.lisp"]` (the canonical single-
13796        // entry form `Caixa::template` scaffolds and every `feira init`
13797        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
13798        // (the canonical multi-library form the
13799        // `validate_code_paths_accepts_explicit_relative_paths_on_
13800        // every_slot` fixture emits), and `["lib/foo.lisp",
13801        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
13802        // validate rejects through `CodePathDuplicate { slot:
13803        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
13804        // but the accessor must ship the raw slot verbatim so the
13805        // `feira build` `for entry in caixa.bibliotecas()` parse walk
13806        // sees the duplicate at the accessor boundary and struct-
13807        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
13808        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
13809        // the duplicate at the accessor).
13810        //
13811        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
13812        // pin on the substrate primitive — folds on the "outer
13813        // [`Caixa`] `&[T]` slice" projection pattern
13814        // `autores_returns_autores_slice_verbatim_across_permutations`
13815        // (b5d813f) opened and
13816        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13817        // (78c7d3c) folded on, sibling in shape and idiom. Pins
13818        // against a future silent detour that returned an owned
13819        // `Vec<String>` (which would type-check but silently clone on
13820        // every accessor call, breaking the zero-cost projection
13821        // every peer sibling slice accessor carries), a `[""] → []`
13822        // collapse (which would silently absorb the `CodePathEmpty`
13823        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
13824        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
13825        // would silently absorb the `CodePathDuplicate` refusal case
13826        // at the accessor boundary — the per-slot set-not-multiset
13827        // gate is downstream of the accessor and must not be silently
13828        // promoted into it).
13829        for bibliotecas in [
13830            vec![],
13831            vec![""],
13832            vec!["lib/demo.lisp"],
13833            vec!["lib/demo.lisp", "lib/helpers.lisp"],
13834            vec!["lib/foo.lisp", "lib/foo.lisp"],
13835        ] {
13836            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
13837            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
13838            assert_eq!(
13839                c.bibliotecas(),
13840                expected.as_slice(),
13841                "Caixa::bibliotecas must return :bibliotecas verbatim \
13842                 (got {:?}, expected {expected:?})",
13843                c.bibliotecas(),
13844            );
13845            assert_eq!(
13846                c.bibliotecas(),
13847                c.bibliotecas.as_slice(),
13848                "Caixa::bibliotecas must byte-equal the raw \
13849                 `self.bibliotecas.as_slice()` field access across \
13850                 every value in the Vec<String> accept-set",
13851            );
13852        }
13853    }
13854
13855    #[test]
13856    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
13857        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
13858        // empty-arm gate on the `:bibliotecas` slot must key off
13859        // [`Caixa::bibliotecas`], not a divergent raw
13860        // `&self.bibliotecas` field-borrow walk. Structurally: a
13861        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
13862        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
13863        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
13864        // into()], .. }` (the canonical single-library form
13865        // `Caixa::template` scaffolds) must pass validate. The pair
13866        // jointly pins the accessor + validate-gate composition: any
13867        // future silent detour that had the accessor return an empty
13868        // slice on the `[""]` arm (a `.iter().filter(|s|
13869        // !s.is_empty()).collect()` collapse) would silently absorb
13870        // the `CodePathEmpty` refusal at the accessor boundary and
13871        // the validate gate would accept a struct-literal
13872        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
13873        // composition pin catches that at caixa-core build time.
13874        //
13875        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
13876        // through_accessor` (b5d813f) and
13877        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13878        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
13879        // composition axes — same "the validate / shape-gate
13880        // predicate must route through the substrate-primitive typed
13881        // dispatch" discipline extended onto the sibling outer top-
13882        // level [`Caixa`] `&[T]`-composition surface. Nominally the
13883        // in-tree `validate_code_paths` production body still keys
13884        // off the internal `[(":bibliotecas", &self.bibliotecas,
13885        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
13886        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
13887        // (the tuple's homogeneous slice-typed shape blocks a per-
13888        // element accessor swap in isolation — a future companion
13889        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
13890        // `&[T]` slice-accessor axis closes that tuple onto the
13891        // triple of typed dispatches as a unit); the composition pin
13892        // catches any future accessor-side silent filter drop against
13893        // that eventual tuple-closure regardless of whether the
13894        // `:bibliotecas` slot is threaded through the accessor or the
13895        // raw field access at the tuple's construction site.
13896        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
13897        assert!(
13898            matches!(
13899                c.validate_code_paths(),
13900                Err(ManifestError::CodePathEmpty {
13901                    slot: ":bibliotecas"
13902                })
13903            ),
13904            "validate_code_paths must reject bibliotecas == vec![\"\"] \
13905             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
13906             accessor and the validate gate must route through the \
13907             same substrate-primitive typed dispatch on the \
13908             :bibliotecas per-entry empty arm",
13909        );
13910        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
13911        assert!(
13912            c.validate_code_paths().is_ok(),
13913            "validate_code_paths must accept bibliotecas == \
13914             vec![\"lib/demo.lisp\"] (the canonical single-library \
13915             shape every `feira init` template scaffolds)",
13916        );
13917    }
13918
13919    #[test]
13920    fn bibliotecas_projects_slice_by_borrow() {
13921        // The by-borrow pin: [`Caixa::bibliotecas`] returns
13922        // `&[String]` by borrow — the returned slice borrows the
13923        // underlying `Vec<String>` storage of the `:bibliotecas` slot
13924        // and the accessor must not clone the backing `Vec` on every
13925        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13926        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
13927        // by-borrow pins on the sibling outer top-level [`Caixa`]
13928        // `&[String]`-return axes — the accessor's returned slice
13929        // must borrow from `&self` (the returned reference's lifetime
13930        // is tied to `&self`), and calling the accessor twice on the
13931        // same [`Caixa`] must yield slices that are pointer-equal
13932        // (the underlying byte-buffer is the storage `Vec`'s
13933        // allocation, not a fresh copy) as well as value-equal
13934        // (idempotent, no side effects on `&self`).
13935        //
13936        // Pins against a future silent detour that returned an owned
13937        // `Vec<String>` (which would type-check but silently clone on
13938        // every call, breaking the zero-cost projection every peer
13939        // sibling slice accessor carries), a `&Vec<String>` return
13940        // (which would leak the backing `Vec`'s grow/push/reserve
13941        // surface no downstream consumer reaches for), or a one-arm-
13942        // only accessor that returned a saturating value on some
13943        // sentinel input (breaking the pass-through invariant the
13944        // sibling slice accessors carry).
13945        for bibliotecas in [
13946            vec![],
13947            vec!["lib/demo.lisp"],
13948            vec!["lib/demo.lisp", "lib/helpers.lisp"],
13949            vec!["lib/foo.lisp", "lib/foo.lisp"],
13950        ] {
13951            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
13952            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
13953            let first = c.bibliotecas();
13954            let second = c.bibliotecas();
13955            assert_eq!(
13956                first, second,
13957                "Caixa::bibliotecas must be idempotent — two \
13958                 successive calls on the same &self must return the \
13959                 same &[String]",
13960            );
13961            assert_eq!(
13962                first.as_ptr(),
13963                second.as_ptr(),
13964                "Caixa::bibliotecas must borrow the underlying \
13965                 Vec<String> storage — two successive calls must \
13966                 return slices with the same backing pointer (a \
13967                 fresh Vec<String> clone would change the pointer on \
13968                 every call)",
13969            );
13970            assert_eq!(
13971                first,
13972                expected.as_slice(),
13973                "Caixa::bibliotecas must return :bibliotecas verbatim \
13974                 by borrow — got {first:?}, expected {expected:?}",
13975            );
13976        }
13977    }
13978
13979    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
13980
13981    #[test]
13982    fn exe_returns_exe_slice_verbatim_across_permutations() {
13983        // The canonical per-`Caixa` `:exe` universal-axis
13984        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
13985        // must return the `:exe` typed [`Vec<String>`] list verbatim as
13986        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
13987        // access across every representative value in the accept-set —
13988        // `[]` (the "no executable declared" arm every `:kind` other
13989        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
13990        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
13991        // + `Binario`-kind combination), `[""]` (a past-the-guard
13992        // sentinel that pins the accessor doesn't perform a silent
13993        // `[""] → []` collapse on the empty-entry arm — validate rejects
13994        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
13995        // accessor must ship the raw slot verbatim so a validate-time
13996        // gate regression surfaces at the layout / `feira nix` boundary
13997        // rather than being silently absorbed into an executable-drop),
13998        // `["exe/cli"]` (the canonical single-entry Binario form every
13999        // in-tree `caixa_with_code_paths` positive control uses),
14000        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
14001        // form the `validate_code_paths_accepts_explicit_relative_paths_
14002        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
14003        // (a past-the-guard duplicate sentinel — validate rejects
14004        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
14005        // set-not-multiset gate, but the accessor must ship the raw
14006        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
14007        // into(), "exe/cli".into()], .. }` fixtures continue to expose
14008        // the duplicate at the accessor).
14009        //
14010        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
14011        // pin on the substrate primitive — folds on the "outer
14012        // [`Caixa`] `&[T]` slice" projection pattern
14013        // `autores_returns_autores_slice_verbatim_across_permutations`
14014        // (b5d813f) opened,
14015        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14016        // (78c7d3c) folded on, and
14017        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
14018        // (8a36c23) closed the universal-axis text-tag family of.
14019        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
14020        // the sibling `:servicos` future lift closes onto. Pins against
14021        // a future silent detour that returned an owned `Vec<String>`
14022        // (which would type-check but silently clone on every accessor
14023        // call, breaking the zero-cost projection every peer sibling
14024        // slice accessor carries), a `[""] → []` collapse (which would
14025        // silently absorb the `CodePathEmpty` refusal case at the
14026        // accessor boundary), or an `["exe/cli", "exe/cli"] →
14027        // ["exe/cli"]` dedup collapse (which would silently absorb the
14028        // `CodePathDuplicate` refusal case at the accessor boundary —
14029        // the per-slot set-not-multiset gate is downstream of the
14030        // accessor and must not be silently promoted into it).
14031        for exe in [
14032            vec![],
14033            vec![""],
14034            vec!["exe/cli"],
14035            vec!["exe/cli", "exe/serve"],
14036            vec!["exe/cli", "exe/cli"],
14037        ] {
14038            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
14039            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
14040            assert_eq!(
14041                c.exe(),
14042                expected.as_slice(),
14043                "Caixa::exe must return :exe verbatim (got {:?}, \
14044                 expected {expected:?})",
14045                c.exe(),
14046            );
14047            assert_eq!(
14048                c.exe(),
14049                c.exe.as_slice(),
14050                "Caixa::exe must byte-equal the raw \
14051                 `self.exe.as_slice()` field access across every value \
14052                 in the Vec<String> accept-set",
14053            );
14054        }
14055    }
14056
14057    #[test]
14058    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
14059        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
14060        // empty-arm gate on the `:exe` slot must key off
14061        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
14062        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
14063        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
14064        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
14065        // (the canonical single-executable form every in-tree
14066        // `caixa_with_code_paths` positive control uses) must pass
14067        // validate. The pair jointly pins the accessor + validate-gate
14068        // composition: any future silent detour that had the accessor
14069        // return an empty slice on the `[""]` arm (a
14070        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
14071        // silently absorb the `CodePathEmpty` refusal at the accessor
14072        // boundary and the validate gate would accept a struct-literal
14073        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
14074        // catches that at caixa-core build time.
14075        //
14076        // Peer of the per-`Caixa`
14077        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
14078        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
14079        // (b5d813f), and
14080        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
14081        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
14082        // composition axes — same "the validate / shape-gate predicate
14083        // must route through the substrate-primitive typed dispatch"
14084        // discipline extended onto the sibling outer top-level [`Caixa`]
14085        // `&[T]`-composition surface. Nominally the in-tree
14086        // `validate_code_paths` production body still keys off the
14087        // internal `[(":bibliotecas", &self.bibliotecas,
14088        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
14089        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
14090        // (the tuple's homogeneous slice-typed shape blocks a per-
14091        // element accessor swap in isolation — a future companion lift
14092        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
14093        // accessor axis closes that tuple onto the triple of typed
14094        // dispatches as a unit); the composition pin catches any future
14095        // accessor-side silent filter drop against that eventual tuple-
14096        // closure regardless of whether the `:exe` slot is threaded
14097        // through the accessor or the raw field access at the tuple's
14098        // construction site.
14099        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
14100        assert!(
14101            matches!(
14102                c.validate_code_paths(),
14103                Err(ManifestError::CodePathEmpty { slot: ":exe" })
14104            ),
14105            "validate_code_paths must reject exe == vec![\"\"] \
14106             with CodePathEmpty {{ slot: \":exe\" }} — the \
14107             accessor and the validate gate must route through the \
14108             same substrate-primitive typed dispatch on the \
14109             :exe per-entry empty arm",
14110        );
14111        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
14112        assert!(
14113            c.validate_code_paths().is_ok(),
14114            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
14115             (the canonical single-executable shape every in-tree \
14116             `caixa_with_code_paths` positive control uses)",
14117        );
14118    }
14119
14120    #[test]
14121    fn exe_projects_slice_by_borrow() {
14122        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
14123        // borrow — the returned slice borrows the underlying
14124        // `Vec<String>` storage of the `:exe` slot and the accessor
14125        // must not clone the backing `Vec` on every call. Peer of the
14126        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
14127        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
14128        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
14129        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
14130        // return axes — the accessor's returned slice must borrow from
14131        // `&self` (the returned reference's lifetime is tied to
14132        // `&self`), and calling the accessor twice on the same
14133        // [`Caixa`] must yield slices that are pointer-equal (the
14134        // underlying byte-buffer is the storage `Vec`'s allocation,
14135        // not a fresh copy) as well as value-equal (idempotent, no
14136        // side effects on `&self`).
14137        //
14138        // Pins against a future silent detour that returned an owned
14139        // `Vec<String>` (which would type-check but silently clone on
14140        // every call, breaking the zero-cost projection every peer
14141        // sibling slice accessor carries), a `&Vec<String>` return
14142        // (which would leak the backing `Vec`'s grow/push/reserve
14143        // surface no downstream consumer reaches for), or a one-arm-
14144        // only accessor that returned a saturating value on some
14145        // sentinel input (breaking the pass-through invariant the
14146        // sibling slice accessors carry).
14147        for exe in [
14148            vec![],
14149            vec!["exe/cli"],
14150            vec!["exe/cli", "exe/serve"],
14151            vec!["exe/cli", "exe/cli"],
14152        ] {
14153            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
14154            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
14155            let first = c.exe();
14156            let second = c.exe();
14157            assert_eq!(
14158                first, second,
14159                "Caixa::exe must be idempotent — two successive calls \
14160                 on the same &self must return the same &[String]",
14161            );
14162            assert_eq!(
14163                first.as_ptr(),
14164                second.as_ptr(),
14165                "Caixa::exe must borrow the underlying Vec<String> \
14166                 storage — two successive calls must return slices \
14167                 with the same backing pointer (a fresh Vec<String> \
14168                 clone would change the pointer on every call)",
14169            );
14170            assert_eq!(
14171                first,
14172                expected.as_slice(),
14173                "Caixa::exe must return :exe verbatim by borrow — \
14174                 got {first:?}, expected {expected:?}",
14175            );
14176        }
14177    }
14178
14179    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
14180
14181    #[test]
14182    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
14183        // The canonical per-`Caixa` `:servicos` universal-axis
14184        // ComputeUnit-CR-YAML-entry-path-list slice pin:
14185        // [`Caixa::servicos`] must return the `:servicos` typed
14186        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
14187        // the raw `self.servicos.as_slice()` access across every
14188        // representative value in the accept-set — `[]` (the "no
14189        // ComputeUnit-CR declared" arm every `:kind` other than
14190        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
14191        // `ServicoWithoutServicos` arm-gate fires exactly on this
14192        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
14193        // guard sentinel that pins the accessor doesn't perform a
14194        // silent `[""] → []` collapse on the empty-entry arm — validate
14195        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
14196        // but the accessor must ship the raw slot verbatim so a
14197        // validate-time gate regression surfaces at the layout /
14198        // per-Servico renderer boundary rather than being silently
14199        // absorbed into a component-drop),
14200        // `["servicos/demo.computeunit.yaml"]` (the canonical
14201        // singleton V0-shape every in-tree `caixa_with_code_paths`
14202        // positive control uses; the same shape
14203        // [`crate::require_single_servico`] admits),
14204        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
14205        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
14206        // singularity gate rejects through `ServicoCountMismatch
14207        // { count: 2 }` but the accessor must ship the raw slot
14208        // verbatim so struct-literal `Caixa { servicos: vec![...,
14209        // ...], .. }` fixtures continue to expose the count at the
14210        // accessor), and `["servicos/a.computeunit.yaml",
14211        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
14212        // sentinel — validate rejects through
14213        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
14214        // set-not-multiset gate, but the accessor must ship the raw
14215        // slot verbatim so struct-literal fixtures continue to expose
14216        // the duplicate at the accessor).
14217        //
14218        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
14219        // slice accessor pin on the substrate primitive — folds on the
14220        // "outer [`Caixa`] `&[T]` slice" projection pattern
14221        // `autores_returns_autores_slice_verbatim_across_permutations`
14222        // (b5d813f) opened,
14223        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14224        // (78c7d3c) folded on,
14225        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
14226        // (8a36c23) closed the universal-axis text-tag family of, and
14227        // `exe_returns_exe_slice_verbatim_across_permutations`
14228        // (65d9527) opened the foreign-code-slot sub-family of. Closes
14229        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
14230        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
14231        // `:servicos`) now each carries a substrate-canonical slice
14232        // accessor. Pins against a future silent detour that returned
14233        // an owned `Vec<String>` (which would type-check but silently
14234        // clone on every accessor call, breaking the zero-cost
14235        // projection every peer sibling slice accessor carries), a
14236        // `[""] → []` collapse (which would silently absorb the
14237        // `CodePathEmpty` refusal case at the accessor boundary), an
14238        // `[a, a] → [a]` dedup collapse (which would silently absorb
14239        // the `CodePathDuplicate` refusal case at the accessor
14240        // boundary — the per-slot set-not-multiset gate is downstream
14241        // of the accessor and must not be silently promoted into it),
14242        // or a `[a, b] → [a]` singleton collapse (which would silently
14243        // absorb the V0 `ServicoCountMismatch` refusal case at the
14244        // accessor boundary — the V0 singularity gate is downstream of
14245        // the accessor and must not be silently promoted into it).
14246        for servicos in [
14247            vec![],
14248            vec![""],
14249            vec!["servicos/demo.computeunit.yaml"],
14250            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
14251            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
14252        ] {
14253            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
14254            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
14255            assert_eq!(
14256                c.servicos(),
14257                expected.as_slice(),
14258                "Caixa::servicos must return :servicos verbatim (got \
14259                 {:?}, expected {expected:?})",
14260                c.servicos(),
14261            );
14262            assert_eq!(
14263                c.servicos(),
14264                c.servicos.as_slice(),
14265                "Caixa::servicos must byte-equal the raw \
14266                 `self.servicos.as_slice()` field access across every \
14267                 value in the Vec<String> accept-set",
14268            );
14269        }
14270    }
14271
14272    #[test]
14273    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
14274        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
14275        // empty-arm gate on the `:servicos` slot must key off
14276        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
14277        // field-borrow walk. Structurally: a `Caixa { servicos:
14278        // vec!["".into()], .. }` must surface the `CodePathEmpty
14279        // { slot: ":servicos" }` refusal exactly, and a `Caixa
14280        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
14281        // .. }` (the canonical singleton V0-shape every in-tree
14282        // `caixa_with_code_paths` positive control uses) must pass
14283        // validate. The pair jointly pins the accessor + validate-gate
14284        // composition: any future silent detour that had the accessor
14285        // return an empty slice on the `[""]` arm (a `.iter().filter
14286        // (|s| !s.is_empty()).collect()` collapse) would silently
14287        // absorb the `CodePathEmpty` refusal at the accessor boundary
14288        // and the validate gate would accept a struct-literal
14289        // `Caixa { servicos: vec!["".into()], .. }` — the composition
14290        // pin catches that at caixa-core build time.
14291        //
14292        // Peer of the per-`Caixa`
14293        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
14294        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
14295        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
14296        // (b5d813f), and
14297        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
14298        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
14299        // composition axes — same "the validate / shape-gate predicate
14300        // must route through the substrate-primitive typed dispatch"
14301        // discipline extended onto the sibling outer top-level
14302        // [`Caixa`] `&[T]`-composition surface, closing the trio of
14303        // code-surface accessor-composition pins on the same axis.
14304        // Nominally the in-tree `validate_code_paths` production body
14305        // still keys off the internal
14306        // `[(":bibliotecas", &self.bibliotecas,
14307        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
14308        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
14309        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
14310        // per-element accessor swap in isolation — a future companion
14311        // lift promotes the tuple's element type to `&[String]` and
14312        // threads the triple of typed dispatches through as a unit);
14313        // the composition pin catches any future accessor-side silent
14314        // filter drop against that eventual tuple-closure regardless
14315        // of whether the `:servicos` slot is threaded through the
14316        // accessor or the raw field access at the tuple's construction
14317        // site.
14318        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
14319        assert!(
14320            matches!(
14321                c.validate_code_paths(),
14322                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
14323            ),
14324            "validate_code_paths must reject servicos == vec![\"\"] \
14325             with CodePathEmpty {{ slot: \":servicos\" }} — the \
14326             accessor and the validate gate must route through the \
14327             same substrate-primitive typed dispatch on the \
14328             :servicos per-entry empty arm",
14329        );
14330        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
14331        assert!(
14332            c.validate_code_paths().is_ok(),
14333            "validate_code_paths must accept servicos == \
14334             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
14335             singleton V0-shape every in-tree `caixa_with_code_paths` \
14336             positive control uses)",
14337        );
14338    }
14339
14340    #[test]
14341    fn servicos_projects_slice_by_borrow() {
14342        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
14343        // borrow — the returned slice borrows the underlying
14344        // `Vec<String>` storage of the `:servicos` slot and the
14345        // accessor must not clone the backing `Vec` on every call.
14346        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
14347        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
14348        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
14349        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
14350        // the sibling outer top-level [`Caixa`] `&[String]`-return
14351        // axes — the accessor's returned slice must borrow from
14352        // `&self` (the returned reference's lifetime is tied to
14353        // `&self`), and calling the accessor twice on the same
14354        // [`Caixa`] must yield slices that are pointer-equal (the
14355        // underlying byte-buffer is the storage `Vec`'s allocation,
14356        // not a fresh copy) as well as value-equal (idempotent, no
14357        // side effects on `&self`).
14358        //
14359        // Pins against a future silent detour that returned an owned
14360        // `Vec<String>` (which would type-check but silently clone on
14361        // every call, breaking the zero-cost projection every peer
14362        // sibling slice accessor carries), a `&Vec<String>` return
14363        // (which would leak the backing `Vec`'s grow/push/reserve
14364        // surface no downstream consumer reaches for), or a one-arm-
14365        // only accessor that returned a saturating value on some
14366        // sentinel input (breaking the pass-through invariant the
14367        // sibling slice accessors carry).
14368        for servicos in [
14369            vec![],
14370            vec!["servicos/demo.computeunit.yaml"],
14371            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
14372            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
14373        ] {
14374            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
14375            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
14376            let first = c.servicos();
14377            let second = c.servicos();
14378            assert_eq!(
14379                first, second,
14380                "Caixa::servicos must be idempotent — two successive \
14381                 calls on the same &self must return the same &[String]",
14382            );
14383            assert_eq!(
14384                first.as_ptr(),
14385                second.as_ptr(),
14386                "Caixa::servicos must borrow the underlying \
14387                 Vec<String> storage — two successive calls must \
14388                 return slices with the same backing pointer (a fresh \
14389                 Vec<String> clone would change the pointer on every \
14390                 call)",
14391            );
14392            assert_eq!(
14393                first,
14394                expected.as_slice(),
14395                "Caixa::servicos must return :servicos verbatim by \
14396                 borrow — got {first:?}, expected {expected:?}",
14397            );
14398        }
14399    }
14400
14401    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
14402
14403    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
14404        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14405        c.deps = deps;
14406        c
14407    }
14408
14409    #[test]
14410    fn deps_returns_deps_slice_verbatim_across_permutations() {
14411        // The canonical per-`Caixa` `:deps` universal-axis runtime-
14412        // dependency-declaration-list slice pin: [`Caixa::deps`] must
14413        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
14414        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
14415        // access across every representative value in the accept-set —
14416        // `[]` (the "no runtime deps declared" arm every existing
14417        // fixture without a `:deps` line carries; the
14418        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
14419        // single-entry list (the shape most consumer caixas carry), a
14420        // canonical two-entry list (the multi-dep runtime closure), and
14421        // two past-the-guard sentinels — a `[""]`-`:nome` entry
14422        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
14423        // `NomeInvalid` but the accessor must ship the raw slot
14424        // verbatim) and a `[a, a]` duplicate (validate rejects through
14425        // `DuplicateNome { list: ":deps" }` but the accessor must ship
14426        // the raw slot verbatim so struct-literal fixtures continue to
14427        // expose the duplicate at the accessor).
14428        //
14429        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
14430        // pin on the substrate primitive — opens the outer-`Caixa`
14431        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
14432        // future lift closes on. Peer of the closed outer-`Caixa`
14433        // foreign-code-slot `&[String]` sub-family
14434        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
14435        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
14436        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
14437        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
14438        // (`autores_returns_autores_slice_verbatim_across_permutations`
14439        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14440        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
14441        // projection pattern onto a novel element-type axis (`Dep`
14442        // composite vs the prior sibling family's `String` scalar).
14443        // Pins against a future silent detour that returned an owned
14444        // `Vec<Dep>` (which would type-check but silently clone on every
14445        // accessor call, breaking the zero-cost projection every peer
14446        // sibling slice accessor carries), a `[""] → []` collapse (which
14447        // would silently absorb the `NomeEmpty` refusal case at the
14448        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
14449        // would silently absorb the `DuplicateNome` refusal case at the
14450        // accessor boundary).
14451        for deps in [
14452            vec![],
14453            vec![Dep::simple("", "^0.1")],
14454            vec![Dep::simple("caixa-teia", "^0.1")],
14455            vec![
14456                Dep::simple("caixa-teia", "^0.1"),
14457                Dep::simple("caixa-core", "^0.1"),
14458            ],
14459            vec![
14460                Dep::simple("caixa-teia", "^0.1"),
14461                Dep::simple("caixa-teia", "^0.2"),
14462            ],
14463        ] {
14464            let c = caixa_with_deps(deps.clone());
14465            assert_eq!(
14466                c.deps(),
14467                deps.as_slice(),
14468                "Caixa::deps must return :deps verbatim (got {:?}, \
14469                 expected {deps:?})",
14470                c.deps(),
14471            );
14472            assert_eq!(
14473                c.deps(),
14474                c.deps.as_slice(),
14475                "Caixa::deps must element-equal the raw \
14476                 `self.deps.as_slice()` field access across every \
14477                 value in the Vec<Dep> accept-set",
14478            );
14479        }
14480    }
14481
14482    #[test]
14483    fn validate_deps_duplicate_arm_routes_through_accessor() {
14484        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
14485        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
14486        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
14487        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
14488        // "^0.2")], .. }` must surface the `DuplicateNome { list:
14489        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
14490        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
14491        // form) must pass validate. The pair jointly pins the accessor +
14492        // validate-gate composition: any future silent detour that had
14493        // the accessor return a dedupped slice on the `[a, a]` arm (a
14494        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
14495        // would silently absorb the `DuplicateNome` refusal at the
14496        // accessor boundary and the validate gate would accept a
14497        // struct-literal `Caixa` carrying the drift — the composition
14498        // pin catches that at caixa-core build time.
14499        //
14500        // Peer of the per-`Caixa`
14501        // `validate_autores_empty_entry_arm_routes_through_accessor`
14502        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
14503        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
14504        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
14505        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
14506        // (611f78b) accessor-composition pins on the sibling `&[T]`-
14507        // composition axes — same "the validate gate must route through
14508        // the substrate-primitive typed dispatch" discipline extended
14509        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
14510        // composition surface, opening the outer-`Caixa` dependency-slot
14511        // arm of the composition-pin family.
14512        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
14513        let err = c.validate_deps().unwrap_err();
14514        assert!(
14515            matches!(
14516                err,
14517                DepError::DuplicateNome { ref nome, list } if nome == "d"
14518                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
14519            ),
14520            "validate_deps must reject deps == \
14521             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
14522             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
14523             accessor and the validate gate must route through the \
14524             same substrate-primitive typed dispatch on the :deps \
14525             within-list duplicate arm (got {err:?})",
14526        );
14527        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
14528        assert!(
14529            c.validate_deps().is_ok(),
14530            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
14531             (the canonical single-entry form)",
14532        );
14533    }
14534
14535    #[test]
14536    fn deps_projects_slice_by_borrow() {
14537        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
14538        // — the returned slice borrows the underlying `Vec<Dep>` storage
14539        // of the `:deps` slot and the accessor must not clone the
14540        // backing `Vec` on every call. Peer of the per-`Caixa`
14541        // `autores_projects_slice_by_borrow` (b5d813f),
14542        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
14543        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
14544        // `exe_projects_slice_by_borrow` (65d9527), and
14545        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
14546        // on the sibling outer top-level [`Caixa`] `&[String]`-return
14547        // axes — the accessor's returned slice must borrow from `&self`
14548        // (the returned reference's lifetime is tied to `&self`), and
14549        // calling the accessor twice on the same [`Caixa`] must yield
14550        // slices that are pointer-equal (the underlying byte-buffer is
14551        // the storage `Vec`'s allocation, not a fresh copy) as well as
14552        // value-equal (idempotent, no side effects on `&self`).
14553        //
14554        // Pins against a future silent detour that returned an owned
14555        // `Vec<Dep>` (which would type-check but silently clone on
14556        // every call), a `&Vec<Dep>` return (which would leak the
14557        // backing `Vec`'s grow/push/reserve surface no downstream
14558        // consumer reaches for), or a one-arm-only accessor that
14559        // returned a saturating value on some sentinel input.
14560        for deps in [
14561            vec![],
14562            vec![Dep::simple("caixa-teia", "^0.1")],
14563            vec![
14564                Dep::simple("caixa-teia", "^0.1"),
14565                Dep::simple("caixa-core", "^0.1"),
14566            ],
14567        ] {
14568            let c = caixa_with_deps(deps.clone());
14569            let first = c.deps();
14570            let second = c.deps();
14571            assert_eq!(
14572                first, second,
14573                "Caixa::deps must be idempotent — two successive calls \
14574                 on the same &self must return the same &[Dep]",
14575            );
14576            assert_eq!(
14577                first.as_ptr(),
14578                second.as_ptr(),
14579                "Caixa::deps must borrow the underlying Vec<Dep> \
14580                 storage — two successive calls must return slices \
14581                 with the same backing pointer (a fresh Vec<Dep> clone \
14582                 would change the pointer on every call)",
14583            );
14584            assert_eq!(
14585                first,
14586                deps.as_slice(),
14587                "Caixa::deps must return :deps verbatim by borrow — \
14588                 got {first:?}, expected {deps:?}",
14589            );
14590        }
14591    }
14592
14593    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
14594
14595    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
14596        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14597        c.deps_dev = deps_dev;
14598        c
14599    }
14600
14601    #[test]
14602    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
14603        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
14604        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
14605        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
14606        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
14607        // access across every representative value in the accept-set —
14608        // `[]` (the "no dev deps declared" arm every existing fixture
14609        // without a `:deps-dev` line carries; the [`Caixa::template`]
14610        // scaffold emits `:deps-dev ()`), a canonical single-entry list
14611        // (the shape most consumer caixas carry — a `tatara-check` dev
14612        // pin), a canonical two-entry list (the multi-dev-dep closure),
14613        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
14614        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
14615        // `NomeInvalid` but the accessor must ship the raw slot
14616        // verbatim) and a `[a, a]` duplicate (validate rejects through
14617        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
14618        // ship the raw slot verbatim so struct-literal fixtures continue
14619        // to expose the duplicate at the accessor).
14620        //
14621        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
14622        // pin on the substrate primitive — closes the outer-`Caixa`
14623        // dependency-slot `&[Dep]` sub-family the sibling
14624        // `deps_returns_deps_slice_verbatim_across_permutations`
14625        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
14626        // slice" projection pattern onto the sibling dev-dep axis —
14627        // pins against a future silent detour that returned an owned
14628        // `Vec<Dep>` (which would type-check but silently clone on every
14629        // accessor call, breaking the zero-cost projection every peer
14630        // sibling slice accessor carries), a `[""] → []` collapse (which
14631        // would silently absorb the `NomeEmpty` refusal case at the
14632        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
14633        // would silently absorb the `DuplicateNome` refusal case at the
14634        // accessor boundary).
14635        for deps_dev in [
14636            vec![],
14637            vec![Dep::simple("", "^0.1")],
14638            vec![Dep::simple("tatara-check", "^0.1")],
14639            vec![
14640                Dep::simple("tatara-check", "^0.1"),
14641                Dep::simple("caixa-lint", "^0.1"),
14642            ],
14643            vec![
14644                Dep::simple("tatara-check", "^0.1"),
14645                Dep::simple("tatara-check", "^0.2"),
14646            ],
14647        ] {
14648            let c = caixa_with_deps_dev(deps_dev.clone());
14649            assert_eq!(
14650                c.deps_dev(),
14651                deps_dev.as_slice(),
14652                "Caixa::deps_dev must return :deps-dev verbatim (got \
14653                 {:?}, expected {deps_dev:?})",
14654                c.deps_dev(),
14655            );
14656            assert_eq!(
14657                c.deps_dev(),
14658                c.deps_dev.as_slice(),
14659                "Caixa::deps_dev must element-equal the raw \
14660                 `self.deps_dev.as_slice()` field access across every \
14661                 value in the Vec<Dep> accept-set",
14662            );
14663        }
14664    }
14665
14666    #[test]
14667    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
14668        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
14669        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
14670        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
14671        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
14672        // Dep::simple("d", "^0.2")], .. }` must surface the
14673        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
14674        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
14675        // canonical single-entry form) must pass validate. The pair
14676        // jointly pins the accessor + validate-gate composition: any
14677        // future silent detour that had the accessor return a dedupped
14678        // slice on the `[a, a]` arm (a
14679        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
14680        // would silently absorb the `DuplicateNome` refusal at the
14681        // accessor boundary and the validate gate would accept a
14682        // struct-literal `Caixa` carrying the drift — the composition
14683        // pin catches that at caixa-core build time.
14684        //
14685        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
14686        // (ad34b4e) on the sibling `:deps` axis — same "the validate
14687        // gate must route through the substrate-primitive typed
14688        // dispatch" discipline folded onto the sibling `:deps-dev`
14689        // axis, closing the two-list dep-graph composition-pin family.
14690        // The `:deps-dev` diagnostic must carry the
14691        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
14692        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
14693        // offending list unambiguously.
14694        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
14695        let err = c.validate_deps().unwrap_err();
14696        assert!(
14697            matches!(
14698                err,
14699                DepError::DuplicateNome { ref nome, list } if nome == "d"
14700                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
14701            ),
14702            "validate_deps must reject deps_dev == \
14703             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
14704             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
14705             accessor and the validate gate must route through the \
14706             same substrate-primitive typed dispatch on the :deps-dev \
14707             within-list duplicate arm (got {err:?})",
14708        );
14709        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
14710        assert!(
14711            c.validate_deps().is_ok(),
14712            "validate_deps must accept deps_dev == \
14713             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
14714        );
14715    }
14716
14717    #[test]
14718    fn deps_dev_projects_slice_by_borrow() {
14719        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
14720        // borrow — the returned slice borrows the underlying `Vec<Dep>`
14721        // storage of the `:deps-dev` slot and the accessor must not
14722        // clone the backing `Vec` on every call. Peer of
14723        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
14724        // `:deps` axis, and of the per-`Caixa`
14725        // `autores_projects_slice_by_borrow` (b5d813f),
14726        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
14727        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
14728        // `exe_projects_slice_by_borrow` (65d9527), and
14729        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
14730        // on the sibling outer top-level [`Caixa`] `&[String]`-return
14731        // axes — the accessor's returned slice must borrow from `&self`
14732        // (the returned reference's lifetime is tied to `&self`), and
14733        // calling the accessor twice on the same [`Caixa`] must yield
14734        // slices that are pointer-equal (the underlying byte-buffer is
14735        // the storage `Vec`'s allocation, not a fresh copy) as well as
14736        // value-equal (idempotent, no side effects on `&self`).
14737        //
14738        // Pins against a future silent detour that returned an owned
14739        // `Vec<Dep>` (which would type-check but silently clone on
14740        // every call), a `&Vec<Dep>` return (which would leak the
14741        // backing `Vec`'s grow/push/reserve surface no downstream
14742        // consumer reaches for), or a one-arm-only accessor that
14743        // returned a saturating value on some sentinel input.
14744        for deps_dev in [
14745            vec![],
14746            vec![Dep::simple("tatara-check", "^0.1")],
14747            vec![
14748                Dep::simple("tatara-check", "^0.1"),
14749                Dep::simple("caixa-lint", "^0.1"),
14750            ],
14751        ] {
14752            let c = caixa_with_deps_dev(deps_dev.clone());
14753            let first = c.deps_dev();
14754            let second = c.deps_dev();
14755            assert_eq!(
14756                first, second,
14757                "Caixa::deps_dev must be idempotent — two successive \
14758                 calls on the same &self must return the same &[Dep]",
14759            );
14760            assert_eq!(
14761                first.as_ptr(),
14762                second.as_ptr(),
14763                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
14764                 storage — two successive calls must return slices \
14765                 with the same backing pointer (a fresh Vec<Dep> clone \
14766                 would change the pointer on every call)",
14767            );
14768            assert_eq!(
14769                first,
14770                deps_dev.as_slice(),
14771                "Caixa::deps_dev must return :deps-dev verbatim by \
14772                 borrow — got {first:?}, expected {deps_dev:?}",
14773            );
14774        }
14775    }
14776
14777    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
14778
14779    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
14780        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14781        c.limits = limits;
14782        c
14783    }
14784
14785    #[test]
14786    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
14787        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
14788        // composite optional-composite-reference-shape pin:
14789        // [`Caixa::limits`] must return the `:limits` typed
14790        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
14791        // reference over the same backing storage the raw
14792        // `self.limits.as_ref()` field access borrows from, byte-equal
14793        // across every representative fixture in the accept-set — the
14794        // author-omitted `None` shape (the "engine-default applies"
14795        // partition every downstream Servico M2 overlay emitter treats
14796        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
14797        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
14798        // per-axis cap is `None`, so the peer M2 overlay emitter's
14799        // `.is_empty()`-gated projection still emits nothing but the
14800        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
14801        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
14802        // fixture (only `:memory` set — the canonical shape most
14803        // memory-heavy Servicos carry), and a fully-populated composite
14804        // (every per-axis cap set — the canonical shape a
14805        // sandboxed-by-default Servico carries).
14806        //
14807        // Pins against a future silent detour that returned a fresh-
14808        // cloned [`LimitsSpec`] copy (which would type-check via the
14809        // `Clone` impl but silently break every downstream caller that
14810        // relied on the reference sharing the composite's backing
14811        // identity), a reference to an operator-resolved overlay (the
14812        // future per-cluster `:limits-overrides` slot — its resolution
14813        // must land at exactly this accessor body, not silently divert
14814        // the raw slot away from a second consumer), a
14815        // `None` → `Some(LimitsSpec::default)` cluster-default
14816        // projection (which would collapse the load-bearing
14817        // "author-omitted `:limits` ⇒ engine-default applies" partition
14818        // the peer [`crate::render::servico_m2_overlay`] emitter and
14819        // the peer [`Caixa::declared_servico_slots`] enumerator both
14820        // read), or an axis-shuffled projection (a future detour that
14821        // swapped `memory` and `fuel` through the accessor would
14822        // silently split the paired [`crate::StandardLayout::verify`]
14823        // per-`:limits` shape gate's traversal input from the peer
14824        // `servico_m2_overlay` emitter's projection input).
14825        //
14826        // First outer top-level [`Caixa`] `Option<&Composite>`-return
14827        // composite-reference accessor pin on the substrate primitive
14828        // — opens the outer-`Caixa` `Option<&Composite>` composite-
14829        // reference projection pattern the sibling `:behavior`
14830        // [`crate::BehaviorSpec`] / `:politicas`
14831        // [`crate::aplicacao::MeshPolicy`] / `:placement`
14832        // [`crate::aplicacao::Placement`] / `:entrada`
14833        // [`crate::aplicacao::Entrada`] future outer-composite lifts
14834        // fold on. Peer of the closed M3 outer-composite family the
14835        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
14836        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
14837        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
14838        // reference accessor pins already carry on the outer
14839        // [`crate::AplicacaoSpec`] altitude — extends the outer-
14840        // accessor byte-equal-projection discipline onto the outer
14841        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
14842        use crate::LimitsSpec;
14843        use std::time::Duration;
14844        let fixtures: Vec<Option<LimitsSpec>> = vec![
14845            None,
14846            Some(LimitsSpec::default()),
14847            Some(LimitsSpec {
14848                memory: Some(64 * 1024 * 1024),
14849                ..Default::default()
14850            }),
14851            Some(LimitsSpec {
14852                memory: Some(64 * 1024 * 1024),
14853                fuel: Some(1_000_000),
14854                wall_clock: Some(Duration::from_secs(30)),
14855                cpu: Some(500),
14856            }),
14857        ];
14858        for limits in fixtures {
14859            let c = caixa_with_limits(limits.clone());
14860            assert_eq!(
14861                c.limits(),
14862                limits.as_ref(),
14863                "Caixa::limits must return :limits verbatim (got {:?}, \
14864                 expected {:?})",
14865                c.limits(),
14866                limits.as_ref(),
14867            );
14868            match (c.limits(), c.limits.as_ref()) {
14869                (Some(a), Some(b)) => assert!(
14870                    std::ptr::eq(a, b),
14871                    "Caixa::limits accessor and self.limits.as_ref() \
14872                     field access must borrow the same backing storage \
14873                     — the accessor is the substrate-primitive typed \
14874                     dispatch every downstream Servico-M2-overlay \
14875                     composite consumer must route through, and a \
14876                     reference-identity split would silently break \
14877                     every consumer that relied on the borrow sharing \
14878                     the composite's storage",
14879                ),
14880                (None, None) => {}
14881                _ => panic!(
14882                    "Caixa::limits presence bit must byte-equal \
14883                     self.limits.is_some() — a presence-bit drift would \
14884                     silently split the paired StandardLayout::verify \
14885                     per-`:limits` shape gate's traversal head from \
14886                     the peer render::servico_m2_overlay M2 overlay \
14887                     emitter's traversal head from the peer \
14888                     Caixa::declared_servico_slots M2 declared-slot \
14889                     enumerator's presence probe",
14890                ),
14891            }
14892            assert_eq!(
14893                c.limits().is_some(),
14894                c.limits.is_some(),
14895                "Caixa::limits().is_some() must byte-equal \
14896                 self.limits.is_some() — a presence-bit drift would \
14897                 silently split every downstream Option<&LimitsSpec> \
14898                 consumer's partition on the engine-default arm",
14899            );
14900        }
14901    }
14902
14903    #[test]
14904    fn declared_servico_slots_limits_arm_routes_through_accessor() {
14905        // Composition pin: [`Caixa::declared_servico_slots`]'s
14906        // `:limits` presence-probe arm must key off [`Caixa::limits`],
14907        // not the raw `self.limits.is_some()` field-probe. Structurally:
14908        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
14909        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
14910        // (the presence bit is `Some`, so the M2 kind-coherence gate
14911        // must surface the slot as "declared" even when every per-axis
14912        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
14913        // push the label (the "author omitted the slot entirely"
14914        // partition). The pair jointly pins the accessor + declared-
14915        // slot enumerator composition: any future silent detour that
14916        // had the accessor collapse `Some(LimitsSpec::default())` to
14917        // `None` (a `.filter(|l| !l.is_empty())` projection) would
14918        // silently absorb the "declared but empty" arm at the
14919        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
14920        // kind-coherence gate would silently accept a
14921        // struct-literal `Caixa` carrying the drift.
14922        //
14923        // Peer of the sibling per-`Caixa`
14924        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
14925        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
14926        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
14927        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
14928        // enumerator gate must route through the substrate-primitive
14929        // typed dispatch" discipline extended onto the outer top-level
14930        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
14931        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
14932        // composition-pin family.
14933        use crate::LimitsSpec;
14934        let c = caixa_with_limits(Some(LimitsSpec::default()));
14935        let slots = c.declared_servico_slots();
14936        assert!(
14937            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
14938            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
14939             when `:limits` is Some (even for LimitsSpec::default()) \
14940             — the accessor and the enumerator gate must route through \
14941             the same substrate-primitive typed dispatch on the outer \
14942             :limits presence bit (got slots={slots:?})",
14943        );
14944        let c = caixa_with_limits(None);
14945        let slots = c.declared_servico_slots();
14946        assert!(
14947            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
14948            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
14949             when `:limits` is None — the author-omitted arm must \
14950             route through the accessor's None-return unchanged (got \
14951             slots={slots:?})",
14952        );
14953    }
14954
14955    #[test]
14956    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
14957        // Composition pin: [`crate::render::servico_m2_overlay`]'s
14958        // per-`:limits` M2 overlay emit arm must key off
14959        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
14960        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
14961        // Some(64 MiB), .. default }), .. }` must surface the
14962        // `M2_KEY_LIMITS` key with the per-axis
14963        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
14964        // limits: Some(LimitsSpec::default()), .. }` must omit the
14965        // key entirely (the `.is_empty()`-gated inner arm elides an
14966        // empty composite even when the outer presence bit is `Some`),
14967        // and a `Caixa { limits: None, .. }` must also omit the key
14968        // (the "author omitted the slot entirely" partition). The
14969        // three-fixture family jointly pins the accessor + M2 overlay
14970        // emitter composition: any future silent detour that had the
14971        // accessor return a fresh-cloned copy on the `Some` arm (a
14972        // `LimitsSpec::clone()` projection) would silently break the
14973        // reference-identity pin the peer per-axis
14974        // `serde_yaml::to_value(limits)` projection reads from.
14975        use crate::LimitsSpec;
14976        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
14977        let c = caixa_with_limits(Some(LimitsSpec {
14978            memory: Some(64 * 1024 * 1024),
14979            ..Default::default()
14980        }));
14981        let overlay = servico_m2_overlay(&c).unwrap();
14982        assert!(
14983            overlay.contains_key(M2_KEY_LIMITS),
14984            "servico_m2_overlay must surface M2_KEY_LIMITS when \
14985             `:limits` carries a non-empty composite — the accessor \
14986             and the M2 overlay emitter must route through the same \
14987             substrate-primitive typed dispatch on the outer :limits \
14988             composite (got overlay={overlay:?})",
14989        );
14990        let c = caixa_with_limits(Some(LimitsSpec::default()));
14991        let overlay = servico_m2_overlay(&c).unwrap();
14992        assert!(
14993            !overlay.contains_key(M2_KEY_LIMITS),
14994            "servico_m2_overlay must omit M2_KEY_LIMITS when \
14995             `:limits` is Some(LimitsSpec::default()) — the empty \
14996             composite's `.is_empty()`-gated inner arm must elide \
14997             the key regardless of the outer presence bit (got \
14998             overlay={overlay:?})",
14999        );
15000        let c = caixa_with_limits(None);
15001        let overlay = servico_m2_overlay(&c).unwrap();
15002        assert!(
15003            !overlay.contains_key(M2_KEY_LIMITS),
15004            "servico_m2_overlay must omit M2_KEY_LIMITS when \
15005             `:limits` is None — the author-omitted arm must route \
15006             through the accessor's None-return unchanged (got \
15007             overlay={overlay:?})",
15008        );
15009    }
15010
15011    #[test]
15012    fn limits_projects_option_ref_by_borrow() {
15013        // The by-borrow pin: [`Caixa::limits`] returns
15014        // `Option<&LimitsSpec>` by borrow — the returned reference
15015        // borrows the underlying `Option<LimitsSpec>` storage of the
15016        // `:limits` slot and the accessor must not clone the backing
15017        // composite on every call. Peer of the sibling
15018        // `deps_projects_slice_by_borrow` (ad34b4e) /
15019        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
15020        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
15021        // extended here to the outer [`Caixa`] `Option<&Composite>`-
15022        // return axis: the accessor's returned reference must borrow
15023        // from `&self` (the returned reference's lifetime is tied to
15024        // `&self`), and calling the accessor twice on the same
15025        // [`Caixa`] must yield references that are pointer-equal (the
15026        // underlying byte-buffer is the storage `LimitsSpec`'s
15027        // allocation, not a fresh copy) as well as value-equal
15028        // (idempotent, no side effects on `&self`).
15029        //
15030        // Pins against a future silent detour that returned an owned
15031        // `LimitsSpec` (which would type-check via the `Clone` impl
15032        // but silently clone on every call), a `&LimitsSpec` panic-
15033        // return on the `None` arm (which would collapse the load-
15034        // bearing `Option` presence-bit into a runtime panic), or a
15035        // one-arm-only accessor that returned a saturating composite
15036        // on some sentinel input.
15037        use crate::LimitsSpec;
15038        use std::time::Duration;
15039        for limits in [
15040            Some(LimitsSpec::default()),
15041            Some(LimitsSpec {
15042                memory: Some(64 * 1024 * 1024),
15043                fuel: Some(1_000_000),
15044                wall_clock: Some(Duration::from_secs(30)),
15045                cpu: Some(500),
15046            }),
15047        ] {
15048            let c = caixa_with_limits(limits.clone());
15049            let first = c.limits().unwrap();
15050            let second = c.limits().unwrap();
15051            assert_eq!(
15052                first, second,
15053                "Caixa::limits must be idempotent — two successive \
15054                 calls on the same &self must return the same \
15055                 &LimitsSpec",
15056            );
15057            assert!(
15058                std::ptr::eq(first, second),
15059                "Caixa::limits must borrow the underlying \
15060                 Option<LimitsSpec> storage — two successive calls \
15061                 must return references with the same backing pointer \
15062                 (a fresh LimitsSpec clone would change the pointer \
15063                 on every call)",
15064            );
15065            assert_eq!(
15066                Some(first),
15067                limits.as_ref(),
15068                "Caixa::limits must return :limits verbatim by borrow \
15069                 — got {first:?}, expected {:?}",
15070                limits.as_ref(),
15071            );
15072        }
15073        let c = caixa_with_limits(None);
15074        assert!(
15075            c.limits().is_none(),
15076            "Caixa::limits must return None when :limits is absent — \
15077             the author-omitted arm must project through the \
15078             accessor's Option::None unchanged",
15079        );
15080    }
15081
15082    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
15083
15084    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
15085        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15086        c.behavior = behavior;
15087        c
15088    }
15089
15090    #[test]
15091    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
15092        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
15093        // composite optional-composite-reference-shape pin:
15094        // [`Caixa::behavior`] must return the `:behavior` typed
15095        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
15096        // reference over the same backing storage the raw
15097        // `self.behavior.as_ref()` field access borrows from, byte-equal
15098        // across every representative fixture in the accept-set — the
15099        // author-omitted `None` shape (the "runtime-default applies"
15100        // partition every downstream Servico M2 overlay emitter treats
15101        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
15102        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
15103        // every per-callback path is `None`, so the peer M2 overlay
15104        // emitter's `.is_empty()`-gated projection still emits nothing
15105        // but the outer presence-bit is `Some`, so
15106        // [`Caixa::declared_servico_slots`] still pushes the
15107        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
15108        // (only `:on-state-change` set — the canonical shape a caixa
15109        // that only wires the hot-upgrade migration path carries), and
15110        // a fully-populated composite (every per-callback path set —
15111        // the canonical shape a fully-instrumented gen_server-shaped
15112        // Servico carries).
15113        //
15114        // Peer of the sibling
15115        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15116        // (b2bd9d7) opening fixture-family + reference-identity +
15117        // presence-bit tetrad pin on the outer top-level [`Caixa`]
15118        // `Option<&Composite>`-return sub-family — extended here to the
15119        // second axis of that sub-family so both of the currently-lifted
15120        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
15121        // `:behavior`) carry the same "byte-equal, borrow-shared,
15122        // presence-bit-preserved" outer-accessor discipline.
15123        //
15124        // Pins against a future silent detour that returned a fresh-
15125        // cloned [`crate::BehaviorSpec`] copy (which would type-check
15126        // via the `Clone` impl but silently break every downstream
15127        // caller that relied on the reference sharing the composite's
15128        // backing identity), a reference to an operator-resolved
15129        // overlay (a future per-cluster `:behavior-overrides` slot —
15130        // its resolution must land at exactly this accessor body, not
15131        // silently divert the raw slot away from a second consumer), a
15132        // `None` → `Some(BehaviorSpec::default)` cluster-default
15133        // projection (which would collapse the load-bearing
15134        // "author-omitted `:behavior` ⇒ runtime-default applies"
15135        // partition the peer [`crate::render::servico_m2_overlay`]
15136        // emitter, the peer [`Caixa::declared_servico_slots`]
15137        // enumerator, and the cross-slot
15138        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
15139        // gate all read), or a callback-shuffled projection (a future
15140        // detour that swapped `on_init` and `on_terminate` through the
15141        // accessor would silently split the paired
15142        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
15143        // traversal input from the peer `servico_m2_overlay` emitter's
15144        // projection input from the cross-slot `:state-change`
15145        // composition gate's traversal input).
15146        use crate::BehaviorSpec;
15147        use std::path::PathBuf;
15148        let fixtures: Vec<Option<BehaviorSpec>> = vec![
15149            None,
15150            Some(BehaviorSpec::default()),
15151            Some(BehaviorSpec {
15152                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
15153                ..Default::default()
15154            }),
15155            Some(BehaviorSpec {
15156                on_init: Some(PathBuf::from("lib/init.lisp")),
15157                on_call: Some(PathBuf::from("lib/handlers.lisp")),
15158                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
15159                on_info: Some(PathBuf::from("lib/handlers.lisp")),
15160                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
15161                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
15162            }),
15163        ];
15164        for behavior in fixtures {
15165            let c = caixa_with_behavior(behavior.clone());
15166            assert_eq!(
15167                c.behavior(),
15168                behavior.as_ref(),
15169                "Caixa::behavior must return :behavior verbatim (got \
15170                 {:?}, expected {:?})",
15171                c.behavior(),
15172                behavior.as_ref(),
15173            );
15174            match (c.behavior(), c.behavior.as_ref()) {
15175                (Some(a), Some(b)) => assert!(
15176                    std::ptr::eq(a, b),
15177                    "Caixa::behavior accessor and self.behavior.as_ref() \
15178                     field access must borrow the same backing storage \
15179                     — the accessor is the substrate-primitive typed \
15180                     dispatch every downstream Servico-M2-overlay \
15181                     composite consumer must route through, and a \
15182                     reference-identity split would silently break \
15183                     every consumer that relied on the borrow sharing \
15184                     the composite's storage",
15185                ),
15186                (None, None) => {}
15187                _ => panic!(
15188                    "Caixa::behavior presence bit must byte-equal \
15189                     self.behavior.is_some() — a presence-bit drift \
15190                     would silently split the paired \
15191                     StandardLayout::verify per-`:behavior` shape \
15192                     gate's traversal head from the peer \
15193                     render::servico_m2_overlay M2 overlay emitter's \
15194                     traversal head from the cross-slot \
15195                     validate_upgrade_from_against_behavior \
15196                     composition gate's traversal head from the peer \
15197                     Caixa::declared_servico_slots M2 declared-slot \
15198                     enumerator's presence probe",
15199                ),
15200            }
15201            assert_eq!(
15202                c.behavior().is_some(),
15203                c.behavior.is_some(),
15204                "Caixa::behavior().is_some() must byte-equal \
15205                 self.behavior.is_some() — a presence-bit drift would \
15206                 silently split every downstream Option<&BehaviorSpec> \
15207                 consumer's partition on the runtime-default arm",
15208            );
15209        }
15210    }
15211
15212    #[test]
15213    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
15214        // Composition pin: [`Caixa::declared_servico_slots`]'s
15215        // `:behavior` presence-probe arm must key off
15216        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
15217        // field-probe. Structurally: a `Caixa { behavior:
15218        // Some(BehaviorSpec::default()), .. }` must still push
15219        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
15220        // presence bit is `Some`, so the M2 kind-coherence gate must
15221        // surface the slot as "declared" even when every per-callback
15222        // path is unset), and a `Caixa { behavior: None, .. }` must
15223        // NOT push the label (the "author omitted the slot entirely"
15224        // partition). The pair jointly pins the accessor + declared-
15225        // slot enumerator composition: any future silent detour that
15226        // had the accessor collapse `Some(BehaviorSpec::default())`
15227        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
15228        // silently absorb the "declared but empty" arm at the
15229        // accessor boundary and the
15230        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
15231        // kind-coherence gate would silently accept a struct-literal
15232        // `Caixa` carrying the drift.
15233        //
15234        // Peer of the sibling
15235        // `declared_servico_slots_limits_arm_routes_through_accessor`
15236        // (b2bd9d7) composition pin on the sibling `:limits` outer-
15237        // `Option<&LimitsSpec>` arm of the same
15238        // [`Caixa::declared_servico_slots`] M2 declared-slot
15239        // enumerator's traversal — same "the enumerator gate must
15240        // route through the substrate-primitive typed dispatch"
15241        // discipline extended onto the outer top-level [`Caixa`]
15242        // `Option<&BehaviorSpec>`-composition surface.
15243        use crate::BehaviorSpec;
15244        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
15245        let slots = c.declared_servico_slots();
15246        assert!(
15247            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
15248            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
15249             when `:behavior` is Some (even for BehaviorSpec::default()) \
15250             — the accessor and the enumerator gate must route through \
15251             the same substrate-primitive typed dispatch on the outer \
15252             :behavior presence bit (got slots={slots:?})",
15253        );
15254        let c = caixa_with_behavior(None);
15255        let slots = c.declared_servico_slots();
15256        assert!(
15257            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
15258            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
15259             when `:behavior` is None — the author-omitted arm must \
15260             route through the accessor's None-return unchanged (got \
15261             slots={slots:?})",
15262        );
15263    }
15264
15265    #[test]
15266    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
15267        // Composition pin: [`crate::render::servico_m2_overlay`]'s
15268        // per-`:behavior` M2 overlay emit arm must key off
15269        // [`Caixa::behavior`], not the raw `&caixa.behavior`
15270        // field-borrow. Structurally: a `Caixa { behavior:
15271        // Some(BehaviorSpec { on_state_change: Some(...), .. default
15272        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
15273        // per-callback `onStateChange` sub-mapping in the overlay, a
15274        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
15275        // must omit the key entirely (the `.is_empty()`-gated inner
15276        // arm elides an empty composite even when the outer presence
15277        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
15278        // also omit the key (the "author omitted the slot entirely"
15279        // partition). The three-fixture family jointly pins the
15280        // accessor + M2 overlay emitter composition: any future
15281        // silent detour that had the accessor return a fresh-cloned
15282        // copy on the `Some` arm (a `BehaviorSpec::clone()`
15283        // projection) would silently break the reference-identity
15284        // pin the peer per-callback `serde_yaml::to_value(behavior)`
15285        // projection reads from.
15286        //
15287        // Peer of the sibling
15288        // `servico_m2_overlay_limits_arm_routes_through_accessor`
15289        // (b2bd9d7) composition pin on the sibling `:limits` outer-
15290        // `Option<&LimitsSpec>` arm of the same
15291        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
15292        // traversal — same "the emitter must route through the
15293        // substrate-primitive typed dispatch on the outer composite"
15294        // discipline extended onto the outer top-level [`Caixa`]
15295        // `Option<&BehaviorSpec>`-composition surface.
15296        use crate::BehaviorSpec;
15297        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
15298        use std::path::PathBuf;
15299        let c = caixa_with_behavior(Some(BehaviorSpec {
15300            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
15301            ..Default::default()
15302        }));
15303        let overlay = servico_m2_overlay(&c).unwrap();
15304        assert!(
15305            overlay.contains_key(M2_KEY_BEHAVIOR),
15306            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
15307             `:behavior` carries a non-empty composite — the accessor \
15308             and the M2 overlay emitter must route through the same \
15309             substrate-primitive typed dispatch on the outer :behavior \
15310             composite (got overlay={overlay:?})",
15311        );
15312        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
15313        let overlay = servico_m2_overlay(&c).unwrap();
15314        assert!(
15315            !overlay.contains_key(M2_KEY_BEHAVIOR),
15316            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
15317             `:behavior` is Some(BehaviorSpec::default()) — the empty \
15318             composite's `.is_empty()`-gated inner arm must elide the \
15319             key regardless of the outer presence bit (got \
15320             overlay={overlay:?})",
15321        );
15322        let c = caixa_with_behavior(None);
15323        let overlay = servico_m2_overlay(&c).unwrap();
15324        assert!(
15325            !overlay.contains_key(M2_KEY_BEHAVIOR),
15326            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
15327             `:behavior` is None — the author-omitted arm must route \
15328             through the accessor's None-return unchanged (got \
15329             overlay={overlay:?})",
15330        );
15331    }
15332
15333    #[test]
15334    fn behavior_projects_option_ref_by_borrow() {
15335        // The by-borrow pin: [`Caixa::behavior`] returns
15336        // `Option<&BehaviorSpec>` by borrow — the returned reference
15337        // borrows the underlying `Option<BehaviorSpec>` storage of the
15338        // `:behavior` slot and the accessor must not clone the backing
15339        // composite on every call. Peer of the sibling
15340        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
15341        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
15342        // return sub-family — extended here to the second axis of the
15343        // same sub-family: the accessor's returned reference must
15344        // borrow from `&self` (the returned reference's lifetime is
15345        // tied to `&self`), and calling the accessor twice on the same
15346        // [`Caixa`] must yield references that are pointer-equal (the
15347        // underlying byte-buffer is the storage `BehaviorSpec`'s
15348        // allocation, not a fresh copy) as well as value-equal
15349        // (idempotent, no side effects on `&self`).
15350        //
15351        // Pins against a future silent detour that returned an owned
15352        // `BehaviorSpec` (which would type-check via the `Clone` impl
15353        // but silently clone on every call), a `&BehaviorSpec` panic-
15354        // return on the `None` arm (which would collapse the load-
15355        // bearing `Option` presence-bit into a runtime panic), or a
15356        // one-arm-only accessor that returned a saturating composite
15357        // on some sentinel input.
15358        use crate::BehaviorSpec;
15359        use std::path::PathBuf;
15360        for behavior in [
15361            Some(BehaviorSpec::default()),
15362            Some(BehaviorSpec {
15363                on_init: Some(PathBuf::from("lib/init.lisp")),
15364                on_call: Some(PathBuf::from("lib/handlers.lisp")),
15365                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
15366                on_info: Some(PathBuf::from("lib/handlers.lisp")),
15367                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
15368                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
15369            }),
15370        ] {
15371            let c = caixa_with_behavior(behavior.clone());
15372            let first = c.behavior().unwrap();
15373            let second = c.behavior().unwrap();
15374            assert_eq!(
15375                first, second,
15376                "Caixa::behavior must be idempotent — two successive \
15377                 calls on the same &self must return the same \
15378                 &BehaviorSpec",
15379            );
15380            assert!(
15381                std::ptr::eq(first, second),
15382                "Caixa::behavior must borrow the underlying \
15383                 Option<BehaviorSpec> storage — two successive calls \
15384                 must return references with the same backing pointer \
15385                 (a fresh BehaviorSpec clone would change the pointer \
15386                 on every call)",
15387            );
15388            assert_eq!(
15389                Some(first),
15390                behavior.as_ref(),
15391                "Caixa::behavior must return :behavior verbatim by \
15392                 borrow — got {first:?}, expected {:?}",
15393                behavior.as_ref(),
15394            );
15395        }
15396        let c = caixa_with_behavior(None);
15397        assert!(
15398            c.behavior().is_none(),
15399            "Caixa::behavior must return None when :behavior is absent \
15400             — the author-omitted arm must project through the \
15401             accessor's Option::None unchanged",
15402        );
15403    }
15404
15405    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
15406
15407    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
15408        use crate::aplicacao::{Membro, WitContract};
15409        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15410        c.kind = CaixaKind::Aplicacao;
15411        c.membros = vec![Membro {
15412            caixa: "a".into(),
15413            versao: "^0.1".into(),
15414        }];
15415        c.contratos = vec![WitContract {
15416            de: "a".into(),
15417            para: "a".into(),
15418            wit: "wasi:http/proxy".into(),
15419            endpoint: Some("/x".into()),
15420            subject: None,
15421            slot: None,
15422        }];
15423        c.politicas = politicas;
15424        c
15425    }
15426
15427    #[test]
15428    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
15429        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
15430        // composite optional-composite-reference-shape pin:
15431        // [`Caixa::politicas`] must return the `:politicas` typed
15432        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
15433        // reference over the same backing storage the raw
15434        // `self.politicas.as_ref()` field access borrows from,
15435        // byte-equal across every representative fixture in the
15436        // accept-set — the author-omitted `None` shape (the "cluster-
15437        // default applies" partition every downstream mesh-artifact
15438        // emitter treats as "emit no `:politicas` overlay"), the
15439        // empty-composite `Some(MeshPolicy { .. default })` shape
15440        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
15441        // per-axis mesh-policy scalar is `None`, so the peer inner
15442        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
15443        // caixa-mesh overlay elides every per-axis emit but the outer
15444        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
15445        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
15446        // single-axis fixture (only `:timeout` set — the canonical
15447        // shape a latency-sensitive Aplicacao carries), and a
15448        // fully-populated composite (every per-axis mesh-policy
15449        // scalar set — the canonical shape a fully-governed
15450        // Aplicacao carries).
15451        //
15452        // Pins against a future silent detour that returned a fresh-
15453        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
15454        // type-check via the `Clone` impl but silently break every
15455        // downstream caller that relied on the reference sharing the
15456        // composite's backing identity), a reference to an operator-
15457        // resolved overlay (the future per-cluster
15458        // `:politicas-overrides` slot — its resolution must land at
15459        // exactly this accessor body, not silently divert the raw
15460        // slot away from the peer [`Caixa::declared_mesh_slots`]
15461        // enumerator's presence probe), a
15462        // `None` → `Some(MeshPolicy::default)` cluster-default
15463        // projection (which would collapse the load-bearing
15464        // "author-omitted `:politicas` ⇒ cluster-default applies"
15465        // partition the peer [`Caixa::declared_mesh_slots`]
15466        // enumerator and the peer [`Caixa::aplicacao_view`]
15467        // Aplicacao-composition seed both read), or an axis-shuffled
15468        // projection (a future detour that swapped `timeout` and
15469        // `retries` through the accessor would silently split the
15470        // paired [`Caixa::aplicacao_view`] seed's fold input from the
15471        // sibling M3 mesh-artifact emitter's projection input).
15472        //
15473        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
15474        // composite-reference accessor pin on the substrate primitive
15475        // — peer of the sibling
15476        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15477        // (b2bd9d7) and
15478        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
15479        // (35d8b52) opening tetrad pins on the outer top-level
15480        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
15481        // here to the first of the three M3 mesh-slot axes so the
15482        // opening third of the outer `Option<&Composite>` sub-family
15483        // carries the same "byte-equal, borrow-shared, presence-bit-
15484        // preserved" outer-accessor discipline.
15485        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
15486        use std::time::Duration;
15487        let fixtures: Vec<Option<MeshPolicy>> = vec![
15488            None,
15489            Some(MeshPolicy::default()),
15490            Some(MeshPolicy {
15491                timeout: Some(Duration::from_secs(30)),
15492                ..Default::default()
15493            }),
15494            Some(MeshPolicy {
15495                timeout: Some(Duration::from_secs(30)),
15496                retries: Some(3),
15497                circuit_breaker: Some(CircuitBreaker {
15498                    max_failures: 5,
15499                    window: Duration::from_secs(60),
15500                }),
15501                mtls_required: Some(true),
15502                rate_limit: Some(RateLimit {
15503                    rate: 100,
15504                    window: Duration::from_secs(1),
15505                }),
15506            }),
15507        ];
15508        for politicas in fixtures {
15509            let c = caixa_aplicacao_with_politicas(politicas.clone());
15510            assert_eq!(
15511                c.politicas(),
15512                politicas.as_ref(),
15513                "Caixa::politicas must return :politicas verbatim (got \
15514                 {:?}, expected {:?})",
15515                c.politicas(),
15516                politicas.as_ref(),
15517            );
15518            match (c.politicas(), c.politicas.as_ref()) {
15519                (Some(a), Some(b)) => assert!(
15520                    std::ptr::eq(a, b),
15521                    "Caixa::politicas accessor and self.politicas.as_ref() \
15522                     field access must borrow the same backing storage \
15523                     — the accessor is the substrate-primitive typed \
15524                     dispatch every downstream Aplicacao-mesh-overlay \
15525                     composite consumer must route through, and a \
15526                     reference-identity split would silently break \
15527                     every consumer that relied on the borrow sharing \
15528                     the composite's storage",
15529                ),
15530                (None, None) => {}
15531                _ => panic!(
15532                    "Caixa::politicas presence bit must byte-equal \
15533                     self.politicas.is_some() — a presence-bit drift \
15534                     would silently split the paired \
15535                     Caixa::aplicacao_view Aplicacao-composition seed's \
15536                     traversal head from the peer \
15537                     Caixa::declared_mesh_slots M3 declared-slot \
15538                     enumerator's presence probe",
15539                ),
15540            }
15541            assert_eq!(
15542                c.politicas().is_some(),
15543                c.politicas.is_some(),
15544                "Caixa::politicas().is_some() must byte-equal \
15545                 self.politicas.is_some() — a presence-bit drift would \
15546                 silently split every downstream Option<&MeshPolicy> \
15547                 consumer's partition on the cluster-default arm",
15548            );
15549        }
15550    }
15551
15552    #[test]
15553    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
15554        // Composition pin: [`Caixa::declared_mesh_slots`]'s
15555        // `:politicas` presence-probe arm must key off
15556        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
15557        // field-probe. Structurally: a `Caixa { politicas:
15558        // Some(MeshPolicy::default()), .. }` must still push
15559        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
15560        // presence bit is `Some`, so the M3 kind-coherence gate must
15561        // surface the slot as "declared" even when every per-axis
15562        // scalar is unset), and a `Caixa { politicas: None, .. }` must
15563        // NOT push the label (the "author omitted the slot entirely"
15564        // partition). The pair jointly pins the accessor + declared-
15565        // slot enumerator composition: any future silent detour that
15566        // had the accessor collapse `Some(MeshPolicy::default())` to
15567        // `None` (a `.filter(|p| !p.is_empty())` projection) would
15568        // silently absorb the "declared but empty" arm at the
15569        // accessor boundary and the
15570        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
15571        // coherence gate would silently accept a struct-literal
15572        // `Caixa` carrying the drift.
15573        //
15574        // Peer of the sibling
15575        // `declared_servico_slots_limits_arm_routes_through_accessor`
15576        // (b2bd9d7) and
15577        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15578        // (35d8b52) composition pins on the sibling `:limits` /
15579        // `:behavior` outer-`Option<&Composite>` arms of the peer
15580        // [`Caixa::declared_servico_slots`] M2 declared-slot
15581        // enumerator's traversal — same "the enumerator gate must
15582        // route through the substrate-primitive typed dispatch"
15583        // discipline extended onto the outer top-level [`Caixa`] M3
15584        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
15585        // enumerator carries the same routing invariant as its M2
15586        // sibling.
15587        use crate::aplicacao::MeshPolicy;
15588        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
15589        let slots = c.declared_mesh_slots();
15590        assert!(
15591            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
15592            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
15593             when `:politicas` is Some (even for MeshPolicy::default()) \
15594             — the accessor and the enumerator gate must route through \
15595             the same substrate-primitive typed dispatch on the outer \
15596             :politicas presence bit (got slots={slots:?})",
15597        );
15598        let c = caixa_aplicacao_with_politicas(None);
15599        let slots = c.declared_mesh_slots();
15600        assert!(
15601            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
15602            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
15603             when `:politicas` is None — the author-omitted arm must \
15604             route through the accessor's None-return unchanged (got \
15605             slots={slots:?})",
15606        );
15607    }
15608
15609    #[test]
15610    fn aplicacao_view_politicas_arm_folds_through_accessor() {
15611        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
15612        // Aplicacao-composition seed must fold through
15613        // [`Caixa::politicas`], not the raw
15614        // `self.politicas.clone().unwrap_or_default()` field-borrow.
15615        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
15616        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
15617        // must surface a projected [`crate::AplicacaoSpec`] whose
15618        // `politicas().timeout()` field byte-equals the outer
15619        // composite's `timeout` scalar (the fold must project the
15620        // authored composite verbatim), a `Caixa { politicas:
15621        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
15622        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
15623        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
15624        // fold's empty-composite arm collapses to the same default the
15625        // author-omitted arm does), and a `Caixa { politicas: None,
15626        // kind: Aplicacao, .. }` must surface an
15627        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
15628        // [`crate::aplicacao::MeshPolicy::default`] (the "author
15629        // omitted the slot entirely" arm folds through the
15630        // `unwrap_or_default` onto the cluster-default). The triad
15631        // jointly pins the accessor + Aplicacao-composition seed
15632        // composition: any future silent detour that had the accessor
15633        // divert the raw slot away from the seed's fold (an operator-
15634        // resolved overlay's default-fold arm silently differing from
15635        // the raw slot's default-fold arm) would silently split the
15636        // build-time mesh-artifact emission gate from the caixa-mesh
15637        // renderer's Aplicacao-view input at the composition boundary.
15638        use crate::aplicacao::MeshPolicy;
15639        use std::time::Duration;
15640        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
15641            timeout: Some(Duration::from_secs(30)),
15642            ..Default::default()
15643        }));
15644        let view = c.aplicacao_view().unwrap();
15645        assert_eq!(
15646            view.politicas().timeout(),
15647            Some(Duration::from_secs(30)),
15648            "Caixa::aplicacao_view must fold the authored :politicas \
15649             :timeout scalar through the accessor verbatim onto the \
15650             projected AplicacaoSpec — a future silent detour at the \
15651             seed's fold arm would surface here as a projected-scalar \
15652             drift (got {:?})",
15653            view.politicas().timeout(),
15654        );
15655        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
15656        let view = c.aplicacao_view().unwrap();
15657        assert_eq!(
15658            view.politicas(),
15659            &MeshPolicy::default(),
15660            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
15661             through the accessor onto MeshPolicy::default — the empty- \
15662             composite arm collapses to the same default the author- \
15663             omitted arm does (got {:?})",
15664            view.politicas(),
15665        );
15666        let c = caixa_aplicacao_with_politicas(None);
15667        let view = c.aplicacao_view().unwrap();
15668        assert_eq!(
15669            view.politicas(),
15670            &MeshPolicy::default(),
15671            "Caixa::aplicacao_view must fold None through the accessor's \
15672             unwrap_or_default onto MeshPolicy::default — the author- \
15673             omitted arm must route through the accessor's None-return \
15674             unchanged (got {:?})",
15675            view.politicas(),
15676        );
15677    }
15678
15679    #[test]
15680    fn politicas_projects_option_ref_by_borrow() {
15681        // The by-borrow pin: [`Caixa::politicas`] returns
15682        // `Option<&MeshPolicy>` by borrow — the returned reference
15683        // borrows the underlying `Option<MeshPolicy>` storage of the
15684        // `:politicas` slot and the accessor must not clone the
15685        // backing composite on every call. Peer of the sibling
15686        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
15687        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
15688        // pins on the outer top-level [`Caixa`]
15689        // `Option<&Composite>`-return sub-family — extended here to
15690        // the third axis of the same sub-family: the accessor's
15691        // returned reference must borrow from `&self` (the returned
15692        // reference's lifetime is tied to `&self`), and calling the
15693        // accessor twice on the same [`Caixa`] must yield references
15694        // that are pointer-equal (the underlying byte-buffer is the
15695        // storage `MeshPolicy`'s allocation, not a fresh copy) as
15696        // well as value-equal (idempotent, no side effects on
15697        // `&self`).
15698        //
15699        // Pins against a future silent detour that returned an owned
15700        // `MeshPolicy` (which would type-check via the `Clone` impl
15701        // but silently clone on every call), a `&MeshPolicy` panic-
15702        // return on the `None` arm (which would collapse the load-
15703        // bearing `Option` presence-bit into a runtime panic), or a
15704        // one-arm-only accessor that returned a saturating composite
15705        // on some sentinel input.
15706        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
15707        use std::time::Duration;
15708        for politicas in [
15709            Some(MeshPolicy::default()),
15710            Some(MeshPolicy {
15711                timeout: Some(Duration::from_secs(30)),
15712                retries: Some(3),
15713                circuit_breaker: Some(CircuitBreaker {
15714                    max_failures: 5,
15715                    window: Duration::from_secs(60),
15716                }),
15717                mtls_required: Some(true),
15718                rate_limit: Some(RateLimit {
15719                    rate: 100,
15720                    window: Duration::from_secs(1),
15721                }),
15722            }),
15723        ] {
15724            let c = caixa_aplicacao_with_politicas(politicas.clone());
15725            let first = c.politicas().unwrap();
15726            let second = c.politicas().unwrap();
15727            assert_eq!(
15728                first, second,
15729                "Caixa::politicas must be idempotent — two successive \
15730                 calls on the same &self must return the same \
15731                 &MeshPolicy",
15732            );
15733            assert!(
15734                std::ptr::eq(first, second),
15735                "Caixa::politicas must borrow the underlying \
15736                 Option<MeshPolicy> storage — two successive calls \
15737                 must return references with the same backing pointer \
15738                 (a fresh MeshPolicy clone would change the pointer on \
15739                 every call)",
15740            );
15741            assert_eq!(
15742                Some(first),
15743                politicas.as_ref(),
15744                "Caixa::politicas must return :politicas verbatim by \
15745                 borrow — got {first:?}, expected {:?}",
15746                politicas.as_ref(),
15747            );
15748        }
15749        let c = caixa_aplicacao_with_politicas(None);
15750        assert!(
15751            c.politicas().is_none(),
15752            "Caixa::politicas must return None when :politicas is \
15753             absent — the author-omitted arm must project through the \
15754             accessor's Option::None unchanged",
15755        );
15756    }
15757
15758    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
15759
15760    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
15761        use crate::aplicacao::{Membro, WitContract};
15762        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15763        c.kind = CaixaKind::Aplicacao;
15764        c.membros = vec![Membro {
15765            caixa: "a".into(),
15766            versao: "^0.1".into(),
15767        }];
15768        c.contratos = vec![WitContract {
15769            de: "a".into(),
15770            para: "a".into(),
15771            wit: "wasi:http/proxy".into(),
15772            endpoint: Some("/x".into()),
15773            subject: None,
15774            slot: None,
15775        }];
15776        c.placement = placement;
15777        c
15778    }
15779
15780    #[test]
15781    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
15782        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
15783        // composite optional-composite-reference-shape pin:
15784        // [`Caixa::placement`] must return the `:placement` typed
15785        // `Option<Placement>` verbatim as an `Option<&Placement>`
15786        // reference over the same backing storage the raw
15787        // `self.placement.as_ref()` field access borrows from,
15788        // byte-equal across every representative fixture in the
15789        // accept-set — the author-omitted `None` shape (the
15790        // "cluster-default applies" partition every downstream mesh-
15791        // artifact emitter treats as "emit no `:placement` overlay"),
15792        // the empty-composite `Some(Placement { .. default })` shape
15793        // (`estrategia: SingleNode`, empty clusters, no shard-key /
15794        // affinity — the outer presence-bit is `Some` so
15795        // [`Caixa::declared_mesh_slots`] still pushes the
15796        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
15797        // `Replicated`-on-two-clusters fixture (the canonical shape a
15798        // stateless HTTP Aplicacao carries), and a fully-populated
15799        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
15800        // shape a stateful Akka-style cluster-sharding Aplicacao
15801        // carries).
15802        //
15803        // Pins against a future silent detour that returned a fresh-
15804        // cloned [`crate::aplicacao::Placement`] copy (which would
15805        // type-check via the `Clone` impl but silently break every
15806        // downstream caller that relied on the reference sharing the
15807        // composite's backing identity), a reference to an operator-
15808        // resolved overlay (the future per-cluster
15809        // `:placement-overrides` slot — its resolution must land at
15810        // exactly this accessor body, not silently divert the raw
15811        // slot away from the peer [`Caixa::declared_mesh_slots`]
15812        // enumerator's presence probe), a `None` →
15813        // `Some(Placement::default)` cluster-default projection (which
15814        // would collapse the load-bearing "author-omitted `:placement`
15815        // ⇒ cluster-default applies" partition the peer
15816        // [`Caixa::declared_mesh_slots`] enumerator and the peer
15817        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
15818        // read), or an axis-shuffled projection (a future detour that
15819        // swapped `clusters` and `affinity` through the accessor would
15820        // silently split the paired [`Caixa::aplicacao_view`] seed's
15821        // fold input from the sibling M3 mesh-artifact emitter's
15822        // projection input).
15823        //
15824        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
15825        // composite-reference accessor pin on the substrate primitive
15826        // — peer of the sibling
15827        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15828        // (b2bd9d7),
15829        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
15830        // (35d8b52), and
15831        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
15832        // (5d23d29) opening triad pins on the outer top-level
15833        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
15834        // here to the second of the three M3 mesh-slot axes so the
15835        // opening four-fifths of the outer `Option<&Composite>` sub-
15836        // family carries the same "byte-equal, borrow-shared,
15837        // presence-bit-preserved" outer-accessor discipline.
15838        use crate::aplicacao::{Placement, PlacementStrategy};
15839        let fixtures: Vec<Option<Placement>> = vec![
15840            None,
15841            Some(Placement::default()),
15842            Some(Placement {
15843                estrategia: PlacementStrategy::Replicated,
15844                clusters: vec!["rio".into(), "sao-paulo".into()],
15845                affinity: None,
15846                shard_key: None,
15847            }),
15848            Some(Placement {
15849                estrategia: PlacementStrategy::Sharded,
15850                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
15851                affinity: Some("data-locality".into()),
15852                shard_key: Some("$tenantId".into()),
15853            }),
15854        ];
15855        for placement in fixtures {
15856            let c = caixa_aplicacao_with_placement(placement.clone());
15857            assert_eq!(
15858                c.placement(),
15859                placement.as_ref(),
15860                "Caixa::placement must return :placement verbatim (got \
15861                 {:?}, expected {:?})",
15862                c.placement(),
15863                placement.as_ref(),
15864            );
15865            match (c.placement(), c.placement.as_ref()) {
15866                (Some(a), Some(b)) => assert!(
15867                    std::ptr::eq(a, b),
15868                    "Caixa::placement accessor and self.placement.as_ref() \
15869                     field access must borrow the same backing storage \
15870                     — the accessor is the substrate-primitive typed \
15871                     dispatch every downstream Aplicacao-distribution- \
15872                     overlay composite consumer must route through, and \
15873                     a reference-identity split would silently break \
15874                     every consumer that relied on the borrow sharing \
15875                     the composite's storage",
15876                ),
15877                (None, None) => {}
15878                _ => panic!(
15879                    "Caixa::placement presence bit must byte-equal \
15880                     self.placement.is_some() — a presence-bit drift \
15881                     would silently split the paired \
15882                     Caixa::aplicacao_view Aplicacao-composition seed's \
15883                     traversal head from the peer \
15884                     Caixa::declared_mesh_slots M3 declared-slot \
15885                     enumerator's presence probe",
15886                ),
15887            }
15888            assert_eq!(
15889                c.placement().is_some(),
15890                c.placement.is_some(),
15891                "Caixa::placement().is_some() must byte-equal \
15892                 self.placement.is_some() — a presence-bit drift would \
15893                 silently split every downstream Option<&Placement> \
15894                 consumer's partition on the cluster-default arm",
15895            );
15896        }
15897    }
15898
15899    #[test]
15900    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
15901        // Composition pin: [`Caixa::declared_mesh_slots`]'s
15902        // `:placement` presence-probe arm must key off
15903        // [`Caixa::placement`], not the raw `self.placement.is_some()`
15904        // field-probe. Structurally: a `Caixa { placement:
15905        // Some(Placement::default()), .. }` must still push
15906        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
15907        // presence bit is `Some`, so the M3 kind-coherence gate must
15908        // surface the slot as "declared" even when every per-axis
15909        // scalar defers to the cluster-default arm), and a `Caixa {
15910        // placement: None, .. }` must NOT push the label (the "author
15911        // omitted the slot entirely" partition). The pair jointly pins
15912        // the accessor + declared-slot enumerator composition: any
15913        // future silent detour that had the accessor collapse
15914        // `Some(Placement::default())` to `None` (a `.filter(|p|
15915        // p.clusters().is_empty().not())` projection) would silently
15916        // absorb the "declared but empty" arm at the accessor boundary
15917        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
15918        // kind-coherence gate would silently accept a struct-literal
15919        // `Caixa` carrying the drift.
15920        //
15921        // Peer of the sibling
15922        // `declared_servico_slots_limits_arm_routes_through_accessor`
15923        // (b2bd9d7),
15924        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15925        // (35d8b52), and
15926        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
15927        // (5d23d29) composition pins on the sibling `:limits` /
15928        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
15929        // — same "the enumerator gate must route through the
15930        // substrate-primitive typed dispatch" discipline extended onto
15931        // the second of the three M3 mesh-slot axes so the
15932        // [`Caixa::declared_mesh_slots`] enumerator carries the same
15933        // routing invariant on the `:placement` arm as the peer
15934        // `:politicas` arm.
15935        use crate::aplicacao::Placement;
15936        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
15937        let slots = c.declared_mesh_slots();
15938        assert!(
15939            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
15940            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
15941             when `:placement` is Some (even for Placement::default()) \
15942             — the accessor and the enumerator gate must route through \
15943             the same substrate-primitive typed dispatch on the outer \
15944             :placement presence bit (got slots={slots:?})",
15945        );
15946        let c = caixa_aplicacao_with_placement(None);
15947        let slots = c.declared_mesh_slots();
15948        assert!(
15949            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
15950            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
15951             when `:placement` is None — the author-omitted arm must \
15952             route through the accessor's None-return unchanged (got \
15953             slots={slots:?})",
15954        );
15955    }
15956
15957    #[test]
15958    fn aplicacao_view_placement_arm_folds_through_accessor() {
15959        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
15960        // Aplicacao-composition seed must fold through
15961        // [`Caixa::placement`], not the raw
15962        // `self.placement.clone().unwrap_or_default()` field-borrow.
15963        // Structurally: a `Caixa { placement: Some(Placement {
15964        // estrategia: Replicated, clusters: ["rio"], .. default }),
15965        // kind: Aplicacao, .. }` must surface a projected
15966        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
15967        // `placement().clusters()` byte-equal the outer composite's
15968        // authored values (the fold must project the authored
15969        // composite verbatim), a `Caixa { placement:
15970        // Some(Placement::default()), kind: Aplicacao, .. }` must
15971        // surface an [`crate::AplicacaoSpec`] whose `placement()`
15972        // byte-equals [`crate::aplicacao::Placement::default`] (the
15973        // fold's empty-composite arm collapses to the same default
15974        // the author-omitted arm does), and a `Caixa { placement:
15975        // None, kind: Aplicacao, .. }` must surface an
15976        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
15977        // [`crate::aplicacao::Placement::default`] (the "author
15978        // omitted the slot entirely" arm folds through the
15979        // `unwrap_or_default` onto the cluster-default). The triad
15980        // jointly pins the accessor + Aplicacao-composition seed
15981        // composition: any future silent detour that had the accessor
15982        // divert the raw slot away from the seed's fold (an operator-
15983        // resolved overlay's default-fold arm silently differing from
15984        // the raw slot's default-fold arm) would silently split the
15985        // build-time distribution-artifact emission gate from the
15986        // caixa-mesh renderer's Aplicacao-view input at the
15987        // composition boundary.
15988        use crate::aplicacao::{Placement, PlacementStrategy};
15989        let c = caixa_aplicacao_with_placement(Some(Placement {
15990            estrategia: PlacementStrategy::Replicated,
15991            clusters: vec!["rio".into()],
15992            affinity: None,
15993            shard_key: None,
15994        }));
15995        let view = c.aplicacao_view().unwrap();
15996        assert_eq!(
15997            view.placement().estrategia(),
15998            PlacementStrategy::Replicated,
15999            "Caixa::aplicacao_view must fold the authored :placement \
16000             :estrategia scalar through the accessor verbatim onto the \
16001             projected AplicacaoSpec — a future silent detour at the \
16002             seed's fold arm would surface here as a projected-scalar \
16003             drift (got {:?})",
16004            view.placement().estrategia(),
16005        );
16006        assert_eq!(
16007            view.placement().clusters(),
16008            &["rio"],
16009            "Caixa::aplicacao_view must fold the authored :placement \
16010             :clusters list through the accessor verbatim onto the \
16011             projected AplicacaoSpec — a future silent detour at the \
16012             seed's fold arm would surface here as a projected-list \
16013             drift (got {:?})",
16014            view.placement().clusters(),
16015        );
16016        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
16017        let view = c.aplicacao_view().unwrap();
16018        assert_eq!(
16019            view.placement(),
16020            &Placement::default(),
16021            "Caixa::aplicacao_view must fold Some(Placement::default()) \
16022             through the accessor onto Placement::default — the empty- \
16023             composite arm collapses to the same default the author- \
16024             omitted arm does (got {:?})",
16025            view.placement(),
16026        );
16027        let c = caixa_aplicacao_with_placement(None);
16028        let view = c.aplicacao_view().unwrap();
16029        assert_eq!(
16030            view.placement(),
16031            &Placement::default(),
16032            "Caixa::aplicacao_view must fold None through the accessor's \
16033             unwrap_or_default onto Placement::default — the author- \
16034             omitted arm must route through the accessor's None-return \
16035             unchanged (got {:?})",
16036            view.placement(),
16037        );
16038    }
16039
16040    #[test]
16041    fn placement_projects_option_ref_by_borrow() {
16042        // The by-borrow pin: [`Caixa::placement`] returns
16043        // `Option<&Placement>` by borrow — the returned reference
16044        // borrows the underlying `Option<Placement>` storage of the
16045        // `:placement` slot and the accessor must not clone the
16046        // backing composite on every call. Peer of the sibling
16047        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
16048        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
16049        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
16050        // pins on the outer top-level [`Caixa`]
16051        // `Option<&Composite>`-return sub-family — extended here to
16052        // the fourth axis of the same sub-family: the accessor's
16053        // returned reference must borrow from `&self` (the returned
16054        // reference's lifetime is tied to `&self`), and calling the
16055        // accessor twice on the same [`Caixa`] must yield references
16056        // that are pointer-equal (the underlying byte-buffer is the
16057        // storage `Placement`'s allocation, not a fresh copy) as well
16058        // as value-equal (idempotent, no side effects on `&self`).
16059        //
16060        // Pins against a future silent detour that returned an owned
16061        // `Placement` (which would type-check via the `Clone` impl
16062        // but silently clone on every call), a `&Placement` panic-
16063        // return on the `None` arm (which would collapse the load-
16064        // bearing `Option` presence-bit into a runtime panic), or a
16065        // one-arm-only accessor that returned a saturating composite
16066        // on some sentinel input.
16067        use crate::aplicacao::{Placement, PlacementStrategy};
16068        for placement in [
16069            Some(Placement::default()),
16070            Some(Placement {
16071                estrategia: PlacementStrategy::Sharded,
16072                clusters: vec!["rio".into(), "sao-paulo".into()],
16073                affinity: Some("data-locality".into()),
16074                shard_key: Some("$tenantId".into()),
16075            }),
16076        ] {
16077            let c = caixa_aplicacao_with_placement(placement.clone());
16078            let first = c.placement().unwrap();
16079            let second = c.placement().unwrap();
16080            assert_eq!(
16081                first, second,
16082                "Caixa::placement must be idempotent — two successive \
16083                 calls on the same &self must return the same \
16084                 &Placement",
16085            );
16086            assert!(
16087                std::ptr::eq(first, second),
16088                "Caixa::placement must borrow the underlying \
16089                 Option<Placement> storage — two successive calls \
16090                 must return references with the same backing pointer \
16091                 (a fresh Placement clone would change the pointer on \
16092                 every call)",
16093            );
16094            assert_eq!(
16095                Some(first),
16096                placement.as_ref(),
16097                "Caixa::placement must return :placement verbatim by \
16098                 borrow — got {first:?}, expected {:?}",
16099                placement.as_ref(),
16100            );
16101        }
16102        let c = caixa_aplicacao_with_placement(None);
16103        assert!(
16104            c.placement().is_none(),
16105            "Caixa::placement must return None when :placement is \
16106             absent — the author-omitted arm must project through the \
16107             accessor's Option::None unchanged",
16108        );
16109    }
16110
16111    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
16112
16113    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
16114        use crate::aplicacao::{Membro, WitContract};
16115        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16116        c.kind = CaixaKind::Aplicacao;
16117        c.membros = vec![Membro {
16118            caixa: "a".into(),
16119            versao: "^0.1".into(),
16120        }];
16121        c.contratos = vec![WitContract {
16122            de: "a".into(),
16123            para: "a".into(),
16124            wit: "wasi:http/proxy".into(),
16125            endpoint: Some("/x".into()),
16126            subject: None,
16127            slot: None,
16128        }];
16129        c.entrada = entrada;
16130        c
16131    }
16132
16133    #[test]
16134    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
16135        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
16136        // composite optional-composite-reference-shape pin:
16137        // [`Caixa::entrada`] must return the `:entrada` typed
16138        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
16139        // reference over the same backing storage the raw
16140        // `self.entrada.as_ref()` field access borrows from,
16141        // byte-equal across every representative fixture in the
16142        // accept-set — the author-omitted `None` shape (the
16143        // "cluster-internal Aplicacao" partition every downstream
16144        // Gateway-API emitter treats as "emit no listener + no
16145        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
16146        // (empty `paths` — the resolved-paths fallback the peer
16147        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
16148        // onto the substrate catch-all), and a fully-populated
16149        // multi-path-with-non-default-port fixture (the canonical
16150        // shape a public HTTP Aplicacao carries).
16151        //
16152        // Pins against a future silent detour that returned a fresh-
16153        // cloned [`crate::aplicacao::Entrada`] copy (which would
16154        // type-check via the `Clone` impl but silently break every
16155        // downstream caller that relied on the reference sharing the
16156        // composite's backing identity), a reference to an operator-
16157        // resolved overlay (the future per-cluster
16158        // `:entrada-overrides` slot — its resolution must land at
16159        // exactly this accessor body, not silently divert the raw
16160        // slot away from the peer [`Caixa::declared_mesh_slots`]
16161        // enumerator's presence probe), or an axis-shuffled projection
16162        // (a future detour that swapped `host` and `para` through the
16163        // accessor would silently split the paired
16164        // [`Caixa::aplicacao_view`] seed's forward input from the
16165        // sibling M3 gateway-artifact emitter's projection input).
16166        //
16167        // Fifth and final outer top-level [`Caixa`]
16168        // `Option<&Composite>`-return composite-reference accessor pin
16169        // on the substrate primitive — peer of the sibling
16170        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16171        // (b2bd9d7),
16172        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
16173        // (35d8b52),
16174        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
16175        // (5d23d29), and
16176        // `placement_returns_placement_option_ref_verbatim_across_permutations`
16177        // (4fb8074) opening tetrad pins on the outer top-level
16178        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
16179        // here to the third and final M3 mesh-slot axis so the closed
16180        // outer `Option<&Composite>` sub-family carries the same
16181        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
16182        // accessor discipline across all five arms.
16183        use crate::aplicacao::Entrada;
16184        let fixtures: Vec<Option<Entrada>> = vec![
16185            None,
16186            Some(Entrada {
16187                host: "checkout.quero.cloud".into(),
16188                para: "gateway".into(),
16189                paths: Vec::new(),
16190                port: crate::DEFAULT_SERVICO_PORT,
16191            }),
16192            Some(Entrada {
16193                host: "api.pleme.io".into(),
16194                para: "public-api".into(),
16195                paths: vec!["/v1".into(), "/v2".into()],
16196                port: 8080,
16197            }),
16198        ];
16199        for entrada in fixtures {
16200            let c = caixa_aplicacao_with_entrada(entrada.clone());
16201            assert_eq!(
16202                c.entrada(),
16203                entrada.as_ref(),
16204                "Caixa::entrada must return :entrada verbatim (got \
16205                 {:?}, expected {:?})",
16206                c.entrada(),
16207                entrada.as_ref(),
16208            );
16209            match (c.entrada(), c.entrada.as_ref()) {
16210                (Some(a), Some(b)) => assert!(
16211                    std::ptr::eq(a, b),
16212                    "Caixa::entrada accessor and self.entrada.as_ref() \
16213                     field access must borrow the same backing storage \
16214                     — the accessor is the substrate-primitive typed \
16215                     dispatch every downstream Aplicacao-external- \
16216                     gateway composite consumer must route through, and \
16217                     a reference-identity split would silently break \
16218                     every consumer that relied on the borrow sharing \
16219                     the composite's storage",
16220                ),
16221                (None, None) => {}
16222                _ => panic!(
16223                    "Caixa::entrada presence bit must byte-equal \
16224                     self.entrada.is_some() — a presence-bit drift \
16225                     would silently split the paired \
16226                     Caixa::aplicacao_view Aplicacao-composition seed's \
16227                     traversal head from the peer \
16228                     Caixa::declared_mesh_slots M3 declared-slot \
16229                     enumerator's presence probe",
16230                ),
16231            }
16232            assert_eq!(
16233                c.entrada().is_some(),
16234                c.entrada.is_some(),
16235                "Caixa::entrada().is_some() must byte-equal \
16236                 self.entrada.is_some() — a presence-bit drift would \
16237                 silently split every downstream Option<&Entrada> \
16238                 consumer's partition on the cluster-internal arm",
16239            );
16240        }
16241    }
16242
16243    #[test]
16244    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
16245        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
16246        // presence-probe arm must key off [`Caixa::entrada`], not the
16247        // raw `self.entrada.is_some()` field-probe. Structurally: a
16248        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
16249        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
16250        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
16251        // presence bit is `Some`, so the M3 kind-coherence gate must
16252        // surface the slot as "declared" even when every per-axis
16253        // scalar defers to the substrate catch-all / default port),
16254        // and a `Caixa { entrada: None, .. }` must NOT push the label
16255        // (the "author omitted the slot entirely" partition). The pair
16256        // jointly pins the accessor + declared-slot enumerator
16257        // composition: any future silent detour that had the accessor
16258        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
16259        // `.filter(|e| !e.paths.is_empty())` projection) would silently
16260        // absorb the "declared but empty-paths" arm at the accessor
16261        // boundary and the
16262        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16263        // coherence gate would silently accept a struct-literal
16264        // `Caixa` carrying the drift.
16265        //
16266        // Peer of the sibling
16267        // `declared_servico_slots_limits_arm_routes_through_accessor`
16268        // (b2bd9d7),
16269        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16270        // (35d8b52),
16271        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
16272        // (5d23d29), and
16273        // `declared_mesh_slots_placement_arm_routes_through_accessor`
16274        // (4fb8074) composition pins on the sibling `:limits` /
16275        // `:behavior` / `:politicas` / `:placement` outer-
16276        // `Option<&Composite>` arms — same "the enumerator gate must
16277        // route through the substrate-primitive typed dispatch"
16278        // discipline extended onto the third and final M3 mesh-slot
16279        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
16280        // carries the routing invariant on every M3 mesh-slot arm.
16281        use crate::aplicacao::Entrada;
16282        let c = caixa_aplicacao_with_entrada(Some(Entrada {
16283            host: "checkout.quero.cloud".into(),
16284            para: "gateway".into(),
16285            paths: Vec::new(),
16286            port: crate::DEFAULT_SERVICO_PORT,
16287        }));
16288        let slots = c.declared_mesh_slots();
16289        assert!(
16290            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
16291            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
16292             `:entrada` is Some (even for empty-paths / default-port) \
16293             — the accessor and the enumerator gate must route through \
16294             the same substrate-primitive typed dispatch on the outer \
16295             :entrada presence bit (got slots={slots:?})",
16296        );
16297        let c = caixa_aplicacao_with_entrada(None);
16298        let slots = c.declared_mesh_slots();
16299        assert!(
16300            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
16301            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
16302             when `:entrada` is None — the author-omitted arm must \
16303             route through the accessor's None-return unchanged (got \
16304             slots={slots:?})",
16305        );
16306    }
16307
16308    #[test]
16309    fn aplicacao_view_entrada_arm_folds_through_accessor() {
16310        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
16311        // Aplicacao-composition seed must fold through
16312        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
16313        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
16314        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
16315        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
16316        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
16317        // equals the outer composite's authored value (the fold must
16318        // project the authored composite verbatim), and a `Caixa {
16319        // entrada: None, kind: Aplicacao, .. }` must surface an
16320        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
16321        // "author omitted the slot entirely" arm folds through the
16322        // accessor's `Option::cloned` onto the same `None` presence
16323        // bit — unlike the peer `:politicas` / `:placement` arms
16324        // `:entrada` has no cluster-default fold, the omitted arm
16325        // stays omitted). The pair jointly pins the accessor +
16326        // Aplicacao-composition seed composition: any future silent
16327        // detour that had the accessor divert the raw slot away from
16328        // the seed's fold (an operator-resolved overlay's forward arm
16329        // silently differing from the raw slot's forward arm) would
16330        // silently split the build-time gateway-artifact emission gate
16331        // from the caixa-mesh renderer's Aplicacao-view input at the
16332        // composition boundary.
16333        use crate::aplicacao::Entrada;
16334        let authored = Entrada {
16335            host: "api.pleme.io".into(),
16336            para: "public-api".into(),
16337            paths: vec!["/v1".into()],
16338            port: 8080,
16339        };
16340        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
16341        let view = c.aplicacao_view().unwrap();
16342        assert_eq!(
16343            view.entrada(),
16344            Some(&authored),
16345            "Caixa::aplicacao_view must fold the authored :entrada \
16346             composite through the accessor verbatim onto the \
16347             projected AplicacaoSpec — a future silent detour at the \
16348             seed's fold arm would surface here as a projected- \
16349             composite drift (got {:?})",
16350            view.entrada(),
16351        );
16352        let c = caixa_aplicacao_with_entrada(None);
16353        let view = c.aplicacao_view().unwrap();
16354        assert!(
16355            view.entrada().is_none(),
16356            "Caixa::aplicacao_view must fold None through the \
16357             accessor's Option::cloned onto None — the author- \
16358             omitted arm must route through the accessor's None-return \
16359             unchanged (got {:?})",
16360            view.entrada(),
16361        );
16362    }
16363
16364    #[test]
16365    fn entrada_projects_option_ref_by_borrow() {
16366        // The by-borrow pin: [`Caixa::entrada`] returns
16367        // `Option<&Entrada>` by borrow — the returned reference
16368        // borrows the underlying `Option<Entrada>` storage of the
16369        // `:entrada` slot and the accessor must not clone the backing
16370        // composite on every call. Peer of the sibling
16371        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
16372        // `behavior_projects_option_ref_by_borrow` (35d8b52),
16373        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
16374        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
16375        // borrow pins on the outer top-level [`Caixa`]
16376        // `Option<&Composite>`-return sub-family — extended here to
16377        // the fifth and final axis of the same sub-family, closing
16378        // the discipline: the accessor's returned reference must
16379        // borrow from `&self` (the returned reference's lifetime is
16380        // tied to `&self`), and calling the accessor twice on the
16381        // same [`Caixa`] must yield references that are pointer-equal
16382        // (the underlying byte-buffer is the storage `Entrada`'s
16383        // allocation, not a fresh copy) as well as value-equal
16384        // (idempotent, no side effects on `&self`).
16385        //
16386        // Pins against a future silent detour that returned an owned
16387        // `Entrada` (which would type-check via the `Clone` impl but
16388        // silently clone on every call), a `&Entrada` panic-return on
16389        // the `None` arm (which would collapse the load-bearing
16390        // `Option` presence-bit into a runtime panic), or a one-arm-
16391        // only accessor that returned a saturating composite on some
16392        // sentinel input.
16393        use crate::aplicacao::Entrada;
16394        for entrada in [
16395            Some(Entrada {
16396                host: "checkout.quero.cloud".into(),
16397                para: "gateway".into(),
16398                paths: Vec::new(),
16399                port: crate::DEFAULT_SERVICO_PORT,
16400            }),
16401            Some(Entrada {
16402                host: "api.pleme.io".into(),
16403                para: "public-api".into(),
16404                paths: vec!["/v1".into(), "/v2".into()],
16405                port: 8080,
16406            }),
16407        ] {
16408            let c = caixa_aplicacao_with_entrada(entrada.clone());
16409            let first = c.entrada().unwrap();
16410            let second = c.entrada().unwrap();
16411            assert_eq!(
16412                first, second,
16413                "Caixa::entrada must be idempotent — two successive \
16414                 calls on the same &self must return the same &Entrada",
16415            );
16416            assert!(
16417                std::ptr::eq(first, second),
16418                "Caixa::entrada must borrow the underlying \
16419                 Option<Entrada> storage — two successive calls must \
16420                 return references with the same backing pointer (a \
16421                 fresh Entrada clone would change the pointer on every \
16422                 call)",
16423            );
16424            assert_eq!(
16425                Some(first),
16426                entrada.as_ref(),
16427                "Caixa::entrada must return :entrada verbatim by \
16428                 borrow — got {first:?}, expected {:?}",
16429                entrada.as_ref(),
16430            );
16431        }
16432        let c = caixa_aplicacao_with_entrada(None);
16433        assert!(
16434            c.entrada().is_none(),
16435            "Caixa::entrada must return None when :entrada is absent \
16436             — the author-omitted arm must project through the \
16437             accessor's Option::None unchanged",
16438        );
16439    }
16440
16441    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
16442
16443    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
16444        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16445        c.estrategia = estrategia;
16446        c
16447    }
16448
16449    #[test]
16450    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
16451        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
16452        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
16453        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
16454        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
16455        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
16456        // over the same discriminant the raw `self.estrategia` field
16457        // access carries, byte-equal across every representative fixture
16458        // in the accept-set — the author-omitted `None` shape (the
16459        // "defer to [`RestartStrategy::default`] through the
16460        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
16461        // every non-`Supervisor`-kind `defcaixa` carries by
16462        // `#[serde(default)]`), and each of the four closed-set variants
16463        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
16464        // / [`RestartStrategy::RestForOne`] /
16465        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
16466        // partitions on.
16467        //
16468        // Pins against a future silent detour that re-derived the
16469        // strategy from a peer axis (an accidental fallback to
16470        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
16471        // collapse that read the outer `:children` list-length axis into
16472        // the strategy discriminator at the accessor boundary), a
16473        // stale-derive detour that substituted [`RestartStrategy::default`]
16474        // when the outer `Option` held `None` (which would silently
16475        // collapse the load-bearing "author explicitly declared
16476        // `:estrategia OneForOne`" vs "author omitted the slot and
16477        // inherited the default" partition the [`Self::declared_supervisor_slots`]
16478        // presence-probe reads — the enumerator gate would still push
16479        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
16480        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
16481        // kind-coherence gate's traversal head from the
16482        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
16483        // composition head), a reference to an operator-resolved overlay
16484        // (the future per-cluster `:estrategia-overrides` slot — its
16485        // resolution must land at exactly this accessor body, not
16486        // silently divert the raw slot away from a second consumer), or
16487        // an axis-remap projection (a future detour that mapped
16488        // `OneForAll` through the accessor onto `OneForOne` would
16489        // silently split every downstream sibling-restart-strategy
16490        // consumer's per-arm fan-out).
16491        //
16492        // First outer top-level [`Caixa`] `Option<Copy>`-return
16493        // supervisor-tree-slot flat-spread accessor pin on the substrate
16494        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
16495        // projection pattern the sibling per-`Caixa` `:max-restarts` /
16496        // `:restart-window` future outer-scalar pins fold on. Peer of
16497        // the inner-altitude
16498        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
16499        // (eafb619) pin on the post-composition [`SupervisorSpec`]
16500        // altitude — same "the substrate-primitive accessor must byte-
16501        // equal the raw field access verbatim across every author-
16502        // declared value" discipline extended onto the pre-composition
16503        // outer author-surface [`Caixa`] altitude. Peer of the closed
16504        // outer-`Caixa` `Option<&Composite>` composite-reference family
16505        // the sibling `limits` / `behavior` / `politicas` / `placement` /
16506        // `entrada`
16507        // `..._returns_..._option_ref_verbatim_across_permutations` pins
16508        // already carry on the outer `Option<&Composite>` altitude.
16509        use crate::supervisor::RestartStrategy;
16510        let fixtures: Vec<Option<RestartStrategy>> = vec![
16511            None,
16512            Some(RestartStrategy::OneForOne),
16513            Some(RestartStrategy::OneForAll),
16514            Some(RestartStrategy::RestForOne),
16515            Some(RestartStrategy::SimpleOneForOne),
16516        ];
16517        for estrategia in fixtures {
16518            let c = caixa_with_estrategia(estrategia);
16519            assert_eq!(
16520                c.estrategia(),
16521                estrategia,
16522                "Caixa::estrategia must return :estrategia verbatim (got \
16523                 {:?}, expected {:?})",
16524                c.estrategia(),
16525                estrategia,
16526            );
16527            assert_eq!(
16528                c.estrategia(),
16529                c.estrategia,
16530                "Caixa::estrategia accessor and self.estrategia field \
16531                 access must byte-equal — the accessor is the substrate-\
16532                 primitive typed dispatch every downstream supervisor-\
16533                 tree flat-spread consumer must route through, and a \
16534                 discriminant split would silently break every consumer \
16535                 that relied on the accessor sharing the field's own \
16536                 Option<Copy> shape",
16537            );
16538            assert_eq!(
16539                c.estrategia().is_some(),
16540                c.estrategia.is_some(),
16541                "Caixa::estrategia().is_some() must byte-equal \
16542                 self.estrategia.is_some() — a presence-bit drift would \
16543                 silently split the paired Caixa::declared_supervisor_slots \
16544                 presence-probe arm from the Caixa::supervisor_view \
16545                 unwrap_or_default() fold's composition input",
16546            );
16547        }
16548    }
16549
16550    #[test]
16551    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
16552        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16553        // `:estrategia` presence-probe arm must key off
16554        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
16555        // field-probe. Structurally: every `Caixa { estrategia:
16556        // Some(RestartStrategy::_), .. }` variant must push
16557        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
16558        // (the presence bit is `Some` for every closed-set variant, so
16559        // the M2 supervisor-tree kind-coherence gate must surface the
16560        // slot as "declared" regardless of which variant the author
16561        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
16562        // the label (the "author omitted the slot entirely, deferring
16563        // to [`RestartStrategy::default`] through the supervisor_view
16564        // fold" partition). The pair jointly pins the accessor +
16565        // declared-slot enumerator composition: any future silent detour
16566        // that had the accessor collapse `Some(RestartStrategy::default())`
16567        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
16568        // projection) would silently absorb the "declared but default-
16569        // valued" arm at the accessor boundary and the
16570        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
16571        // coherence gate would silently accept a struct-literal `Caixa`
16572        // carrying the drift.
16573        //
16574        // Peer of the sibling per-`Caixa`
16575        // `declared_servico_slots_limits_arm_routes_through_accessor`
16576        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
16577        // `Option<&LimitsSpec>` composition axis — same "the enumerator
16578        // gate must route through the substrate-primitive typed
16579        // dispatch" discipline extended onto the flat-spread M2
16580        // supervisor-tree `Option<RestartStrategy>`-composition surface,
16581        // opening the outer-`Caixa` supervisor-tree-slot arm of the
16582        // composition-pin family.
16583        use crate::supervisor::RestartStrategy;
16584        for estrategia in [
16585            RestartStrategy::OneForOne,
16586            RestartStrategy::OneForAll,
16587            RestartStrategy::RestForOne,
16588            RestartStrategy::SimpleOneForOne,
16589        ] {
16590            let c = caixa_with_estrategia(Some(estrategia));
16591            let slots = c.declared_supervisor_slots();
16592            assert!(
16593                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
16594                "declared_supervisor_slots must push \
16595                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
16596                 Some({estrategia:?}) — the accessor and the enumerator \
16597                 gate must route through the same substrate-primitive \
16598                 typed dispatch on the outer :estrategia presence bit \
16599                 (got slots={slots:?})",
16600            );
16601        }
16602        let c = caixa_with_estrategia(None);
16603        let slots = c.declared_supervisor_slots();
16604        assert!(
16605            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
16606            "declared_supervisor_slots must NOT push \
16607             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
16608             — the author-omitted arm must route through the accessor's \
16609             None-return unchanged (got slots={slots:?})",
16610        );
16611    }
16612
16613    #[test]
16614    fn supervisor_view_estrategia_arm_routes_through_accessor() {
16615        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
16616        // [`SupervisorSpec`] construction arm must key off
16617        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
16618        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
16619        // for every `:kind Supervisor` `Caixa` carrying an author-
16620        // declared `Some(RestartStrategy::_)` variant, the composed
16621        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
16622        // outer accessor's declared variant unchanged; and for a
16623        // `:kind Supervisor` `Caixa` carrying `None`, the composed
16624        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
16625        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
16626        // arm the flat-spread `unwrap_or_default()` fold projects to on
16627        // the author-omitted arm — this is the *composition* between the
16628        // outer `Option<RestartStrategy>` accessor's presence-bit
16629        // surface and the inner post-composition non-`Option`
16630        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
16631        // pins the accessor + supervisor_view composition: any future
16632        // silent detour that had the accessor promote `None` to
16633        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
16634        // projection) would silently collapse the two arms into one at
16635        // the accessor boundary and the [`Self::declared_supervisor_slots`]
16636        // presence probe would silently drift from the composition site.
16637        //
16638        // Peer of the sibling M2 supervisor-slot post-composition
16639        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
16640        // pin on the [`SupervisorSpec::validate`] altitude — this pin
16641        // extends that inner-altitude accessor-routing discipline onto
16642        // the pre-composition outer author-surface [`Caixa`] altitude,
16643        // pinning the composition edge between the flat-spread outer
16644        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
16645        // `RestartStrategy` axes.
16646        use crate::CaixaKind;
16647        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16648        for estrategia in [
16649            RestartStrategy::OneForOne,
16650            RestartStrategy::OneForAll,
16651            RestartStrategy::RestForOne,
16652            RestartStrategy::SimpleOneForOne,
16653        ] {
16654            let mut c = caixa_with_estrategia(Some(estrategia));
16655            c.kind = CaixaKind::Supervisor;
16656            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
16657            // shape partition through the [`gen_platform::IsVariant`]
16658            // derive-generated
16659            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
16660            // than the raw `matches!(estrategia, RestartStrategy::
16661            // SimpleOneForOne)` open-coded pattern-match — same closed-
16662            // set-typed-enum arm-discriminator dispatch discipline the
16663            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
16664            // convergence (915a934) extended onto its two paired positive
16665            // / negated `matches!` sites and the peer
16666            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
16667            // predicate convergence (766ec63) extended onto the M3 mesh-
16668            // slot per-`:placement` distribution-strategy discriminator
16669            // axis. See the sibling `supervisor::tests::
16670            // round_trip_all_strategies` and
16671            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
16672            // fixtures — the three sites (all test-only,
16673            // acknowledged in 915a934's Prior-commits footnote as the
16674            // outstanding follow-up) now consult one typed dispatch on
16675            // the substrate primitive.
16676            c.children = if estrategia.is_simple_one_for_one() {
16677                Vec::new()
16678            } else {
16679                vec![ChildSpec {
16680                    caixa: "worker".into(),
16681                    versao: "^0.1".into(),
16682                    restart: RestartPolicy::Permanent,
16683                }]
16684            };
16685            let view = c.supervisor_view().expect(
16686                "supervisor_view must materialize a SupervisorSpec for a \
16687                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
16688            );
16689            assert_eq!(
16690                view.estrategia(),
16691                c.estrategia().unwrap(),
16692                "supervisor_view must carry the outer Caixa::estrategia() \
16693                 declared variant onto the composed SupervisorSpec.estrategia \
16694                 field verbatim on the Some arm (got {:?}, expected {:?})",
16695                view.estrategia(),
16696                c.estrategia().unwrap(),
16697            );
16698        }
16699        // The author-omitted arm: outer `None` → composed
16700        // `RestartStrategy::default()` through the flat-spread
16701        // `unwrap_or_default()` fold.
16702        let mut c = caixa_with_estrategia(None);
16703        c.kind = CaixaKind::Supervisor;
16704        // Populate children so the sibling supervisor slots are coherent
16705        // for the [`Self::supervisor_view`] projection; the `:estrategia`
16706        // arm still defers to [`RestartStrategy::default`] on the
16707        // author-omitted arm even when the sibling slots carry values.
16708        c.children = vec![ChildSpec {
16709            caixa: "worker".into(),
16710            versao: "^0.1".into(),
16711            restart: RestartPolicy::Permanent,
16712        }];
16713        let view = c.supervisor_view().expect(
16714            "supervisor_view must materialize a SupervisorSpec for a \
16715             :kind Supervisor Caixa carrying a None `:estrategia` slot",
16716        );
16717        assert_eq!(
16718            view.estrategia(),
16719            RestartStrategy::default(),
16720            "supervisor_view must project the outer Caixa::estrategia() \
16721             None arm onto RestartStrategy::default() through the flat-\
16722             spread unwrap_or_default() fold (got {:?}, expected {:?})",
16723            view.estrategia(),
16724            RestartStrategy::default(),
16725        );
16726        assert!(
16727            c.estrategia().is_none(),
16728            "Caixa::estrategia() must remain None on the author-omitted \
16729             arm — the supervisor_view fold must not mutate the outer \
16730             flat-spread presence bit",
16731        );
16732    }
16733
16734    #[test]
16735    fn estrategia_projects_option_by_copy() {
16736        // The by-`Copy` pin: [`Caixa::estrategia`] returns
16737        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
16738        // the accessor does not borrow `&self` past the call (no
16739        // lifetime on the return type), and calling the accessor twice
16740        // on the same [`Caixa`] must yield discriminant-equal values
16741        // (idempotent, no side effects on `&self`). Peer of the sibling
16742        // outer-`Caixa` `Option<&Composite>` by-borrow
16743        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
16744        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
16745        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
16746        // `placement_projects_option_ref_by_borrow` (4fb8074) /
16747        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
16748        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
16749        // extended here to the outer-`Caixa` `Option<Copy>`-return
16750        // flat-spread axis. The `Copy` discipline replaces the pointer-
16751        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
16752        // `Copy` discriminant is definitionally the same discriminant, so
16753        // the axis reduces to discriminant equality).
16754        //
16755        // Pins against a future silent detour that returned a fresh
16756        // `Option<&RestartStrategy>` (which would type-check but silently
16757        // introduce a borrow of `&self` past the call, collapsing the
16758        // load-bearing "no lifetime on the return type" `Copy` projection
16759        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
16760        // read side effect that flipped the outer discriminant on
16761        // successive calls, or an axis-remap projection that returned a
16762        // different variant than the field storage.
16763        use crate::supervisor::RestartStrategy;
16764        for estrategia in [
16765            Some(RestartStrategy::OneForOne),
16766            Some(RestartStrategy::OneForAll),
16767            Some(RestartStrategy::RestForOne),
16768            Some(RestartStrategy::SimpleOneForOne),
16769        ] {
16770            let c = caixa_with_estrategia(estrategia);
16771            let first = c.estrategia();
16772            let second = c.estrategia();
16773            assert_eq!(
16774                first, second,
16775                "Caixa::estrategia must be idempotent — two successive \
16776                 calls on the same &self must return the same \
16777                 Option<RestartStrategy>",
16778            );
16779            assert_eq!(
16780                first, estrategia,
16781                "Caixa::estrategia must return :estrategia verbatim by \
16782                 Copy — got {first:?}, expected {estrategia:?}",
16783            );
16784        }
16785        let c = caixa_with_estrategia(None);
16786        assert!(
16787            c.estrategia().is_none(),
16788            "Caixa::estrategia must return None when :estrategia is \
16789             absent — the author-omitted arm must project through the \
16790             accessor's Option::None unchanged",
16791        );
16792    }
16793
16794    // ── Caixa::max_restarts / Caixa::restart_window —
16795    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
16796    //    (Option<u32> / Option<&str>) folding on the ed04d3c
16797    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
16798
16799    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
16800        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16801        c.max_restarts = max_restarts;
16802        c
16803    }
16804
16805    fn caixa_supervisor_with_max_restarts_and_window(
16806        max_restarts: Option<u32>,
16807        restart_window: Option<&str>,
16808    ) -> Caixa {
16809        use crate::CaixaKind;
16810        use crate::supervisor::{ChildSpec, RestartPolicy};
16811        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
16812        c.kind = CaixaKind::Supervisor;
16813        c.max_restarts = max_restarts;
16814        c.restart_window = restart_window.map(str::to_string);
16815        c.children = vec![ChildSpec {
16816            caixa: "worker".into(),
16817            versao: "^0.1".into(),
16818            restart: RestartPolicy::Permanent,
16819        }];
16820        c
16821    }
16822
16823    #[test]
16824    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
16825        // Value-shape pin: [`Caixa::max_restarts`] returns the
16826        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
16827        // from the typed slot's own storage, byte-equal across the
16828        // author-omitted `None` arm (the "defer to the
16829        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
16830        // `{intensity, 5, 60}` default" partition every
16831        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
16832        // and each of the representative fixtures in the accept-set —
16833        // `0` (the zero-floor arm the peer
16834        // [`crate::supervisor::SupervisorSpec::validate`]
16835        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
16836        // the post-composition altitude — the accessor must ship the
16837        // raw slot verbatim so struct-literal fixtures continue to
16838        // expose the zero at the accessor boundary), the OTP-canonical
16839        // `5` default (`{intensity, 5, 60}` worker-supervisor from
16840        // Learn You Some Erlang), `1000` (the
16841        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
16842        // upper-bound gate accepts on the boundary), `u32::MAX` (a
16843        // past-the-cap sentinel that the substrate-primitive accessor
16844        // must still ship verbatim). Second outer top-level
16845        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
16846        // pin — folds on the sibling
16847        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
16848        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
16849        // onto the sibling `Option<u32>` restart-budget-count arm.
16850        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
16851        for max_restarts in fixtures {
16852            let c = caixa_with_max_restarts(max_restarts);
16853            assert_eq!(
16854                c.max_restarts(),
16855                max_restarts,
16856                "Caixa::max_restarts must return :max-restarts verbatim \
16857                 (got {:?}, expected {max_restarts:?})",
16858                c.max_restarts(),
16859            );
16860            assert_eq!(
16861                c.max_restarts(),
16862                c.max_restarts,
16863                "Caixa::max_restarts accessor and self.max_restarts \
16864                 field access must byte-equal — a presence-bit or count \
16865                 drift would silently split the paired \
16866                 Caixa::declared_supervisor_slots presence-probe arm \
16867                 from the Caixa::supervisor_view unwrap_or(5) fold's \
16868                 composition input",
16869            );
16870        }
16871    }
16872
16873    #[test]
16874    fn max_restarts_projects_option_by_copy() {
16875        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
16876        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
16877        // borrow `&self` past the call (no lifetime on the return type),
16878        // and calling the accessor twice on the same [`Caixa`] must
16879        // yield equal values (idempotent, no side effects). Peer of the
16880        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
16881        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
16882        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
16883            let c = caixa_with_max_restarts(max_restarts);
16884            let first = c.max_restarts();
16885            let second = c.max_restarts();
16886            assert_eq!(
16887                first, second,
16888                "Caixa::max_restarts must be idempotent — two successive \
16889                 calls on the same &self must return the same Option<u32>",
16890            );
16891            assert_eq!(
16892                first, max_restarts,
16893                "Caixa::max_restarts must return :max-restarts verbatim \
16894                 by Copy — got {first:?}, expected {max_restarts:?}",
16895            );
16896        }
16897    }
16898
16899    #[test]
16900    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
16901        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16902        // `:max-restarts` presence-probe arm must key off
16903        // [`Caixa::max_restarts`], not the raw
16904        // `self.max_restarts.is_some()` field-probe. Structurally: every
16905        // `Caixa { max_restarts: Some(_), .. }` variant must push
16906        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
16907        // list (the presence bit is `Some` for every representative
16908        // count, so the M2 kind-coherence gate must surface the slot as
16909        // "declared"), and a `Caixa { max_restarts: None, .. }` must
16910        // NOT push the label. Peer of the sibling
16911        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
16912        // (ed04d3c) composition pin — same routing-through-accessor
16913        // discipline extended onto the sibling flat-spread `Option<u32>`
16914        // arm.
16915        for max_restarts in [0u32, 5, 1000, u32::MAX] {
16916            let c = caixa_with_max_restarts(Some(max_restarts));
16917            let slots = c.declared_supervisor_slots();
16918            assert!(
16919                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
16920                "declared_supervisor_slots must push \
16921                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
16922                 is Some({max_restarts}) — the accessor and the \
16923                 enumerator gate must route through the same \
16924                 substrate-primitive typed dispatch on the outer \
16925                 :max-restarts presence bit (got slots={slots:?})",
16926            );
16927        }
16928        let c = caixa_with_max_restarts(None);
16929        let slots = c.declared_supervisor_slots();
16930        assert!(
16931            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
16932            "declared_supervisor_slots must NOT push \
16933             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
16934             None — the author-omitted arm must route through the \
16935             accessor's None-return unchanged (got slots={slots:?})",
16936        );
16937    }
16938
16939    #[test]
16940    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
16941        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
16942        // [`SupervisorSpec`] construction arm must key off
16943        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
16944        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
16945        // every `:kind Supervisor` `Caixa` carrying an author-declared
16946        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
16947        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
16948        // carrying `None`, the composed [`SupervisorSpec`]'s
16949        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
16950        // of the sibling
16951        // `supervisor_view_estrategia_arm_routes_through_accessor`
16952        // (ed04d3c) composition pin.
16953        for max_restarts in [1u32, 5, 1000] {
16954            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
16955            let view = c.supervisor_view().expect(
16956                "supervisor_view must materialize a SupervisorSpec for a \
16957                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
16958            );
16959            assert_eq!(
16960                view.max_restarts(),
16961                max_restarts,
16962                "supervisor_view must carry the outer \
16963                 Caixa::max_restarts() Some arm onto the composed \
16964                 SupervisorSpec.max_restarts field verbatim (got {}, \
16965                 expected {max_restarts})",
16966                view.max_restarts(),
16967            );
16968        }
16969        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
16970        let view = c.supervisor_view().expect(
16971            "supervisor_view must materialize a SupervisorSpec for a \
16972             :kind Supervisor Caixa carrying a None :max-restarts",
16973        );
16974        assert_eq!(
16975            view.max_restarts(),
16976            5,
16977            "supervisor_view must project the outer \
16978             Caixa::max_restarts() None arm onto the OTP-canonical \
16979             {{intensity, 5, 60}} default (5) through the flat-spread \
16980             unwrap_or(5) fold (got {})",
16981            view.max_restarts(),
16982        );
16983        assert!(
16984            c.max_restarts().is_none(),
16985            "Caixa::max_restarts() must remain None on the author-\
16986             omitted arm — the supervisor_view fold must not mutate \
16987             the outer flat-spread presence bit",
16988        );
16989    }
16990
16991    #[test]
16992    fn supervisor_view_estrategia_fallback_routes_through_lifted_default() {
16993        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
16994        // `:estrategia` arm must degrade onto the substrate-canonical
16995        // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
16996        // `pub const` — the Erlang/OTP-canonical `one_for_one` strategy
16997        // half of Learn You Some Erlang's `{one_for_one, intensity, 5, 60}`
16998        // worker-supervisor default — rather than the transitively-
16999        // derived [`crate::supervisor::RestartStrategy::default`] route
17000        // the prior `.unwrap_or_default()` fold reached for. Prior to the
17001        // lift the composition site carried `.unwrap_or_default()` with
17002        // no compile-time link back to the shared OTP-canonical strategy
17003        // default that the paired [`crate::supervisor::Default for
17004        // RestartStrategy`] impl and the [`crate::supervisor::Default for
17005        // SupervisorSpec`] impl's struct-literal `estrategia` field both
17006        // (now) route through the same lifted constant — so a future
17007        // rebrand of the OTP-canonical strategy default (an OTP
17008        // `rest_for_one` widening once the substrate discovers startup-
17009        // order-coupled child cohorts as the more common worker-
17010        // supervisor shape, a per-cluster overlay the operator pins
17011        // through the MESH-COMPOSITION §III.2 supervision-canary
17012        // `:estrategia-overrides` roadmap slot) would have had to migrate
17013        // the paired `MaxIntensity` + `Period` halves through the lifted
17014        // constants and the `one_for_one` half through a
17015        // `RestartStrategy::default()` route in lockstep or a
17016        // `:kind Supervisor` caixa carrying an author-omitted
17017        // `:estrategia` slot would silently resolve to a `SupervisorSpec`
17018        // whose `estrategia` disagreed with the paired
17019        // `SupervisorSpec::default()` view. Byte-parity against the
17020        // lifted constant closes the split. Peer of the sibling
17021        // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
17022        // composition pin on the paired `MaxIntensity` half + the
17023        // [`crate::supervisor::restart_strategy_default_routes_through_lifted_default`]
17024        // + [`crate::supervisor::supervisor_spec_default_estrategia_routes_through_lifted_default`]
17025        // pins on the sibling entry points onto the shared substrate
17026        // constant.
17027        use crate::CaixaKind;
17028        use crate::supervisor::{ChildSpec, RestartPolicy};
17029        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
17030        c.kind = CaixaKind::Supervisor;
17031        c.estrategia = None;
17032        c.children = vec![ChildSpec {
17033            caixa: "worker".into(),
17034            versao: "^0.1".into(),
17035            restart: RestartPolicy::Permanent,
17036        }];
17037        let view = c.supervisor_view().expect(
17038            "supervisor_view must materialize a SupervisorSpec for a \
17039             :kind Supervisor Caixa carrying a None :estrategia",
17040        );
17041        assert_eq!(
17042            view.estrategia(),
17043            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
17044            "supervisor_view must degrade the outer \
17045             Caixa::estrategia() None arm onto the lifted \
17046             SUPERVISOR_ESTRATEGIA_DEFAULT typed pub const (got {:?}, \
17047             expected {:?})",
17048            view.estrategia(),
17049            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
17050        );
17051    }
17052
17053    #[test]
17054    fn supervisor_view_max_restarts_fallback_routes_through_lifted_default() {
17055        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
17056        // `:max-restarts` arm must degrade onto the substrate-canonical
17057        // [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`] typed
17058        // `pub const` — the Erlang/OTP-canonical `{intensity, 5, 60}`
17059        // `MaxIntensity` default — rather than a raw `5` literal. Prior
17060        // to the lift the composition site carried an inline
17061        // `.unwrap_or(5)` with no compile-time link back to the shared
17062        // OTP-canonical default that the serde-side
17063        // `#[serde(default = "default_max_restarts")]` wire-format arm
17064        // and the [`Default for crate::supervisor::SupervisorSpec`]
17065        // struct-literal default arm both key off — so a future rebrand
17066        // of the OTP-canonical default (Elixir's `Supervisor` `3`
17067        // default, a per-cluster overlay the operator pins through the
17068        // MESH-COMPOSITION §III.2 supervision-canary
17069        // `:supervisor :max-restarts-overrides` roadmap slot) would
17070        // have had to be threaded through both the serde-side helper
17071        // and this view-construction arm in lockstep or a `:kind
17072        // Supervisor` caixa carrying `:max-restarts ()` would silently
17073        // resolve to a `SupervisorSpec` whose `max_restarts` disagreed
17074        // with the same fixture's serde-side `SupervisorSpec` view (an
17075        // author-omitted slot round-tripping through
17076        // `SupervisorSpec::default()` to the lifted constant, then
17077        // splitting to a stale literal past `supervisor_view`).
17078        // Byte-parity against the lifted constant closes the split.
17079        // Peer of the sibling
17080        // [`crate::supervisor::default_max_restarts_helper_routes_through_lifted_default`]
17081        // + [`crate::supervisor::supervisor_spec_default_max_restarts_routes_through_lifted_default`]
17082        // composition pins that close the same routing on the two
17083        // sibling entry points onto the shared substrate constant.
17084        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
17085        let view = c.supervisor_view().expect(
17086            "supervisor_view must materialize a SupervisorSpec for a \
17087             :kind Supervisor Caixa carrying a None :max-restarts",
17088        );
17089        assert_eq!(
17090            view.max_restarts(),
17091            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
17092            "supervisor_view must degrade the outer \
17093             Caixa::max_restarts() None arm onto the lifted \
17094             SUPERVISOR_MAX_RESTARTS_DEFAULT typed pub const (got {}, \
17095             expected {})",
17096            view.max_restarts(),
17097            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
17098        );
17099    }
17100
17101    #[test]
17102    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
17103        // Value-shape pin: [`Caixa::restart_window`] returns the
17104        // `:restart-window` typed `Option<String>` verbatim as an
17105        // `Option<&str>`, borrowed from the typed slot's own storage,
17106        // byte-equal across the author-omitted `None` arm and each of
17107        // the representative fixtures in the accept-set — the canonical
17108        // `"60s"` from `{intensity, 5, 60}`, the sibling
17109        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
17110        // / `"0s"`) the shared codec's positive-set sweep pin covers,
17111        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
17112        // seconds drift the sibling [`Self::validate_restart_window`]
17113        // gate refuses; the accessor must ship the raw slot verbatim
17114        // so struct-literal fixtures continue to expose the drift at
17115        // the accessor boundary). Third outer top-level [`Caixa`]
17116        // supervisor-tree flat-spread pin — extends the sub-family onto
17117        // the sibling `Option<&str>` raw-duration-string arm.
17118        for window in [
17119            None,
17120            Some("60s"),
17121            Some("5m"),
17122            Some("1h"),
17123            Some("500ms"),
17124            Some("1.5s"),
17125            Some(""),
17126        ] {
17127            let c = caixa_with_restart_window(window);
17128            assert_eq!(
17129                c.restart_window(),
17130                window,
17131                "Caixa::restart_window must return :restart-window \
17132                 verbatim as Option<&str> (got {:?}, expected {window:?})",
17133                c.restart_window(),
17134            );
17135            assert_eq!(
17136                c.restart_window(),
17137                c.restart_window.as_deref(),
17138                "Caixa::restart_window accessor and \
17139                 self.restart_window.as_deref() field access must \
17140                 byte-equal — a byte-level drift would silently split \
17141                 the paired Caixa::declared_supervisor_slots \
17142                 presence-probe arm from the \
17143                 Caixa::validate_restart_window shared-codec gate and \
17144                 the Caixa::supervisor_view soft-swallowing fold",
17145            );
17146        }
17147    }
17148
17149    #[test]
17150    fn restart_window_projects_slice_by_borrow() {
17151        // The by-borrow pin: [`Caixa::restart_window`] returns
17152        // `Option<&str>` by borrow — the returned string slice borrows
17153        // the underlying `Option<String>` storage of the `:restart-window`
17154        // slot and the accessor must not clone on every call. Peer of
17155        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
17156        // by-borrow pins on the universal-axis scalar family
17157        // (`licenca_projects_option_ref_by_borrow` /
17158        // `descricao_projects_option_ref_by_borrow` and siblings) —
17159        // extended onto the M2 supervisor-tree flat-spread
17160        // `Option<&str>` raw-duration-string axis.
17161        for window in [None, Some("60s"), Some("5m"), Some("")] {
17162            let c = caixa_with_restart_window(window);
17163            let first = c.restart_window();
17164            let second = c.restart_window();
17165            assert_eq!(
17166                first, second,
17167                "Caixa::restart_window must be idempotent — two \
17168                 successive calls on the same &self must return the \
17169                 same Option<&str>",
17170            );
17171            if let (Some(a), Some(b)) = (first, second) {
17172                assert_eq!(
17173                    a.as_ptr(),
17174                    b.as_ptr(),
17175                    "Caixa::restart_window must borrow the underlying \
17176                     String storage — two successive Some-arm calls must \
17177                     return slices with the same backing pointer (a fresh \
17178                     String clone would change the pointer on every call)",
17179                );
17180            }
17181            assert_eq!(
17182                first, window,
17183                "Caixa::restart_window must return :restart-window \
17184                 verbatim by borrow — got {first:?}, expected {window:?}",
17185            );
17186        }
17187    }
17188
17189    #[test]
17190    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
17191        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17192        // `:restart-window` presence-probe arm must key off
17193        // [`Caixa::restart_window`], not the raw
17194        // `self.restart_window.is_some()` field-probe. Structurally:
17195        // every `Caixa { restart_window: Some(_), .. }` must push
17196        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
17197        // list, and a `Caixa { restart_window: None, .. }` must NOT
17198        // push the label. Peer of the sibling
17199        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
17200        // routing pin.
17201        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
17202            let c = caixa_with_restart_window(Some(window));
17203            let slots = c.declared_supervisor_slots();
17204            assert!(
17205                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
17206                "declared_supervisor_slots must push \
17207                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
17208                 `:restart-window` is Some({window:?}) — the accessor \
17209                 and the enumerator gate must route through the same \
17210                 substrate-primitive typed dispatch on the outer \
17211                 :restart-window presence bit (got slots={slots:?})",
17212            );
17213        }
17214        let c = caixa_with_restart_window(None);
17215        let slots = c.declared_supervisor_slots();
17216        assert!(
17217            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
17218            "declared_supervisor_slots must NOT push \
17219             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
17220             is None — the author-omitted arm must route through the \
17221             accessor's None-return unchanged (got slots={slots:?})",
17222        );
17223    }
17224
17225    #[test]
17226    fn validate_restart_window_arm_routes_through_accessor() {
17227        // Composition pin: [`Caixa::validate_restart_window`]'s
17228        // shared-codec fold arm must key off [`Caixa::restart_window`],
17229        // not the raw `self.restart_window.as_deref()` field-projection.
17230        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
17231        // express no reset" canonical shape); (2) a canonical `Some`
17232        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
17233        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
17234        // .. })` carrying the offending raw string verbatim. The three
17235        // arms jointly pin that the validator's raw-string binding is
17236        // the accessor's return, not a peer projection — any future
17237        // silent detour that had the accessor collapse `Some("")` to
17238        // `None` would silently absorb the empty-after-trim refusal
17239        // case at the accessor boundary.
17240        caixa_with_restart_window(None)
17241            .validate_restart_window()
17242            .expect("None :restart-window must validate through the accessor");
17243        caixa_with_restart_window(Some("60s"))
17244            .validate_restart_window()
17245            .expect("canonical :restart-window \"60s\" must validate through the accessor");
17246        let err = caixa_with_restart_window(Some("1.5s"))
17247            .validate_restart_window()
17248            .expect_err("fractional-seconds :restart-window must fail through the accessor");
17249        assert!(
17250            matches!(
17251                err,
17252                ManifestError::RestartWindowMalformed { ref restart_window, .. }
17253                    if restart_window == "1.5s"
17254            ),
17255            "validator must carry the offending raw string verbatim \
17256             from the accessor's borrowed &str (got {err:?})",
17257        );
17258    }
17259
17260    #[test]
17261    fn supervisor_view_restart_window_arm_routes_through_accessor() {
17262        // Composition pin: [`Caixa::supervisor_view`]'s
17263        // per-`:restart-window` [`SupervisorSpec`] construction arm
17264        // must key off [`Caixa::restart_window`]'s soft-swallowing
17265        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
17266        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
17267        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
17268        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
17269        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
17270        // (the shared codec's canonical parse); (3) codec-rejected
17271        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
17272        // (the soft-swallow preserving the view's best-effort shape).
17273        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
17274        let view = c.supervisor_view().expect("Supervisor kind has a view");
17275        assert_eq!(
17276            view.restart_window(),
17277            None,
17278            "supervisor_view must project outer None :restart-window \
17279             onto None on the composed SupervisorSpec (never-reset \
17280             sentinel) through the accessor's None-return unchanged",
17281        );
17282
17283        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
17284        let view = c.supervisor_view().expect("Supervisor kind has a view");
17285        assert_eq!(
17286            view.restart_window(),
17287            Some(std::time::Duration::from_secs(60)),
17288            "supervisor_view must fold outer Some(\"60s\") through the \
17289             shared duration_codec into Duration::from_secs(60) on the \
17290             composed SupervisorSpec (accessor's Some(&str) → codec \
17291             parse → Some(Duration))",
17292        );
17293
17294        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
17295        let view = c.supervisor_view().expect("Supervisor kind has a view");
17296        assert_eq!(
17297            view.restart_window(),
17298            None,
17299            "supervisor_view must soft-swallow the shared-codec parse \
17300             failure to None (the view's best-effort shape the sibling \
17301             manifest-level validate_restart_window surfaces as \
17302             RestartWindowMalformed); the accessor's raw-string return \
17303             is the single input every downstream consumer keys off",
17304        );
17305    }
17306
17307    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
17308
17309    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
17310        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17311        c.upgrade_from = upgrade_from;
17312        c
17313    }
17314
17315    #[test]
17316    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
17317        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
17318        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
17319        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
17320        // typed `Vec<UpgradeFromEntry>` verbatim as a
17321        // `&[UpgradeFromEntry]` slice-view over the same backing
17322        // buffer the raw `self.upgrade_from.as_slice()` field access
17323        // borrows from, element-equal across every representative
17324        // fixture in the accept-set — `[]` (the "no hot-upgrade path
17325        // declared" arm every `defcaixa` without an `:upgrade-from`
17326        // block carries; `#[serde(default)]` folds an omitted slot
17327        // onto `Vec::new()`), a canonical single-entry `Restart`
17328        // fixture (the shape most Servicos carry — a single prior
17329        // version with the fallback strategy), a canonical multi-
17330        // entry list carrying every typed instruction variant
17331        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
17332        // `Restart`), and a past-the-guard sentinel — a duplicate-
17333        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
17334        // ([`crate::upgrade::validate_upgrade_from`] rejects through
17335        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
17336        // ship the raw slot verbatim so struct-literal fixtures
17337        // continue to expose the duplicate at the accessor boundary).
17338        //
17339        // Pins against a future silent detour that returned an owned
17340        // `Vec<UpgradeFromEntry>` (which would type-check but silently
17341        // clone on every accessor call, breaking the zero-cost
17342        // projection every peer sibling slice accessor carries), a
17343        // `[dup, dup] → [dup]` dedup collapse (which would silently
17344        // absorb the `DuplicateFrom` refusal case at the accessor
17345        // boundary and the [`crate::StandardLayout::verify`] cross-
17346        // entry gate would silently accept a struct-literal `Caixa`
17347        // carrying the drift), a reference to an operator-resolved
17348        // overlay (the future per-cluster `:upgrade-overrides` slot
17349        // — its resolution must land at exactly this accessor body,
17350        // not silently divert the raw slot away from a second
17351        // consumer), or an axis-shuffled projection (a future detour
17352        // that reordered entries through the accessor would silently
17353        // split the paired [`crate::StandardLayout::verify`] per-
17354        // `:upgrade-from` shape gate's traversal input from the peer
17355        // [`crate::render::servico_m2_overlay`] emitter's projection
17356        // input, since the operator's hot-upgrade dispatch matches
17357        // per-`:from` and axis reordering would silently split the
17358        // per-entry script-path existence probe's iteration order
17359        // from the M2 overlay emitter's serialized-entry order).
17360        //
17361        // First outer top-level [`Caixa`] `&[Composite]`-return
17362        // slice accessor pin on the substrate primitive for M2 / M3
17363        // typed-slot vec-carry axes — opens the outer-`Caixa`
17364        // `&[Composite]` composite-slice projection pattern the
17365        // sibling `:children` [`crate::supervisor::ChildSpec`] /
17366        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
17367        // [`crate::aplicacao::WitContract`] future outer-composite-
17368        // slice pins fold on. Peer of the closed outer-`Caixa`
17369        // scalar `Option<&Composite>` composite-reference family the
17370        // sibling `limits` / `behavior` / `politicas` / `placement`
17371        // / `entrada` `..._returns_..._option_ref_verbatim_across_
17372        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
17373        // the "byte-equal, borrow-shared" outer-accessor discipline
17374        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
17375        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17376        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
17377            vec![],
17378            vec![UpgradeFromEntry {
17379                from: "0.0.1".into(),
17380                instructions: vec![UpgradeInstruction::Restart],
17381            }],
17382            vec![
17383                UpgradeFromEntry {
17384                    from: "0.0.1".into(),
17385                    instructions: vec![
17386                        UpgradeInstruction::LoadModule {
17387                            module: "demo".into(),
17388                        },
17389                        UpgradeInstruction::SoftPurge {
17390                            module: "demo".into(),
17391                        },
17392                    ],
17393                },
17394                UpgradeFromEntry {
17395                    from: "0.0.2".into(),
17396                    instructions: vec![
17397                        UpgradeInstruction::StateChange {
17398                            script: "servicos/upgrade.lisp".into(),
17399                        },
17400                        UpgradeInstruction::Purge {
17401                            module: "demo".into(),
17402                        },
17403                        UpgradeInstruction::Restart,
17404                    ],
17405                },
17406            ],
17407            vec![
17408                UpgradeFromEntry {
17409                    from: "0.1.0".into(),
17410                    instructions: vec![UpgradeInstruction::Restart],
17411                },
17412                UpgradeFromEntry {
17413                    from: "0.1.0".into(),
17414                    instructions: vec![UpgradeInstruction::Restart],
17415                },
17416            ],
17417        ];
17418        for upgrade_from in fixtures {
17419            let c = caixa_with_upgrade_from(upgrade_from.clone());
17420            assert_eq!(
17421                c.upgrade_from(),
17422                upgrade_from.as_slice(),
17423                "Caixa::upgrade_from must return :upgrade-from \
17424                 verbatim (got {:?}, expected {upgrade_from:?})",
17425                c.upgrade_from(),
17426            );
17427            assert_eq!(
17428                c.upgrade_from(),
17429                c.upgrade_from.as_slice(),
17430                "Caixa::upgrade_from must element-equal the raw \
17431                 `self.upgrade_from.as_slice()` field access across \
17432                 every value in the Vec<UpgradeFromEntry> accept-set",
17433            );
17434            assert_eq!(
17435                c.upgrade_from().is_empty(),
17436                c.upgrade_from.is_empty(),
17437                "Caixa::upgrade_from().is_empty() must byte-equal \
17438                 self.upgrade_from.is_empty() — a presence-bit drift \
17439                 would silently split the paired \
17440                 Caixa::declared_servico_slots M2 declared-slot \
17441                 enumerator's presence probe from the peer \
17442                 crate::render::servico_m2_overlay M2 overlay \
17443                 emitter's presence gate",
17444            );
17445        }
17446    }
17447
17448    #[test]
17449    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
17450        // Composition pin: [`Caixa::declared_servico_slots`]'s
17451        // `:upgrade-from` presence-probe arm must key off
17452        // [`Caixa::upgrade_from`], not the raw
17453        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
17454        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
17455        // instructions: vec![Restart] }], .. }` must push
17456        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
17457        // (the presence bit is non-empty, so the M2 kind-coherence
17458        // gate must surface the slot as "declared"), and a `Caixa {
17459        // upgrade_from: vec![], .. }` must NOT push the label (the
17460        // "author omitted the slot entirely" arm — the empty-slice
17461        // partition the serde-default folds onto). The pair jointly
17462        // pins the accessor + declared-slot enumerator composition:
17463        // any future silent detour that had the accessor collapse
17464        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
17465        // is_empty())` projection) would silently absorb the
17466        // "declared but degenerate" arm at the accessor boundary and
17467        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
17468        // coherence gate would silently accept a struct-literal
17469        // `Caixa` carrying the drift.
17470        //
17471        // Peer of the sibling
17472        // `declared_servico_slots_limits_arm_routes_through_accessor`
17473        // (b2bd9d7) and
17474        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17475        // (35d8b52) composition pins on the sibling `:limits` /
17476        // `:behavior` outer-`Option<&Composite>` arms — same "the
17477        // enumerator gate must route through the substrate-primitive
17478        // typed dispatch" discipline extended onto the third M2
17479        // Servico-runtime slot axis, closing the enumerator's routing
17480        // invariant on every M2 arm.
17481        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17482        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
17483            from: "0.0.1".into(),
17484            instructions: vec![UpgradeInstruction::Restart],
17485        }]);
17486        let slots = c.declared_servico_slots();
17487        assert!(
17488            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
17489            "declared_servico_slots must push \
17490             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
17491             non-empty — the accessor and the enumerator gate must \
17492             route through the same substrate-primitive typed \
17493             dispatch on the outer :upgrade-from presence bit (got \
17494             slots={slots:?})",
17495        );
17496        let c = caixa_with_upgrade_from(vec![]);
17497        let slots = c.declared_servico_slots();
17498        assert!(
17499            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
17500            "declared_servico_slots must NOT push \
17501             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
17502             empty — the author-omitted arm must route through the \
17503             accessor's empty-slice return unchanged (got \
17504             slots={slots:?})",
17505        );
17506    }
17507
17508    #[test]
17509    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
17510        // Composition pin: [`crate::render::servico_m2_overlay`]'s
17511        // per-`:upgrade-from` M2 overlay emit arm must key off
17512        // [`Caixa::upgrade_from`], not the raw
17513        // `!caixa.upgrade_from.is_empty()` presence gate + the
17514        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
17515        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
17516        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
17517        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
17518        // sequence in the overlay (the emitter fans onto the serde
17519        // slice-serialization), and a `Caixa { upgrade_from: vec![],
17520        // .. }` must omit the key entirely (the empty-slice
17521        // partition — the `!.is_empty()` outer gate elides the key
17522        // when the author omitted the slot). The pair jointly pins
17523        // the accessor + M2 overlay emitter composition: any future
17524        // silent detour that had the accessor return a fresh-cloned
17525        // `Vec<UpgradeFromEntry>` copy would silently break the
17526        // reference-identity pin the peer per-entry
17527        // `serde_yaml::to_value(caixa.upgrade_from())` projection
17528        // reads from — the projection would clone once per accessor
17529        // call instead of borrowing the storage buffer verbatim.
17530        //
17531        // Peer of the sibling
17532        // `servico_m2_overlay_limits_arm_routes_through_accessor`
17533        // (b2bd9d7) and
17534        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
17535        // (35d8b52) composition pins on the sibling `:limits` /
17536        // `:behavior` outer-`Option<&Composite>` arms — same "the
17537        // M2 overlay emitter must route through the substrate-
17538        // primitive typed dispatch" discipline extended onto the
17539        // third M2 Servico-runtime slot axis, closing the overlay
17540        // emitter's routing invariant on every M2 arm.
17541        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
17542        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17543        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
17544            from: "0.0.1".into(),
17545            instructions: vec![UpgradeInstruction::Restart],
17546        }]);
17547        let overlay = servico_m2_overlay(&c).unwrap();
17548        assert!(
17549            overlay.contains_key(M2_KEY_UPGRADE_FROM),
17550            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
17551             `:upgrade-from` is non-empty — the accessor and the M2 \
17552             overlay emitter must route through the same substrate- \
17553             primitive typed dispatch on the outer :upgrade-from \
17554             slice (got overlay={overlay:?})",
17555        );
17556        let c = caixa_with_upgrade_from(vec![]);
17557        let overlay = servico_m2_overlay(&c).unwrap();
17558        assert!(
17559            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
17560            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
17561             `:upgrade-from` is empty — the empty-slice partition \
17562             must route through the accessor's empty-slice return \
17563             unchanged (got overlay={overlay:?})",
17564        );
17565    }
17566
17567    #[test]
17568    fn upgrade_from_projects_slice_by_borrow() {
17569        // The by-borrow pin: [`Caixa::upgrade_from`] returns
17570        // `&[UpgradeFromEntry]` by borrow — the returned slice
17571        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
17572        // the `:upgrade-from` slot and the accessor must not clone
17573        // the backing `Vec` on every call. Peer of the sibling
17574        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
17575        // (`autores_projects_slice_by_borrow` b5d813f,
17576        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
17577        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17578        // `exe_projects_slice_by_borrow` 65d9527,
17579        // `servicos_projects_slice_by_borrow` 611f78b,
17580        // `deps_projects_slice_by_borrow` ad34b4e,
17581        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
17582        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
17583        // axes — extended here to the first outer-`Caixa`
17584        // composite-element `&[Composite]` axis: the accessor's
17585        // returned slice must borrow from `&self` (the returned
17586        // reference's lifetime is tied to `&self`), and calling the
17587        // accessor twice on the same [`Caixa`] must yield slices
17588        // that are pointer-equal (the underlying byte-buffer is the
17589        // storage `Vec`'s allocation, not a fresh copy) as well as
17590        // value-equal (idempotent, no side effects on `&self`).
17591        //
17592        // Pins against a future silent detour that returned an owned
17593        // `Vec<UpgradeFromEntry>` (which would type-check but
17594        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
17595        // return (which would leak the backing `Vec`'s
17596        // grow/push/reserve surface no downstream consumer reaches
17597        // for), or a one-arm-only accessor that returned a
17598        // saturating value on some sentinel input.
17599        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17600        for upgrade_from in [
17601            vec![],
17602            vec![UpgradeFromEntry {
17603                from: "0.0.1".into(),
17604                instructions: vec![UpgradeInstruction::Restart],
17605            }],
17606            vec![
17607                UpgradeFromEntry {
17608                    from: "0.0.1".into(),
17609                    instructions: vec![UpgradeInstruction::Restart],
17610                },
17611                UpgradeFromEntry {
17612                    from: "0.0.2".into(),
17613                    instructions: vec![UpgradeInstruction::SoftPurge {
17614                        module: "demo".into(),
17615                    }],
17616                },
17617            ],
17618        ] {
17619            let c = caixa_with_upgrade_from(upgrade_from.clone());
17620            let first = c.upgrade_from();
17621            let second = c.upgrade_from();
17622            assert_eq!(
17623                first, second,
17624                "Caixa::upgrade_from must be idempotent — two \
17625                 successive calls on the same &self must return the \
17626                 same &[UpgradeFromEntry]",
17627            );
17628            assert_eq!(
17629                first.as_ptr(),
17630                second.as_ptr(),
17631                "Caixa::upgrade_from must borrow the underlying \
17632                 Vec<UpgradeFromEntry> storage — two successive calls \
17633                 must return slices with the same backing pointer (a \
17634                 fresh Vec<UpgradeFromEntry> clone would change the \
17635                 pointer on every call)",
17636            );
17637            assert_eq!(
17638                first,
17639                upgrade_from.as_slice(),
17640                "Caixa::upgrade_from must return :upgrade-from \
17641                 verbatim by borrow — got {first:?}, expected \
17642                 {upgrade_from:?}",
17643            );
17644        }
17645    }
17646
17647    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
17648
17649    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
17650        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17651        c.children = children;
17652        c
17653    }
17654
17655    #[test]
17656    fn children_returns_children_slice_verbatim_across_permutations() {
17657        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
17658        // outer-composite `&[ChildSpec]`-return slice-shape pin:
17659        // [`Caixa::children`] must return the `:children` typed
17660        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
17661        // the same backing buffer the raw `self.children.as_slice()`
17662        // field access borrows from, element-equal across every
17663        // representative fixture in the accept-set — `[]` (the "no
17664        // static children declared" arm every non-`Supervisor`-kind
17665        // `defcaixa` carries by `#[serde(default)]` and every
17666        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
17667        // a canonical single-child `Permanent` fixture (the shape
17668        // most `OneForOne` supervisors carry — a single long-running
17669        // worker child), a canonical multi-child list carrying every
17670        // typed restart-policy variant (`Permanent` / `Transient` /
17671        // `Temporary`), and a past-the-guard sentinel — a duplicate
17672        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
17673        // ([`crate::SupervisorSpec::validate`] rejects through
17674        // `DuplicateChildNome { nome: "w" }` but the accessor must
17675        // ship the raw slot verbatim so struct-literal fixtures
17676        // continue to expose the duplicate at the accessor boundary).
17677        //
17678        // Pins against a future silent detour that returned an owned
17679        // `Vec<ChildSpec>` (which would type-check but silently clone
17680        // on every accessor call, breaking the zero-cost projection
17681        // every peer sibling slice accessor carries), a `[dup, dup] →
17682        // [dup]` dedup collapse (which would silently absorb the
17683        // `DuplicateChildNome` refusal case at the accessor boundary
17684        // and the [`crate::StandardLayout::verify`] cross-child gate
17685        // would silently accept a struct-literal `Caixa` carrying the
17686        // drift), a reference to an operator-resolved overlay (the
17687        // future per-cluster `:children-overrides` slot — its
17688        // resolution must land at exactly this accessor body, not
17689        // silently divert the raw slot away from a second consumer),
17690        // or an axis-shuffled projection (a future detour that
17691        // reordered children through the accessor would silently
17692        // split the paired [`crate::StandardLayout::verify`] per-
17693        // supervisor gate's traversal input from the peer
17694        // [`Self::supervisor_view`] fold-in path's clone-order input,
17695        // since the OTP `RestForOne` restart strategy dispatches on
17696        // declared child order and axis reordering would silently
17697        // split the operator's per-cluster restart-fan-out order
17698        // from the caixa.lisp source-order).
17699        //
17700        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
17701        // accessor pin on the substrate primitive for M2 / M3 typed-
17702        // slot vec-carry axes — folds on the outer-`Caixa`
17703        // `&[Composite]` composite-slice sub-family the sibling
17704        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
17705        // (2a1f907) pin opened, peer at the outer altitude of the
17706        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
17707        // (bc92bce) accessor on the same OTP-supervisor static-child-
17708        // list axis.
17709        use crate::supervisor::{ChildSpec, RestartPolicy};
17710        let fixtures: Vec<Vec<ChildSpec>> = vec![
17711            vec![],
17712            vec![ChildSpec {
17713                caixa: "worker".into(),
17714                versao: "^0.1".into(),
17715                restart: RestartPolicy::Permanent,
17716            }],
17717            vec![
17718                ChildSpec {
17719                    caixa: "worker-a".into(),
17720                    versao: "^0.1".into(),
17721                    restart: RestartPolicy::Permanent,
17722                },
17723                ChildSpec {
17724                    caixa: "worker-b".into(),
17725                    versao: "^0.1".into(),
17726                    restart: RestartPolicy::Transient,
17727                },
17728                ChildSpec {
17729                    caixa: "worker-c".into(),
17730                    versao: "^0.1".into(),
17731                    restart: RestartPolicy::Temporary,
17732                },
17733            ],
17734            vec![
17735                ChildSpec {
17736                    caixa: "w".into(),
17737                    versao: "^0.1".into(),
17738                    restart: RestartPolicy::Permanent,
17739                },
17740                ChildSpec {
17741                    caixa: "w".into(),
17742                    versao: "^0.1".into(),
17743                    restart: RestartPolicy::Permanent,
17744                },
17745            ],
17746        ];
17747        for children in fixtures {
17748            let c = caixa_with_children(children.clone());
17749            assert_eq!(
17750                c.children(),
17751                children.as_slice(),
17752                "Caixa::children must return :children verbatim \
17753                 (got {:?}, expected {children:?})",
17754                c.children(),
17755            );
17756            assert_eq!(
17757                c.children(),
17758                c.children.as_slice(),
17759                "Caixa::children must element-equal the raw \
17760                 `self.children.as_slice()` field access across \
17761                 every value in the Vec<ChildSpec> accept-set",
17762            );
17763            assert_eq!(
17764                c.children().is_empty(),
17765                c.children.is_empty(),
17766                "Caixa::children().is_empty() must byte-equal \
17767                 self.children.is_empty() — a presence-bit drift \
17768                 would silently split the paired \
17769                 Caixa::declared_supervisor_slots supervisor-tree \
17770                 declared-slot enumerator's presence probe from the \
17771                 peer Caixa::supervisor_view typed-view composer's \
17772                 fold-in path",
17773            );
17774        }
17775    }
17776
17777    #[test]
17778    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
17779        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17780        // `:children` presence-probe arm must key off
17781        // [`Caixa::children`], not the raw
17782        // `!self.children.is_empty()` field-probe. Structurally: a
17783        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
17784        // "^0.1", restart: Permanent }], .. }` must push
17785        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
17786        // (the presence bit is non-empty, so the supervisor-tree
17787        // kind-coherence gate must surface the slot as "declared"),
17788        // and a `Caixa { children: vec![], .. }` must NOT push the
17789        // label (the "author omitted the slot entirely" arm — the
17790        // empty-slice partition the serde-default folds onto). The
17791        // pair jointly pins the accessor + declared-slot enumerator
17792        // composition: any future silent detour that had the accessor
17793        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
17794        // "__reserved__")` projection) would silently absorb the
17795        // "declared but degenerate" arm at the accessor boundary and
17796        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
17797        // kind-coherence gate would silently accept a struct-literal
17798        // `Caixa` carrying the drift.
17799        //
17800        // Peer of the sibling
17801        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
17802        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
17803        // same "the enumerator gate must route through the substrate-
17804        // primitive typed dispatch" discipline extended onto the
17805        // supervisor-tree `:children` composite-slice arm.
17806        use crate::supervisor::{ChildSpec, RestartPolicy};
17807        let c = caixa_with_children(vec![ChildSpec {
17808            caixa: "w".into(),
17809            versao: "^0.1".into(),
17810            restart: RestartPolicy::Permanent,
17811        }]);
17812        let slots = c.declared_supervisor_slots();
17813        assert!(
17814            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
17815            "declared_supervisor_slots must push \
17816             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
17817             non-empty — the accessor and the enumerator gate must \
17818             route through the same substrate-primitive typed \
17819             dispatch on the outer :children presence bit (got \
17820             slots={slots:?})",
17821        );
17822        let c = caixa_with_children(vec![]);
17823        let slots = c.declared_supervisor_slots();
17824        assert!(
17825            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
17826            "declared_supervisor_slots must NOT push \
17827             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
17828             empty — the author-omitted arm must route through the \
17829             accessor's empty-slice return unchanged (got \
17830             slots={slots:?})",
17831        );
17832    }
17833
17834    #[test]
17835    fn supervisor_view_children_arm_routes_through_accessor() {
17836        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
17837        // fold-in arm must key off [`Caixa::children`], not the raw
17838        // `self.children.clone()` field-clone. Structurally: a `Caixa {
17839        // kind: Supervisor, estrategia: Some(OneForOne), children:
17840        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
17841        // per-child list through the accessor into the typed
17842        // [`SupervisorSpec`] view's `children` field verbatim — every
17843        // entry the accessor surfaces must land in the view's
17844        // `children` slot in the same order. The pair jointly pins the
17845        // accessor + view-composer composition: any future silent
17846        // detour that had the accessor return a fresh-cloned
17847        // `Vec<ChildSpec>` copy would silently break the reference-
17848        // identity pin the peer `supervisor_view` fold-in path reads
17849        // from — the fold would clone once more per accessor call
17850        // instead of borrowing the storage buffer verbatim once.
17851        //
17852        // Peer of the sibling
17853        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
17854        // family) composition pin on the peer kind-gate arm — same
17855        // "the view composer must route through the substrate-
17856        // primitive typed dispatch" discipline extended onto the
17857        // per-`:children` fold-in arm, closing the supervisor-view
17858        // composer's routing invariant on the composite-slice input.
17859        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17860        let mut c = caixa_with_children(vec![
17861            ChildSpec {
17862                caixa: "worker-a".into(),
17863                versao: "^0.1".into(),
17864                restart: RestartPolicy::Permanent,
17865            },
17866            ChildSpec {
17867                caixa: "worker-b".into(),
17868                versao: "^0.1".into(),
17869                restart: RestartPolicy::Transient,
17870            },
17871        ]);
17872        c.kind = crate::CaixaKind::Supervisor;
17873        c.estrategia = Some(RestartStrategy::OneForOne);
17874        let view = c
17875            .supervisor_view()
17876            .expect("Supervisor kind must produce a supervisor_view");
17877        assert_eq!(
17878            view.children(),
17879            c.children(),
17880            "supervisor_view must fold Caixa::children verbatim into \
17881             SupervisorSpec::children — the accessor and the view \
17882             composer must route through the same substrate-primitive \
17883             typed dispatch on the outer :children slice (got view \
17884             children={:?}, expected {:?})",
17885            view.children(),
17886            c.children(),
17887        );
17888    }
17889
17890    #[test]
17891    fn children_projects_slice_by_borrow() {
17892        // The by-borrow pin: [`Caixa::children`] returns
17893        // `&[ChildSpec]` by borrow — the returned slice borrows the
17894        // underlying `Vec<ChildSpec>` storage of the `:children` slot
17895        // and the accessor must not clone the backing `Vec` on every
17896        // call. Peer of the sibling outer top-level [`Caixa`]
17897        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
17898        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
17899        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17900        // `exe_projects_slice_by_borrow` 65d9527,
17901        // `servicos_projects_slice_by_borrow` 611f78b,
17902        // `deps_projects_slice_by_borrow` ad34b4e,
17903        // `deps_dev_projects_slice_by_borrow` f7fd81e,
17904        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
17905        // sibling outer top-level [`Caixa`] scalar-element and
17906        // composite-element `&[T]` axes — folds on the outer-`Caixa`
17907        // composite-element `&[Composite]` axis: the accessor's
17908        // returned slice must borrow from `&self` (the returned
17909        // reference's lifetime is tied to `&self`), and calling the
17910        // accessor twice on the same [`Caixa`] must yield slices
17911        // that are pointer-equal (the underlying byte-buffer is the
17912        // storage `Vec`'s allocation, not a fresh copy) as well as
17913        // value-equal (idempotent, no side effects on `&self`).
17914        //
17915        // Pins against a future silent detour that returned an owned
17916        // `Vec<ChildSpec>` (which would type-check but silently clone
17917        // on every call), a `&Vec<ChildSpec>` return (which would leak
17918        // the backing `Vec`'s grow/push/reserve surface no downstream
17919        // consumer reaches for), or a one-arm-only accessor that
17920        // returned a saturating value on some sentinel input.
17921        use crate::supervisor::{ChildSpec, RestartPolicy};
17922        for children in [
17923            vec![],
17924            vec![ChildSpec {
17925                caixa: "w".into(),
17926                versao: "^0.1".into(),
17927                restart: RestartPolicy::Permanent,
17928            }],
17929            vec![
17930                ChildSpec {
17931                    caixa: "worker-a".into(),
17932                    versao: "^0.1".into(),
17933                    restart: RestartPolicy::Permanent,
17934                },
17935                ChildSpec {
17936                    caixa: "worker-b".into(),
17937                    versao: "^0.1".into(),
17938                    restart: RestartPolicy::Transient,
17939                },
17940            ],
17941        ] {
17942            let c = caixa_with_children(children.clone());
17943            let first = c.children();
17944            let second = c.children();
17945            assert_eq!(
17946                first, second,
17947                "Caixa::children must be idempotent — two successive \
17948                 calls on the same &self must return the same \
17949                 &[ChildSpec]",
17950            );
17951            assert_eq!(
17952                first.as_ptr(),
17953                second.as_ptr(),
17954                "Caixa::children must borrow the underlying \
17955                 Vec<ChildSpec> storage — two successive calls must \
17956                 return slices with the same backing pointer (a fresh \
17957                 Vec<ChildSpec> clone would change the pointer on \
17958                 every call)",
17959            );
17960            assert_eq!(
17961                first,
17962                children.as_slice(),
17963                "Caixa::children must return :children verbatim by \
17964                 borrow — got {first:?}, expected {children:?}",
17965            );
17966        }
17967    }
17968
17969    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
17970
17971    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
17972        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17973        c.kind = CaixaKind::Aplicacao;
17974        c.membros = membros;
17975        c
17976    }
17977
17978    #[test]
17979    fn membros_returns_membros_slice_verbatim_across_permutations() {
17980        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
17981        // composite `&[Membro]`-return slice-shape pin:
17982        // [`Caixa::membros`] must return the `:membros` typed
17983        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
17984        // same backing buffer the raw `self.membros.as_slice()` field
17985        // access borrows from, element-equal across every
17986        // representative fixture in the accept-set — `[]` (the "no
17987        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
17988        // carries by `#[serde(default)]` and every partially-authored
17989        // Aplicacao carries before the
17990        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
17991        // canonical single-member fixture (the shape a minimal
17992        // Aplicacao carries — one Servico wrapping one contained
17993        // computation), a canonical multi-member list carrying three
17994        // distinct entries (the canonical checkout-shape Aplicacao —
17995        // cart / pricing / auth — every canonical example carries), and
17996        // a past-the-guard sentinel — a duplicate `:caixa`
17997        // `[("cart", ...), ("cart", ...)]` entry pair
17998        // ([`crate::AplicacaoSpec::validate`] rejects through
17999        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
18000        // the raw slot verbatim so struct-literal fixtures continue to
18001        // expose the duplicate at the accessor boundary).
18002        //
18003        // Pins against a future silent detour that returned an owned
18004        // `Vec<Membro>` (which would type-check but silently clone on
18005        // every accessor call, breaking the zero-cost projection every
18006        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
18007        // dedup collapse (which would silently absorb the
18008        // `DuplicateMembro` refusal case at the accessor boundary and
18009        // the [`crate::StandardLayout::verify`] cross-member gate would
18010        // silently accept a struct-literal `Caixa` carrying the drift),
18011        // a reference to an operator-resolved overlay (the future per-
18012        // cluster `:membros-overrides` slot — its resolution must land
18013        // at exactly this accessor body, not silently divert the raw
18014        // slot away from a second consumer), or an axis-shuffled
18015        // projection (a future detour that reordered members through
18016        // the accessor would silently split the paired
18017        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
18018        // traversal input from the peer [`Self::aplicacao_view`] fold-
18019        // in path's clone-order input, since the canonical `:contratos`
18020        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
18021        // read the member set through the same slice).
18022        //
18023        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
18024        // accessor pin on the substrate primitive for M2 / M3 typed-
18025        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
18026        // arm of the `&[Composite]` composite-slice sub-family the
18027        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18028        // (2a1f907) and
18029        // `children_returns_children_slice_verbatim_across_permutations`
18030        // (c17b51e) pins opened, peer at the outer altitude of the
18031        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
18032        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
18033        // list axis.
18034        use crate::aplicacao::Membro;
18035        let fixtures: Vec<Vec<Membro>> = vec![
18036            vec![],
18037            vec![Membro {
18038                caixa: "cart".into(),
18039                versao: "^0.1".into(),
18040            }],
18041            vec![
18042                Membro {
18043                    caixa: "cart".into(),
18044                    versao: "^0.1".into(),
18045                },
18046                Membro {
18047                    caixa: "pricing".into(),
18048                    versao: "^0.2".into(),
18049                },
18050                Membro {
18051                    caixa: "auth".into(),
18052                    versao: "^1.0".into(),
18053                },
18054            ],
18055            vec![
18056                Membro {
18057                    caixa: "cart".into(),
18058                    versao: "^0.1".into(),
18059                },
18060                Membro {
18061                    caixa: "cart".into(),
18062                    versao: "^0.1".into(),
18063                },
18064            ],
18065        ];
18066        for membros in fixtures {
18067            let c = caixa_aplicacao_with_membros(membros.clone());
18068            assert_eq!(
18069                c.membros(),
18070                membros.as_slice(),
18071                "Caixa::membros must return :membros verbatim \
18072                 (got {:?}, expected {membros:?})",
18073                c.membros(),
18074            );
18075            assert_eq!(
18076                c.membros(),
18077                c.membros.as_slice(),
18078                "Caixa::membros must element-equal the raw \
18079                 `self.membros.as_slice()` field access across every \
18080                 value in the Vec<Membro> accept-set",
18081            );
18082            assert_eq!(
18083                c.membros().is_empty(),
18084                c.membros.is_empty(),
18085                "Caixa::membros().is_empty() must byte-equal \
18086                 self.membros.is_empty() — a presence-bit drift would \
18087                 silently split the paired Caixa::declared_mesh_slots \
18088                 mesh declared-slot enumerator's presence probe from \
18089                 the peer Caixa::aplicacao_view typed-view composer's \
18090                 fold-in path",
18091            );
18092        }
18093    }
18094
18095    #[test]
18096    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
18097        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
18098        // presence-probe arm must key off [`Caixa::membros`], not the
18099        // raw `!self.membros.is_empty()` field-probe. Structurally: a
18100        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
18101        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
18102        // declared-slot list (the presence bit is non-empty, so the
18103        // mesh kind-coherence gate must surface the slot as
18104        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
18105        // push the label (the "author omitted the slot entirely" arm
18106        // — the empty-slice partition the serde-default folds onto).
18107        // The pair jointly pins the accessor + declared-slot
18108        // enumerator composition: any future silent detour that had
18109        // the accessor collapse `[Membro { .. }]` to `[]` (a
18110        // `.filter(|m| m.nome() != "__reserved__")` projection) would
18111        // silently absorb the "declared but degenerate" arm at the
18112        // accessor boundary and the
18113        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
18114        // coherence gate would silently accept a struct-literal
18115        // `Caixa` carrying the drift.
18116        //
18117        // Peer of the sibling
18118        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
18119        // (2a1f907) and
18120        // `declared_supervisor_slots_children_arm_routes_through_accessor`
18121        // (c17b51e) composition pins on the M2 `:upgrade-from` /
18122        // `:children` composite-slice arms — same "the enumerator gate
18123        // must route through the substrate-primitive typed dispatch"
18124        // discipline extended onto the M3 `:membros` composite-slice
18125        // arm, opening the M3 arm of the declared-slot enumerator's
18126        // routing invariant.
18127        use crate::aplicacao::Membro;
18128        let c = caixa_aplicacao_with_membros(vec![Membro {
18129            caixa: "cart".into(),
18130            versao: "^0.1".into(),
18131        }]);
18132        let slots = c.declared_mesh_slots();
18133        assert!(
18134            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
18135            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
18136             `:membros` is non-empty — the accessor and the enumerator \
18137             gate must route through the same substrate-primitive \
18138             typed dispatch on the outer :membros presence bit (got \
18139             slots={slots:?})",
18140        );
18141        let c = caixa_aplicacao_with_membros(vec![]);
18142        let slots = c.declared_mesh_slots();
18143        assert!(
18144            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
18145            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
18146             when `:membros` is empty — the author-omitted arm must \
18147             route through the accessor's empty-slice return unchanged \
18148             (got slots={slots:?})",
18149        );
18150    }
18151
18152    #[test]
18153    fn aplicacao_view_membros_arm_routes_through_accessor() {
18154        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
18155        // fold-in arm must key off [`Caixa::membros`], not the raw
18156        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
18157        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
18158        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
18159        // member list through the accessor into the typed
18160        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
18161        // every entry the accessor surfaces must land in the view's
18162        // `membros` slot in the same order. The pair jointly pins the
18163        // accessor + view-composer composition: any future silent
18164        // detour that had the accessor return a fresh-cloned
18165        // `Vec<Membro>` copy would silently break the reference-
18166        // identity pin the peer `aplicacao_view` fold-in path reads
18167        // from — the fold would clone once more per accessor call
18168        // instead of borrowing the storage buffer verbatim once.
18169        //
18170        // Peer of the sibling
18171        // `aplicacao_view_politicas_arm_folds_through_accessor`
18172        // (5d23d29) /
18173        // `aplicacao_view_placement_arm_folds_through_accessor`
18174        // (4fb8074) /
18175        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
18176        // composition pins on the M3 `:politicas` / `:placement` /
18177        // `:entrada` outer-`Option<&Composite>` arms — extended here to
18178        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
18179        // closing the aplicacao-view composer's routing invariant on
18180        // the composite-slice input.
18181        use crate::aplicacao::Membro;
18182        let c = caixa_aplicacao_with_membros(vec![
18183            Membro {
18184                caixa: "cart".into(),
18185                versao: "^0.1".into(),
18186            },
18187            Membro {
18188                caixa: "pricing".into(),
18189                versao: "^0.2".into(),
18190            },
18191        ]);
18192        let view = c
18193            .aplicacao_view()
18194            .expect("Aplicacao kind must produce an aplicacao_view");
18195        assert_eq!(
18196            view.membros(),
18197            c.membros(),
18198            "aplicacao_view must fold Caixa::membros verbatim into \
18199             AplicacaoSpec::membros — the accessor and the view \
18200             composer must route through the same substrate-primitive \
18201             typed dispatch on the outer :membros slice (got view \
18202             membros={:?}, expected {:?})",
18203            view.membros(),
18204            c.membros(),
18205        );
18206    }
18207
18208    #[test]
18209    fn membros_projects_slice_by_borrow() {
18210        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
18211        // borrow — the returned slice borrows the underlying
18212        // `Vec<Membro>` storage of the `:membros` slot and the
18213        // accessor must not clone the backing `Vec` on every call.
18214        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
18215        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
18216        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
18217        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18218        // `exe_projects_slice_by_borrow` 65d9527,
18219        // `servicos_projects_slice_by_borrow` 611f78b,
18220        // `deps_projects_slice_by_borrow` ad34b4e,
18221        // `deps_dev_projects_slice_by_borrow` f7fd81e,
18222        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
18223        // `children_projects_slice_by_borrow` c17b51e) on the sibling
18224        // outer top-level [`Caixa`] scalar-element and composite-
18225        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
18226        // slot composite-element `&[Composite]` axis: the accessor's
18227        // returned slice must borrow from `&self` (the returned
18228        // reference's lifetime is tied to `&self`), and calling the
18229        // accessor twice on the same [`Caixa`] must yield slices that
18230        // are pointer-equal (the underlying byte-buffer is the storage
18231        // `Vec`'s allocation, not a fresh copy) as well as value-equal
18232        // (idempotent, no side effects on `&self`).
18233        //
18234        // Pins against a future silent detour that returned an owned
18235        // `Vec<Membro>` (which would type-check but silently clone on
18236        // every call), a `&Vec<Membro>` return (which would leak the
18237        // backing `Vec`'s grow/push/reserve surface no downstream
18238        // consumer reaches for), or a one-arm-only accessor that
18239        // returned a saturating value on some sentinel input.
18240        use crate::aplicacao::Membro;
18241        for membros in [
18242            vec![],
18243            vec![Membro {
18244                caixa: "cart".into(),
18245                versao: "^0.1".into(),
18246            }],
18247            vec![
18248                Membro {
18249                    caixa: "cart".into(),
18250                    versao: "^0.1".into(),
18251                },
18252                Membro {
18253                    caixa: "pricing".into(),
18254                    versao: "^0.2".into(),
18255                },
18256            ],
18257        ] {
18258            let c = caixa_aplicacao_with_membros(membros.clone());
18259            let first = c.membros();
18260            let second = c.membros();
18261            assert_eq!(
18262                first, second,
18263                "Caixa::membros must be idempotent — two successive \
18264                 calls on the same &self must return the same &[Membro]",
18265            );
18266            assert_eq!(
18267                first.as_ptr(),
18268                second.as_ptr(),
18269                "Caixa::membros must borrow the underlying Vec<Membro> \
18270                 storage — two successive calls must return slices with \
18271                 the same backing pointer (a fresh Vec<Membro> clone \
18272                 would change the pointer on every call)",
18273            );
18274            assert_eq!(
18275                first,
18276                membros.as_slice(),
18277                "Caixa::membros must return :membros verbatim by borrow \
18278                 — got {first:?}, expected {membros:?}",
18279            );
18280        }
18281    }
18282
18283    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
18284
18285    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
18286        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18287        c.kind = CaixaKind::Aplicacao;
18288        c.contratos = contratos;
18289        c
18290    }
18291
18292    fn contrato_http_for_test(
18293        de: &str,
18294        para: &str,
18295        endpoint: &str,
18296    ) -> crate::aplicacao::WitContract {
18297        crate::aplicacao::WitContract {
18298            de: de.into(),
18299            para: para.into(),
18300            wit: "wasi:http/proxy".into(),
18301            endpoint: Some(endpoint.into()),
18302            subject: None,
18303            slot: None,
18304        }
18305    }
18306
18307    #[test]
18308    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
18309        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
18310        // composite `&[WitContract]`-return slice-shape pin:
18311        // [`Caixa::contratos`] must return the `:contratos` typed
18312        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
18313        // over the same backing buffer the raw
18314        // `self.contratos.as_slice()` field access borrows from,
18315        // element-equal across every representative fixture in the
18316        // accept-set — `[]` (the "no contracts declared" arm every
18317        // non-`Aplicacao`-kind `defcaixa` carries by
18318        // `#[serde(default)]` and every leaf-Aplicacao with a single
18319        // member carries), a canonical single-edge fixture (the
18320        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
18321        // edge), and a canonical multi-edge fixture with three distinct
18322        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
18323        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
18324        //
18325        // Pins against a future silent detour that returned an owned
18326        // `Vec<WitContract>` (which would type-check but silently clone
18327        // on every accessor call, breaking the zero-cost projection
18328        // every peer sibling slice accessor carries), an axis-shuffled
18329        // projection (a future detour that reordered edges through the
18330        // accessor would silently split the paired
18331        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
18332        // traversal input from the peer [`Self::aplicacao_view`] fold-
18333        // in path's clone-order input, since every canonical
18334        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
18335        // seed dispatch reads the edge set through the same slice),
18336        // or a reference to an operator-resolved overlay (the future
18337        // per-cluster `:contratos-overrides` slot — its resolution
18338        // must land at exactly this accessor body, not silently divert
18339        // the raw slot away from a second consumer).
18340        //
18341        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
18342        // accessor pin on the substrate primitive for M2 / M3 typed-
18343        // slot vec-carry axes — closes the outer-`Caixa`
18344        // `&[Composite]` composite-slice sub-family the sibling M2
18345        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18346        // (2a1f907) and
18347        // `children_returns_children_slice_verbatim_across_permutations`
18348        // (c17b51e) pins opened and the M3
18349        // `membros_returns_membros_slice_verbatim_across_permutations`
18350        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
18351        // slot arm of the composite-slice sub-family. Peer at the outer
18352        // altitude of the closed inner-
18353        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
18354        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
18355        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
18356            vec![],
18357            vec![contrato_http_for_test("cart", "catalog", "/items")],
18358            vec![
18359                contrato_http_for_test("cart", "catalog", "/items"),
18360                contrato_http_for_test("cart", "pricing", "/price"),
18361                contrato_http_for_test("cart", "auth", "/whoami"),
18362            ],
18363        ];
18364        for contratos in fixtures {
18365            let c = caixa_aplicacao_with_contratos(contratos.clone());
18366            assert_eq!(
18367                c.contratos(),
18368                contratos.as_slice(),
18369                "Caixa::contratos must return :contratos verbatim \
18370                 (got {:?}, expected {contratos:?})",
18371                c.contratos(),
18372            );
18373            assert_eq!(
18374                c.contratos(),
18375                c.contratos.as_slice(),
18376                "Caixa::contratos must element-equal the raw \
18377                 `self.contratos.as_slice()` field access across every \
18378                 value in the Vec<WitContract> accept-set",
18379            );
18380            assert_eq!(
18381                c.contratos().is_empty(),
18382                c.contratos.is_empty(),
18383                "Caixa::contratos().is_empty() must byte-equal \
18384                 self.contratos.is_empty() — a presence-bit drift would \
18385                 silently split the paired Caixa::declared_mesh_slots \
18386                 mesh declared-slot enumerator's presence probe from \
18387                 the peer Caixa::aplicacao_view typed-view composer's \
18388                 fold-in path",
18389            );
18390        }
18391    }
18392
18393    #[test]
18394    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
18395        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
18396        // presence-probe arm must key off [`Caixa::contratos`], not the
18397        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
18398        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
18399        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
18400        // presence bit is non-empty, so the mesh kind-coherence gate
18401        // must surface the slot as "declared"), and a `Caixa {
18402        // contratos: vec![], .. }` must NOT push the label (the "author
18403        // omitted the slot entirely" arm — the empty-slice partition
18404        // the serde-default folds onto). The pair jointly pins the
18405        // accessor + declared-slot enumerator composition: any future
18406        // silent detour that had the accessor collapse
18407        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
18408        // "__reserved__")` projection) would silently absorb the
18409        // "declared but degenerate" arm at the accessor boundary and
18410        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
18411        // coherence gate would silently accept a struct-literal
18412        // `Caixa` carrying the drift.
18413        //
18414        // Peer of the sibling
18415        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
18416        // (2a1f907),
18417        // `declared_supervisor_slots_children_arm_routes_through_accessor`
18418        // (c17b51e), and
18419        // `declared_mesh_slots_membros_arm_routes_through_accessor`
18420        // (0f26987) composition pins on the M2 `:upgrade-from` /
18421        // `:children` / M3 `:membros` composite-slice arms — same "the
18422        // enumerator gate must route through the substrate-primitive
18423        // typed dispatch" discipline extended onto the M3 `:contratos`
18424        // composite-slice arm, closing the M3 mesh-slot arm of the
18425        // declared-slot enumerator's routing invariant on the
18426        // composite-slice inputs.
18427        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
18428            "cart", "catalog", "/items",
18429        )]);
18430        let slots = c.declared_mesh_slots();
18431        assert!(
18432            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
18433            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
18434             `:contratos` is non-empty — the accessor and the enumerator \
18435             gate must route through the same substrate-primitive \
18436             typed dispatch on the outer :contratos presence bit (got \
18437             slots={slots:?})",
18438        );
18439        let c = caixa_aplicacao_with_contratos(vec![]);
18440        let slots = c.declared_mesh_slots();
18441        assert!(
18442            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
18443            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
18444             when `:contratos` is empty — the author-omitted arm must \
18445             route through the accessor's empty-slice return unchanged \
18446             (got slots={slots:?})",
18447        );
18448    }
18449
18450    #[test]
18451    fn aplicacao_view_contratos_arm_routes_through_accessor() {
18452        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
18453        // fold-in arm must key off [`Caixa::contratos`], not the raw
18454        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
18455        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
18456        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
18457        // per-edge list through the accessor into the typed
18458        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
18459        // every entry the accessor surfaces must land in the view's
18460        // `contratos` slot in the same order. The pair jointly pins
18461        // the accessor + view-composer composition: a future silent
18462        // detour that had the accessor shuffle or drop an edge would
18463        // silently split the paired declared-slot enumerator's
18464        // presence bit from the typed-view composer's edge-list, a
18465        // two-consumer split at the enumerator and the view composer
18466        // far from the source `caixa.lisp`.
18467        //
18468        // Peer of the sibling
18469        // `aplicacao_view_membros_arm_routes_through_accessor`
18470        // (0f26987) composition pin on the M3 `:membros` outer-
18471        // `&[Composite]` composite-slice arm, closing the aplicacao-
18472        // view composer's routing invariant on the composite-slice
18473        // inputs at the outer altitude.
18474        let c = caixa_aplicacao_with_contratos(vec![
18475            contrato_http_for_test("cart", "catalog", "/items"),
18476            contrato_http_for_test("cart", "pricing", "/price"),
18477        ]);
18478        let view = c
18479            .aplicacao_view()
18480            .expect("Aplicacao kind must produce an aplicacao_view");
18481        assert_eq!(
18482            view.contratos(),
18483            c.contratos(),
18484            "aplicacao_view must fold Caixa::contratos verbatim into \
18485             AplicacaoSpec::contratos — the accessor and the view \
18486             composer must route through the same substrate-primitive \
18487             typed dispatch on the outer :contratos slice (got view \
18488             contratos={:?}, expected {:?})",
18489            view.contratos(),
18490            c.contratos(),
18491        );
18492    }
18493
18494    #[test]
18495    fn contratos_projects_slice_by_borrow() {
18496        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
18497        // by borrow — the returned slice borrows the underlying
18498        // `Vec<WitContract>` storage of the `:contratos` slot and the
18499        // accessor must not clone the backing `Vec` on every call.
18500        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
18501        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
18502        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
18503        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18504        // `exe_projects_slice_by_borrow` 65d9527,
18505        // `servicos_projects_slice_by_borrow` 611f78b,
18506        // `deps_projects_slice_by_borrow` ad34b4e,
18507        // `deps_dev_projects_slice_by_borrow` f7fd81e,
18508        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
18509        // `children_projects_slice_by_borrow` c17b51e,
18510        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
18511        // outer top-level [`Caixa`] scalar-element and composite-
18512        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
18513        // composite-element `&[Composite]` axis on the by-borrow pin:
18514        // the accessor's returned slice must borrow from `&self` (the
18515        // returned reference's lifetime is tied to `&self`), and
18516        // calling the accessor twice on the same [`Caixa`] must yield
18517        // slices that are pointer-equal (the underlying byte-buffer is
18518        // the storage `Vec`'s allocation, not a fresh copy) as well as
18519        // value-equal (idempotent, no side effects on `&self`).
18520        //
18521        // Pins against a future silent detour that returned an owned
18522        // `Vec<WitContract>` (which would type-check but silently clone
18523        // on every call), a `&Vec<WitContract>` return (which would
18524        // leak the backing `Vec`'s grow/push/reserve surface no
18525        // downstream consumer reaches for), or a one-arm-only accessor
18526        // that returned a saturating value on some sentinel input.
18527        for contratos in [
18528            vec![],
18529            vec![contrato_http_for_test("cart", "catalog", "/items")],
18530            vec![
18531                contrato_http_for_test("cart", "catalog", "/items"),
18532                contrato_http_for_test("cart", "pricing", "/price"),
18533            ],
18534        ] {
18535            let c = caixa_aplicacao_with_contratos(contratos.clone());
18536            let first = c.contratos();
18537            let second = c.contratos();
18538            assert_eq!(
18539                first, second,
18540                "Caixa::contratos must be idempotent — two successive \
18541                 calls on the same &self must return the same \
18542                 &[WitContract]",
18543            );
18544            assert_eq!(
18545                first.as_ptr(),
18546                second.as_ptr(),
18547                "Caixa::contratos must borrow the underlying \
18548                 Vec<WitContract> storage — two successive calls must \
18549                 return slices with the same backing pointer (a fresh \
18550                 Vec<WitContract> clone would change the pointer on \
18551                 every call)",
18552            );
18553            assert_eq!(
18554                first,
18555                contratos.as_slice(),
18556                "Caixa::contratos must return :contratos verbatim by \
18557                 borrow — got {first:?}, expected {contratos:?}",
18558            );
18559        }
18560    }
18561
18562    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
18563
18564    #[test]
18565    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
18566        // Load-bearing invariant: every multi-word top-level [`Caixa`]
18567        // serde-derived JSON key routes through a lifted `&'static str`
18568        // const. The Rust field names are `snake_case`
18569        // (`deps_dev` / `upgrade_from` / `max_restarts` /
18570        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
18571        // "camelCase")]` derive attribute maps each to the camelCase
18572        // byte-string the [`Caixa::to_lisp`] round-trip's
18573        // `serde_json::to_value(self)` step lands under before
18574        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
18575        // to the kebab-case `:deps-dev` / `:upgrade-from` /
18576        // `:max-restarts` / `:restart-window` author surface. Serialize
18577        // a fully-populated [`Caixa`] and pin that each canonical
18578        // byte-sequence appears verbatim in the JSON — a future
18579        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
18580        // verbatim-field-name flip at the derive attribute (any of
18581        // which would silently break every [`Caixa::to_lisp`]
18582        // round-trip and the future M4 operator-side manifest ingest's
18583        // `Value::get(<key>)` navigation) surfaces here as a build-time
18584        // test failure at `manifest.rs`, not as an apply-time
18585        // `.get(<stale-canonical-const>)` returning `None` far from the
18586        // derive-attr drift's commit. Same discipline the sibling
18587        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
18588        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
18589        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
18590        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
18591        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
18592        // [`UpgradeFromEntry`] per-entry axes — extended here to the
18593        // enclosing M0 [`Caixa`] top-level axis so the last of the four
18594        // multi-word top-level [`Caixa`] serde-derived JSON keys
18595        // (`depsDev`) joins the substrate's "one canonical byte-string
18596        // per typed serialized-key axis" discipline.
18597        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
18598        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18599        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18600        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
18601        c.upgrade_from = vec![UpgradeFromEntry {
18602            from: "0.0.1".into(),
18603            instructions: vec![UpgradeInstruction::Restart],
18604        }];
18605        c.estrategia = Some(RestartStrategy::OneForOne);
18606        c.max_restarts = Some(3);
18607        c.restart_window = Some("60s".into());
18608        c.children = vec![ChildSpec {
18609            caixa: "child".into(),
18610            versao: "^0.1".into(),
18611            restart: RestartPolicy::Permanent,
18612        }];
18613        let json = serde_json::to_string(&c).unwrap();
18614        for key in [
18615            crate::render::CAIXA_KEY_DEPS_DEV,
18616            crate::render::M2_KEY_UPGRADE_FROM,
18617            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
18618            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
18619        ] {
18620            let quoted = format!("\"{key}\"");
18621            assert!(
18622                json.contains(&quoted),
18623                "serialized Caixa must carry the lifted top-level \
18624                 multi-word byte-sequence {quoted} verbatim in the JSON \
18625                 emission (got: {json})",
18626            );
18627        }
18628    }
18629
18630    #[test]
18631    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
18632        // Cross-axis drift-detection pin: a future collapse of the four
18633        // canonical [`Caixa`] top-level multi-word byte-strings onto the
18634        // same value (e.g. an accidental copy-paste flip of
18635        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
18636        // `"upgradeFrom"`) would silently reroute every downstream
18637        // `Value::get(<key>)` probe on one axis onto the sibling axis's
18638        // top-level entry and pass every propagation-probe test that
18639        // expected only the stale axis's value. Peer of the sibling
18640        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
18641        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
18642        let all = [
18643            crate::render::CAIXA_KEY_DEPS_DEV,
18644            crate::render::M2_KEY_UPGRADE_FROM,
18645            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
18646            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
18647        ];
18648        for (i, a) in all.iter().enumerate() {
18649            for b in all.iter().skip(i + 1) {
18650                assert_ne!(
18651                    a, b,
18652                    "Caixa top-level multi-word key consts must be \
18653                     pairwise-distinct canonical byte-sequences — got \
18654                     `{a}` == `{b}`",
18655                );
18656            }
18657        }
18658    }
18659
18660    #[test]
18661    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
18662        // Shape-pin: every [`Caixa`] top-level multi-word key const must
18663        // be a lowerCamelCase byte-sequence (no `snake_case`
18664        // underscores, no `kebab-case` hyphens, no leading colon, no
18665        // `PascalCase` leading capital, no whitespace / dots) — the
18666        // canonical shape the `#[serde(rename_all = "camelCase")]`
18667        // derive produces on [`Caixa`]. A future flip to a
18668        // non-camelCase attribute at the derive surfaces both here
18669        // (this test fails on the stale-constant shape) and at
18670        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
18671        // (that test fails on the mismatch between const and derive).
18672        // Peer with `membro_key_consts_are_lower_camel_case_shape`
18673        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
18674        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
18675        for key in [
18676            crate::render::CAIXA_KEY_DEPS_DEV,
18677            crate::render::M2_KEY_UPGRADE_FROM,
18678            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
18679            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
18680        ] {
18681            assert!(
18682                !key.is_empty(),
18683                "Caixa top-level multi-word key const must be non-empty \
18684                 (got {key:?})"
18685            );
18686            let first = key.chars().next().unwrap();
18687            assert!(
18688                first.is_ascii_lowercase(),
18689                "Caixa top-level multi-word key const must lead with an \
18690                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
18691            );
18692            assert!(
18693                key.chars().all(|c| c.is_ascii_alphanumeric()),
18694                "Caixa top-level multi-word key const must be \
18695                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
18696                 whitespace (got {key:?})",
18697            );
18698        }
18699    }
18700
18701    #[test]
18702    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
18703        // Scalar-value pin: the byte-string the
18704        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
18705        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
18706        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
18707        // → `depsTest` matching a hypothetical per-test-target
18708        // vocabulary flip) lands as an edit to exactly one const AND
18709        // one derive attribute — the sibling
18710        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
18711        // pin already ties the const to the derive attribute, so a
18712        // rebrand that touches only one side of the pair fails at
18713        // caixa-core build time. Same "scalar-value pin per const"
18714        // discipline the sibling
18715        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
18716        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
18717        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
18718        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
18719    }
18720
18721    #[test]
18722    fn caixa_key_deps_pins_canonical_byte_string() {
18723        // Scalar-value pin: the byte-string the
18724        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
18725        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
18726        // on the two-list dep-graph serialized-key axis — the sibling
18727        // pin covers the multi-word `deps_dev → depsDev` camelCase
18728        // arm, this pin covers the single-word `deps → deps` no-op arm
18729        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
18730        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
18731        // axis and the emitted JSON key equals the source-side field
18732        // name byte-for-byte). A future [`crate::Caixa::deps`] field
18733        // rename (`deps` → `dependencies` matching Cargo's verbatim
18734        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
18735        // hypothetical per-runtime-target vocabulary flip) OR an added
18736        // `#[serde(rename = "…")]` explicit override lands as an edit
18737        // to exactly one const AND one derive-attr / field name — the
18738        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
18739        // pin ties the const to the emitted JSON key, so a rebrand
18740        // that touches only one side of the pair fails at caixa-core
18741        // build time.
18742        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
18743    }
18744
18745    #[test]
18746    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
18747        // Load-bearing invariant on the single-word `deps` top-level
18748        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
18749        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
18750        // `serde_json::to_value(self)` step emits. Serialize a
18751        // populated [`Caixa`] whose `:deps` slot carries at least one
18752        // entry (the `#[serde(default)]` attribute on the field emits
18753        // an empty `[]` even without members, but a non-empty vec
18754        // additionally covers the codec's per-`Dep`-entry emission
18755        // path) and pin that `"deps"` appears verbatim in the JSON
18756        // emission — a future accidental `rename_all = "snake_case"` /
18757        // `"kebab-case"` flip at the derive attribute (or an added
18758        // `#[serde(rename = "…")]` explicit override on the field, or
18759        // a Rust field rename) would break every [`Caixa::to_lisp`]
18760        // round-trip and the future M4 operator-side manifest ingest's
18761        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
18762        // build-time test failure at `manifest.rs`, not as an
18763        // apply-time `.get(<stale-canonical-const>)` returning `None`
18764        // far from the drift's commit. Peer of the sibling
18765        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
18766        // multi-word pin on the same M0 [`Caixa`] top-level
18767        // serialized-key axis, extended here to the single-word arm
18768        // the multi-word test's `rename_all = "camelCase"` sweep can't
18769        // reach (single-word `deps → deps` is a no-op the multi-word
18770        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
18771        // `\"restartWindow\"` byte-scan can never observe).
18772        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18773        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
18774        let json = serde_json::to_string(&c).unwrap();
18775        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
18776        assert!(
18777            json.contains(&quoted),
18778            "serialized Caixa must carry the lifted top-level `deps` \
18779             byte-sequence {quoted} verbatim in the JSON emission (got: \
18780             {json})",
18781        );
18782    }
18783
18784    #[test]
18785    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
18786        // Cross-axis drift-detection pin on the two-list dep-graph
18787        // renderer-side wire-key axis: a future collapse of the
18788        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
18789        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
18790        // same value (e.g. an accidental copy-paste flip of
18791        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
18792        // reroute every downstream `Value::get(<key>)` probe on one
18793        // axis onto the sibling axis's dep-list and pass every
18794        // propagation-probe test that expected only the stale axis's
18795        // value — a dev-only dep would land in the runtime closure at
18796        // publish time, or a runtime dep would be excluded from the
18797        // published lacre. Peer of the sibling four-way distinct pin
18798        // on the top-level multi-word tetrad
18799        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
18800        // and the two-way pin on the sibling
18801        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
18802        // author-facing arm (4da6fba's test), extended here to the
18803        // renderer-side wire-key arm of the same two-list dep-graph
18804        // axis so both halves of the "one canonical byte-string per
18805        // typed axis per (author, wire)" grid carry the same
18806        // distinct-ness discipline.
18807        assert_ne!(
18808            crate::render::CAIXA_KEY_DEPS,
18809            crate::render::CAIXA_KEY_DEPS_DEV,
18810            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
18811             canonical byte-sequences on the two-list dep-graph \
18812             renderer-side wire-key axis"
18813        );
18814    }
18815
18816    // ── DepList / Caixa::push_dep pin ────────────────────────────────
18817    //
18818    // The compounding pin: the two-arm closed-set typed enum
18819    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
18820    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
18821    // consumer of the top-level manifest's dep-mutation surface reads
18822    // through, and the typed dispatch [`Caixa::push_dep`] on the
18823    // substrate primitive folds the "select list → check within-list
18824    // dup → push" cascade onto one method call. Prior to this landing
18825    // the two axes lived across two `&'static str` constants
18826    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
18827    // set type carrying the pair; the `feira add` mutation site's
18828    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
18829    // caixa.deps }` dispatch expressed no compile-time link back to
18830    // the substrate primitive, and a future third dep-list axis would
18831    // have silently split at every open-coded mutation site.
18832
18833    #[test]
18834    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
18835        // Every arm returns the same `&'static str` the substrate's
18836        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
18837        // constants carry. A future rebrand on either constant reaches
18838        // the enum through one edit; a regression to inline literals
18839        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
18840        // quotes from the wire-format constants every consumer routes
18841        // through and this pin flags it at build time.
18842        assert_eq!(
18843            crate::dep::DepList::Prod.as_str(),
18844            crate::render::DEP_AUTHOR_KEY_DEPS
18845        );
18846        assert_eq!(
18847            crate::dep::DepList::Dev.as_str(),
18848            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
18849        );
18850    }
18851
18852    #[test]
18853    fn dep_list_display_routes_through_as_str() {
18854        // Same as-str-through-Display convergence discipline the
18855        // sibling closed-set typed enums carry — a `format!("{list}")`
18856        // call must land byte-for-byte on the accessor's return so a
18857        // future consumer that formats the enum for a diagnostic line
18858        // reaches the same wire-format constant the wire-format
18859        // producers do.
18860        assert_eq!(
18861            format!("{}", crate::dep::DepList::Prod),
18862            crate::dep::DepList::Prod.as_str()
18863        );
18864        assert_eq!(
18865            format!("{}", crate::dep::DepList::Dev),
18866            crate::dep::DepList::Dev.as_str()
18867        );
18868    }
18869
18870    #[test]
18871    fn dep_list_all_enumerates_every_variant_once() {
18872        // Exhaustive-iteration pin — every arm appears exactly once in
18873        // `ALL`, matching the closed set the compiler enforces on the
18874        // sibling `match self` arms. A future variant addition that
18875        // extends only one method's match without extending `ALL`
18876        // would silently drop the new arm from every consumer that
18877        // iterates the slice.
18878        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
18879        assert!(variants.contains(&crate::dep::DepList::Prod));
18880        assert!(variants.contains(&crate::dep::DepList::Dev));
18881        assert_eq!(variants.len(), 2);
18882    }
18883
18884    #[test]
18885    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
18886        // Reverse projection on the two-list dep-graph axis: the
18887        // author-surface wire tag the sibling `as_str` emitter walks
18888        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
18889        // `Some(DepList::Prod)`. A regression that hand-rolled the
18890        // per-arm match without routing through the lifted
18891        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
18892        // future wire-tag rebrand and this pin flags it at build time.
18893        assert_eq!(
18894            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
18895            Some(crate::dep::DepList::Prod)
18896        );
18897    }
18898
18899    #[test]
18900    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
18901        // Peer of the `Prod`-arm pin on the dev-only axis: the
18902        // author-surface wire tag the sibling `as_str` emitter walks
18903        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
18904        // back to `Some(DepList::Dev)`. Same drift-detection posture
18905        // as the peer arm — the sibling method `match` arms are
18906        // compiler-checked exhaustive so a future variant addition
18907        // trips at build time.
18908        assert_eq!(
18909            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
18910            Some(crate::dep::DepList::Dev)
18911        );
18912    }
18913
18914    #[test]
18915    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
18916        // Every input outside the closed-set arm-string set the
18917        // sibling `as_str` emitter walks lands on the terminal `None`
18918        // fallback — no silent-accept surface. Sweeps a set of
18919        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
18920        // rebrand candidates, foreign wire tags, empty string) so a
18921        // future variant addition that widened one wire form without
18922        // extending the emitter's arm-set would trip the sibling
18923        // round-trip pin below rather than silently accepting the new
18924        // form here.
18925        for candidate in [
18926            "",
18927            "deps",
18928            "deps-dev",
18929            ":deps ",
18930            ":Deps",
18931            ":DEPS",
18932            ":build-dep",
18933            ":tool-dep",
18934            "prod",
18935            "dev",
18936        ] {
18937            assert_eq!(
18938                crate::dep::DepList::from_wire(candidate),
18939                None,
18940                "from_wire({candidate:?}) must return None; every input outside \
18941                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
18942                 the sibling as_str emitter walks lands on the terminal fallback",
18943            );
18944        }
18945    }
18946
18947    #[test]
18948    fn dep_list_round_trips_through_as_str_and_from_wire() {
18949        // Load-bearing round-trip pin: every arm the `ALL` iteration
18950        // exposes survives the `as_str` → `from_wire` composition
18951        // byte-for-byte. Same discipline the sibling closed-set enums
18952        // carry — `CaixaKind` /
18953        // `RestartStrategy` / `RestartPolicy` /
18954        // `PlacementStrategy` — extended onto the two-list dep-graph
18955        // axis. A future variant addition that extends `ALL` +
18956        // `as_str` without extending `from_wire` (or vice versa)
18957        // trips at build time on this iteration because the compiler
18958        // enforces exhaustiveness on the sibling `match self` arms.
18959        for &list in crate::dep::DepList::ALL {
18960            assert_eq!(
18961                crate::dep::DepList::from_wire(list.as_str()),
18962                Some(list),
18963                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
18964                 a silent split between the forward emitter and the reverse parser \
18965                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
18966            );
18967        }
18968    }
18969
18970    #[test]
18971    fn push_dep_routes_to_deps_slot_on_prod_arm() {
18972        // The `Prod` arm dispatches to the runtime-closure `:deps`
18973        // slot every downstream lacre-pipeline consumer resolves at
18974        // build time. A future arm that regressed to inline `&mut
18975        // self.deps_dev` on the `Prod` path would silently reroute
18976        // every runtime dep into the dev-only closure at publish time
18977        // — this pin refuses that regression.
18978        let src = Caixa::template("host");
18979        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18980        let before_deps = caixa.deps().len();
18981        let before_deps_dev = caixa.deps_dev().len();
18982        let dep = Dep {
18983            nome: "caixa-teia".to_string(),
18984            versao: "^0.1".to_string(),
18985            fonte: None,
18986            opcional: false,
18987            caracteristicas: Vec::new(),
18988        };
18989        caixa
18990            .push_dep(crate::dep::DepList::Prod, dep)
18991            .expect("first push into :deps succeeds");
18992        assert_eq!(caixa.deps().len(), before_deps + 1);
18993        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
18994        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
18995    }
18996
18997    #[test]
18998    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
18999        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
19000        // must dispatch to the dev-only-closure `:deps-dev` slot every
19001        // downstream test-facing artifact resolver reads. A future
19002        // regression that inverted the two arms would silently route
19003        // every dev-only dep into the runtime closure at publish time
19004        // and this pin catches it before the drift ships.
19005        let src = Caixa::template("host");
19006        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19007        let dep = Dep {
19008            nome: "tatara-check".to_string(),
19009            versao: "*".to_string(),
19010            fonte: None,
19011            opcional: false,
19012            caracteristicas: Vec::new(),
19013        };
19014        caixa
19015            .push_dep(crate::dep::DepList::Dev, dep)
19016            .expect("first push into :deps-dev succeeds");
19017        assert!(caixa.deps().is_empty());
19018        assert_eq!(caixa.deps_dev().len(), 1);
19019        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
19020    }
19021
19022    #[test]
19023    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
19024        // Within-list dup check routes through the canonical
19025        // [`DepError::DuplicateNome`] carrier — the substrate's typed
19026        // diagnostic for the same axis [`Caixa::validate_deps`]'s
19027        // parse-time [`crate::render::insert_first_seen`] walk raises
19028        // on. Prior to the lift the mutation site's inline
19029        // `bail!("dep '{}' already declared", …)` string-diagnostic
19030        // path expressed no through-line back to the typed error;
19031        // routing every dep-list refusal through one carrier means an
19032        // author reading a `feira add` refusal and a `feira build`
19033        // refusal reaches for the same corrective surface without
19034        // switching diagnostic idioms.
19035        let src = Caixa::template("host");
19036        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19037        let dep = Dep {
19038            nome: "caixa-teia".to_string(),
19039            versao: "^0.1".to_string(),
19040            fonte: None,
19041            opcional: false,
19042            caracteristicas: Vec::new(),
19043        };
19044        caixa
19045            .push_dep(crate::dep::DepList::Prod, dep.clone())
19046            .expect("first push succeeds");
19047        let dup = Dep {
19048            nome: "caixa-teia".to_string(),
19049            versao: "^0.2".to_string(),
19050            fonte: None,
19051            opcional: false,
19052            caracteristicas: Vec::new(),
19053        };
19054        let err = caixa
19055            .push_dep(crate::dep::DepList::Prod, dup)
19056            .expect_err("second push with same :nome refuses");
19057        assert_eq!(
19058            err,
19059            DepError::DuplicateNome {
19060                nome: "caixa-teia".to_string(),
19061                list: crate::render::DEP_AUTHOR_KEY_DEPS,
19062            }
19063        );
19064        // The refused mutation must not corrupt the target list —
19065        // exactly one entry lives past the refusal, matching the
19066        // canonical single-source-of-truth invariant `Caixa::deps()`
19067        // carries.
19068        assert_eq!(caixa.deps().len(), 1);
19069    }
19070
19071    #[test]
19072    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
19073        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
19074        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
19075        // `list` payload so a future author reading the refusal grep's
19076        // for the correct `:deps-dev` block in their `caixa.lisp`,
19077        // not the sibling `:deps` block the runtime closure resolves.
19078        let src = Caixa::template("host");
19079        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19080        let dep = Dep {
19081            nome: "tatara-check".to_string(),
19082            versao: "*".to_string(),
19083            fonte: None,
19084            opcional: false,
19085            caracteristicas: Vec::new(),
19086        };
19087        caixa
19088            .push_dep(crate::dep::DepList::Dev, dep.clone())
19089            .expect("first push succeeds");
19090        let err = caixa
19091            .push_dep(crate::dep::DepList::Dev, dep)
19092            .expect_err("second push with same :nome refuses");
19093        assert!(matches!(
19094            err,
19095            DepError::DuplicateNome {
19096                ref nome,
19097                list,
19098            } if nome == "tatara-check"
19099                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
19100        ));
19101    }
19102
19103    #[test]
19104    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
19105        // The within-list dup check is scoped to the target arm — a
19106        // caixa may legitimately carry the same `:nome` under both
19107        // `:deps` and `:deps-dev` (though the substrate's peer
19108        // [`crate::Caixa::validate_deps`] walk still refuses the
19109        // shape at parse time; the mutation-site refusal is scoped to
19110        // the mutation-site's list to match the peer parse-time
19111        // per-list [`crate::render::insert_first_seen`] discipline).
19112        // The two arms hold independent seen-sets.
19113        let src = Caixa::template("host");
19114        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19115        let dep_prod = Dep {
19116            nome: "shared".to_string(),
19117            versao: "^0.1".to_string(),
19118            fonte: None,
19119            opcional: false,
19120            caracteristicas: Vec::new(),
19121        };
19122        let dep_dev = Dep {
19123            nome: "shared".to_string(),
19124            versao: "*".to_string(),
19125            fonte: None,
19126            opcional: false,
19127            caracteristicas: Vec::new(),
19128        };
19129        caixa
19130            .push_dep(crate::dep::DepList::Prod, dep_prod)
19131            .expect("push into :deps succeeds");
19132        caixa
19133            .push_dep(crate::dep::DepList::Dev, dep_dev)
19134            .expect("push same :nome into :deps-dev succeeds");
19135        assert_eq!(caixa.deps().len(), 1);
19136        assert_eq!(caixa.deps_dev().len(), 1);
19137    }
19138
19139    #[test]
19140    fn deps_of_prod_returns_the_deps_slot_verbatim() {
19141        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
19142        // accessor must project onto the runtime-closure `:deps` slot —
19143        // element-equal and length-equal to the sibling per-slot
19144        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
19145        // A future arm that regressed to `self.deps_dev()` on the `Prod`
19146        // path would silently reroute every downstream typed-dispatch
19147        // walker (the [`Caixa::validate_deps`] per-list
19148        // [`crate::render::insert_first_seen`] dedup walk, any future
19149        // per-axis-parametrised consumer) into the sibling dev-only
19150        // closure and this pin refuses that regression.
19151        let src = Caixa::template("host");
19152        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19153        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
19154        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
19155        let dep = Dep {
19156            nome: "caixa-teia".to_string(),
19157            versao: "^0.1".to_string(),
19158            fonte: None,
19159            opcional: false,
19160            caracteristicas: Vec::new(),
19161        };
19162        caixa
19163            .push_dep(crate::dep::DepList::Prod, dep.clone())
19164            .expect("push into :deps succeeds");
19165        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
19166        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
19167        assert_eq!(
19168            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
19169            "caixa-teia"
19170        );
19171    }
19172
19173    #[test]
19174    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
19175        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
19176        // [`Caixa::deps_of`] must project onto the dev-only-closure
19177        // `:deps-dev` slot, element-equal and length-equal to the
19178        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
19179        // future regression that inverted the two arms would silently
19180        // route every dev-list walker onto the runtime closure and this
19181        // pin catches it before the drift ships.
19182        let src = Caixa::template("host");
19183        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19184        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
19185        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
19186        let dep = Dep {
19187            nome: "tatara-check".to_string(),
19188            versao: "*".to_string(),
19189            fonte: None,
19190            opcional: false,
19191            caracteristicas: Vec::new(),
19192        };
19193        caixa
19194            .push_dep(crate::dep::DepList::Dev, dep)
19195            .expect("push into :deps-dev succeeds");
19196        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
19197        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
19198        assert_eq!(
19199            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
19200            "tatara-check"
19201        );
19202    }
19203
19204    #[test]
19205    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
19206        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
19207        // [`Caixa::deps_of`] must land on the same two-slot partition the
19208        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
19209        // expose — the canonical dispatch a future per-axis-parametrised
19210        // walker (a future `feira app graph` per-list dep summary, a
19211        // future M4 per-cluster dev-closure-audit overlay the CR
19212        // materializer resolves per-CR) reads through. Prior to the
19213        // lift the two-block iteration lived open-coded at every walker,
19214        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
19215        // §I) would have had to grow a third block at every consumer.
19216        // A regression that dropped the `Dev` arm from `ALL` would flip
19217        // the collected pairs to `[(":deps", &[])]` alone and this pin
19218        // refuses that shape.
19219        let src = Caixa::template("host");
19220        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19221        let prod_dep = Dep {
19222            nome: "caixa-teia".to_string(),
19223            versao: "^0.1".to_string(),
19224            fonte: None,
19225            opcional: false,
19226            caracteristicas: Vec::new(),
19227        };
19228        let dev_dep = Dep {
19229            nome: "tatara-check".to_string(),
19230            versao: "*".to_string(),
19231            fonte: None,
19232            opcional: false,
19233            caracteristicas: Vec::new(),
19234        };
19235        caixa
19236            .push_dep(crate::dep::DepList::Prod, prod_dep)
19237            .expect("push into :deps succeeds");
19238        caixa
19239            .push_dep(crate::dep::DepList::Dev, dev_dep)
19240            .expect("push into :deps-dev succeeds");
19241        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
19242            .iter()
19243            .map(|&list| {
19244                let slice = caixa.deps_of(list);
19245                (list.as_str(), slice.len(), slice[0].nome())
19246            })
19247            .collect();
19248        assert_eq!(
19249            collected,
19250            vec![
19251                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
19252                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
19253            ]
19254        );
19255    }
19256
19257    #[test]
19258    fn caixa_deps_of_is_const_fn() {
19259        // Fail-before-pass-after pin on [`Caixa::deps_of`]'s
19260        // `const`-eval-surface posture. The typed-dispatch read
19261        // accessor forwards through the sibling `pub const fn`
19262        // [`Caixa::deps`] / [`Caixa::deps_dev`] per-slot slice
19263        // accessors on the two [`crate::dep::DepList`] enum arms —
19264        // every operator in the body is already `const`-callable
19265        // (`DepList` is a plain `#[derive(Copy)]` closed-set
19266        // discriminator so the `match` arms are const-evaluable, and
19267        // each arm dispatches through the sibling `pub const fn`
19268        // slice accessor). Any future accidental downgrade to
19269        // non-`const` fails the `deps_of_via_const_fn` wrapper below
19270        // at caixa-core build time with E0015 (`cannot call non-const
19271        // method`), strictly stronger than a runtime `assert!` and
19272        // side-stepping the destructor-in-const restriction the
19273        // `Caixa` fixture's owning `String` / `Vec<Dep>` carriers
19274        // rule out on the direct-`const _: () = assert!(...)`
19275        // residence.
19276        //
19277        // Peer of the sibling outer-`Caixa` accessor family pins
19278        // ([`caixa_outer_string_slice_return_accessor_family_is_const_fn`]
19279        // on the `&[String]` universal-axis surface,
19280        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
19281        // on the outer `&[T]` composite-slice surface,
19282        // [`caixa_outer_option_composite_reference_return_accessor_family_is_const_fn`]
19283        // on the outer `Option<&Composite>` surface) — this pin
19284        // extends the `const`-eval-surface discipline onto the outer-
19285        // `Caixa` typed-dispatch read surface on the [`DepList`]-keyed
19286        // dep-list axis, closing the outer-`Caixa` accessor family's
19287        // last unlifted `pub fn` on the read side.
19288        const fn deps_of_via_const_fn(c: &Caixa, list: crate::dep::DepList) -> &[Dep] {
19289            c.deps_of(list)
19290        }
19291        let src = Caixa::template("host");
19292        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19293        // Empty-list arm: both `Prod` and `Dev` degenerate to the
19294        // empty slice with no silent `None` collapse — the
19295        // `#[serde(default)]` `Vec::new()` fold every `defcaixa` form
19296        // that omits the slot lands on.
19297        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod).is_empty());
19298        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev).is_empty());
19299        assert_eq!(
19300            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
19301            caixa.deps()
19302        );
19303        assert_eq!(
19304            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
19305            caixa.deps_dev()
19306        );
19307        // Populated arms: each list carries its own entry, and the
19308        // wrapper / direct dispatches agree byte-for-byte on the
19309        // slice-view under both non-empty arms.
19310        let prod_dep = Dep {
19311            nome: "caixa-teia".to_string(),
19312            versao: "^0.1".to_string(),
19313            fonte: None,
19314            opcional: false,
19315            caracteristicas: Vec::new(),
19316        };
19317        let dev_dep = Dep {
19318            nome: "tatara-check".to_string(),
19319            versao: "*".to_string(),
19320            fonte: None,
19321            opcional: false,
19322            caracteristicas: Vec::new(),
19323        };
19324        caixa
19325            .push_dep(crate::dep::DepList::Prod, prod_dep)
19326            .expect("push into :deps succeeds");
19327        caixa
19328            .push_dep(crate::dep::DepList::Dev, dev_dep)
19329            .expect("push into :deps-dev succeeds");
19330        assert_eq!(
19331            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
19332            caixa.deps()
19333        );
19334        assert_eq!(
19335            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
19336            caixa.deps_dev()
19337        );
19338        assert_eq!(
19339            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod)[0].nome(),
19340            "caixa-teia"
19341        );
19342        assert_eq!(
19343            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev)[0].nome(),
19344            "tatara-check"
19345        );
19346    }
19347
19348    #[test]
19349    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
19350        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
19351        // must route its per-list [`crate::render::insert_first_seen`]
19352        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
19353        // rather than the pre-lift open-coded two-block iteration over
19354        // `self.deps()` + `self.deps_dev()`. A regression that dropped
19355        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
19356        // stop refusing within-list dups on the sibling arm; a
19357        // regression that flipped the arm-to-list-key mapping
19358        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
19359        // diagnostic surface. Both drifts surface here through a paired
19360        // duplicate-name refusal per arm plus an offending-list-key
19361        // check on the emitted [`DepError::DuplicateNome`] carrier.
19362        for &list in crate::dep::DepList::ALL {
19363            let src = Caixa::template("host");
19364            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19365            let dup = Dep {
19366                nome: "twin".to_string(),
19367                versao: "^0.1".to_string(),
19368                fonte: None,
19369                opcional: false,
19370                caracteristicas: Vec::new(),
19371            };
19372            match list {
19373                crate::dep::DepList::Prod => {
19374                    caixa.deps.push(dup.clone());
19375                    caixa.deps.push(dup);
19376                }
19377                crate::dep::DepList::Dev => {
19378                    caixa.deps_dev.push(dup.clone());
19379                    caixa.deps_dev.push(dup);
19380                }
19381            }
19382            let err = caixa
19383                .validate_deps()
19384                .expect_err("within-list duplicate :nome must refuse");
19385            assert_eq!(
19386                err,
19387                DepError::DuplicateNome {
19388                    nome: "twin".to_string(),
19389                    list: list.as_str(),
19390                },
19391                "validate_deps on {list} arm must emit \
19392                 DepError::DuplicateNome carrying the arm's own \
19393                 as_str() diagnostic — the arm-to-list-key mapping \
19394                 flowed through DepList::ALL + Caixa::deps_of"
19395            );
19396        }
19397    }
19398
19399    #[test]
19400    fn caixa_licenca_default_pins_canonical_mit_byte() {
19401        // Bridge-arm pin: [`CAIXA_LICENCA_DEFAULT`] resolves to the
19402        // canonical SPDX-`"MIT"` byte today, the same license expression
19403        // every peer substrate-side consumer of the author-omitted
19404        // `:licenca` slot ([`caixa-helm`]'s `build_readme` fallback arm at
19405        // `caixa-helm/src/lib.rs`, the future M4
19406        // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR
19407        // `Chart.yaml annotations["artifacthub.io/license"]` emitter this
19408        // crate's [`Caixa::validate_licenca`] docstring roadmap already
19409        // names as the second consumer) fills into its per-consumer
19410        // README/annotation emit site. Pin the literal here (peer with the
19411        // [`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`] /
19412        // [`crate::version::DEFAULT_GIT_REMOTE`] /
19413        // [`crate::version::DEFAULT_PLEME_GIT_ORG`] canonical-literal pins
19414        // on the sibling lifted-constant surfaces) so a future
19415        // substrate-side license-fallback rebrand surfaces here as a
19416        // coordinated edit-point: the sibling caixa-helm
19417        // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
19418        // pinning test already pins the equality at the renderer-emit
19419        // axis; this pin closes the second coordinate of the pair by
19420        // anchoring the lifted constant's current byte to the canonical
19421        // CAIXA-SDLC §I license scaffold's documented shape.
19422        assert_eq!(CAIXA_LICENCA_DEFAULT, "MIT");
19423    }
19424
19425    // ── Caixa::validate_upgrade_from — compound per-Caixa entry gate on ──
19426    // ── the M2 `:upgrade-from` slot: folds the three top-level        ──
19427    // ── `crate::upgrade` validators (per-entry + cross-entry           ──
19428    // ── duplicate-`:from`, cross-slot `:from < :versao` precedence,   ──
19429    // ── cross-slot `:state-change` ↔ `:on-state-change` composition)  ──
19430    // ── onto one substrate primitive. Byte-for-byte equivalent to the ──
19431    // ── pre-fold three-block cascade at                               ──
19432    // ── `crate::layout::StandardLayout::verify` under the same        ──
19433    // ── canonical dispatch order.                                     ──
19434
19435    #[test]
19436    fn validate_upgrade_from_folds_per_entry_arm_matches_gate() {
19437        // Fail-before-pass-after per-arm equivalence pin on the
19438        // per-entry + cross-entry axis: a fixture whose `:upgrade-from`
19439        // carries a per-entry-invalid `:from` (git-tag shape `"v0.1.0"`,
19440        // which `semver::Version::parse` rejects) surfaces the same
19441        // [`crate::UpgradeError`] through the compound gate
19442        // [`Caixa::validate_upgrade_from`] and the standalone per-entry
19443        // gate [`crate::upgrade::validate_upgrade_from`] on the same
19444        // [`Caixa::upgrade_from`] slice. Pins the fold — a silent
19445        // regression that de-folded the per-entry arm would surface here
19446        // as a mismatch between the two dispatches. Sibling in shape to
19447        // the peer per-slot-≡-standalone equivalence pins the
19448        // [`crate::AplicacaoSpec::validate_contratos`] /
19449        // [`crate::MeshPolicy::validate`] /
19450        // [`crate::SupervisorSpec::validate_children`] compound gates
19451        // each carry on their axes.
19452        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19453        c.upgrade_from = vec![crate::UpgradeFromEntry {
19454            from: "v0.1.0".into(),
19455            instructions: vec![crate::UpgradeInstruction::Restart],
19456        }];
19457        let via_method = c.validate_upgrade_from().unwrap_err();
19458        let via_standalone = crate::upgrade::validate_upgrade_from(c.upgrade_from()).unwrap_err();
19459        assert_eq!(
19460            via_method, via_standalone,
19461            "Caixa::validate_upgrade_from must surface the per-entry \
19462             axis's diagnostic byte-equal to the standalone \
19463             `crate::upgrade::validate_upgrade_from` on the same \
19464             upgrade_from() slice"
19465        );
19466        assert!(
19467            matches!(
19468                via_method,
19469                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.1.0"
19470            ),
19471            "expected FromInvalid on the git-tag-shape `:from`, got {via_method:?}"
19472        );
19473    }
19474
19475    #[test]
19476    fn validate_upgrade_from_folds_versao_arm_matches_gate() {
19477        // Per-arm equivalence pin on the cross-slot `:from ↔ :versao`
19478        // precedence axis: a fixture with a well-formed `:from` (so the
19479        // per-entry arm passes) whose parsed semver is >= the caixa's
19480        // `:versao` under SemVer-2 precedence surfaces the same
19481        // [`crate::UpgradeError::FromNotBeforeVersao`] through both the
19482        // compound gate and the standalone
19483        // [`crate::upgrade::validate_upgrade_from_against_versao`] gate
19484        // keyed off the same `(upgrade_from, versao)` pair. Pins the
19485        // fold's second arm — reaching this arm through the compound
19486        // gate requires the per-entry arm to pass first, which itself
19487        // pins the per-arm cross-arm ordering.
19488        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19489        c.versao = "0.1.0".into();
19490        c.upgrade_from = vec![crate::UpgradeFromEntry {
19491            from: "0.2.0".into(),
19492            instructions: vec![crate::UpgradeInstruction::Restart],
19493        }];
19494        let via_method = c.validate_upgrade_from().unwrap_err();
19495        let via_standalone =
19496            crate::upgrade::validate_upgrade_from_against_versao(c.upgrade_from(), c.versao())
19497                .unwrap_err();
19498        assert_eq!(
19499            via_method, via_standalone,
19500            "Caixa::validate_upgrade_from must surface the \
19501             `:from >= :versao` diagnostic byte-equal to the standalone \
19502             `crate::upgrade::validate_upgrade_from_against_versao` on \
19503             the same (upgrade_from, versao) pair"
19504        );
19505        assert!(
19506            matches!(
19507                via_method,
19508                crate::UpgradeError::FromNotBeforeVersao { ref from, ref versao }
19509                    if from == "0.2.0" && versao == "0.1.0"
19510            ),
19511            "expected FromNotBeforeVersao carrying the offending pair, got {via_method:?}"
19512        );
19513    }
19514
19515    #[test]
19516    fn validate_upgrade_from_folds_behavior_arm_matches_gate() {
19517        // Per-arm equivalence pin on the cross-slot `:state-change ↔
19518        // :on-state-change` composition axis: a fixture with a
19519        // well-formed `:from` strictly less than `:versao` (so the
19520        // per-entry and versao arms both pass) whose `:instructions`
19521        // list carries a `(:state-change …)` instruction with no
19522        // `:behavior :on-state-change` callback declared surfaces the
19523        // same [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
19524        // through both the compound gate and the standalone
19525        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
19526        // gate keyed off the same `(upgrade_from, behavior)` pair.
19527        // Reaching this arm through the compound gate requires both
19528        // prior arms to pass first — the ordering pin below pins the
19529        // per-arm dispatch order explicitly.
19530        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19531        c.versao = "0.2.0".into();
19532        c.behavior = None;
19533        c.upgrade_from = vec![crate::UpgradeFromEntry {
19534            from: "0.1.0".into(),
19535            instructions: vec![
19536                crate::UpgradeInstruction::LoadModule {
19537                    module: "demo".into(),
19538                },
19539                crate::UpgradeInstruction::StateChange {
19540                    script: std::path::PathBuf::from("lib/m.lisp"),
19541                },
19542                crate::UpgradeInstruction::SoftPurge {
19543                    module: "demo-old".into(),
19544                },
19545            ],
19546        }];
19547        let via_method = c.validate_upgrade_from().unwrap_err();
19548        let via_standalone =
19549            crate::upgrade::validate_upgrade_from_against_behavior(c.upgrade_from(), c.behavior())
19550                .unwrap_err();
19551        assert_eq!(
19552            via_method, via_standalone,
19553            "Caixa::validate_upgrade_from must surface the \
19554             `:state-change` ↔ `:on-state-change` composition \
19555             diagnostic byte-equal to the standalone \
19556             `crate::upgrade::validate_upgrade_from_against_behavior` \
19557             on the same (upgrade_from, behavior) pair"
19558        );
19559        assert!(
19560            matches!(
19561                via_method,
19562                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback {
19563                    ref from,
19564                    ref script,
19565                } if from == "0.1.0" && script == &std::path::PathBuf::from("lib/m.lisp")
19566            ),
19567            "expected StateChangeWithoutOnStateChangeCallback carrying \
19568             the offending (from, script) pair, got {via_method:?}"
19569        );
19570    }
19571
19572    #[test]
19573    fn validate_upgrade_from_per_entry_arm_fires_before_versao_arm() {
19574        // Cross-arm ordering pin between the first two arms of the
19575        // fold: a fixture carrying BOTH a per-entry-invalid `:from`
19576        // (`"v0.0.5"` — git-tag shape rejected by
19577        // [`crate::upgrade::validate_upgrade_from`]) AND a would-be
19578        // versao-precedence violation on a second entry (`"0.2.0" >=
19579        // :versao "0.1.0"`) surfaces the per-entry diagnostic first
19580        // through the compound gate. Sanity assertion: the second
19581        // entry alone under the same `:versao` trips the versao arm
19582        // on its own via the standalone
19583        // [`crate::upgrade::validate_upgrade_from_against_versao`], so
19584        // the per-entry-first surfacing is a real ordering property,
19585        // not a case where the versao arm silently accepts the
19586        // fixture. Pins the pre-fold layout wire-up's canonical
19587        // dispatch order (per-entry → versao → behavior) as a
19588        // property of the substrate primitive rather than a
19589        // convention of the layout call site.
19590        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19591        c.versao = "0.1.0".into();
19592        c.upgrade_from = vec![
19593            crate::UpgradeFromEntry {
19594                from: "v0.0.5".into(),
19595                instructions: vec![crate::UpgradeInstruction::Restart],
19596            },
19597            crate::UpgradeFromEntry {
19598                from: "0.2.0".into(),
19599                instructions: vec![crate::UpgradeInstruction::Restart],
19600            },
19601        ];
19602        let err = c.validate_upgrade_from().unwrap_err();
19603        assert!(
19604            matches!(
19605                err,
19606                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.0.5"
19607            ),
19608            "per-entry arm must fire before versao arm — expected \
19609             FromInvalid on `v0.0.5`, got {err:?}"
19610        );
19611        // Sanity: the versao-violating second entry alone under the
19612        // same `:versao` trips the versao arm on its own — proves the
19613        // per-entry-first surfacing above is a real ordering property.
19614        let sanity = crate::upgrade::validate_upgrade_from_against_versao(
19615            &[crate::UpgradeFromEntry {
19616                from: "0.2.0".into(),
19617                instructions: vec![crate::UpgradeInstruction::Restart],
19618            }],
19619            "0.1.0",
19620        )
19621        .unwrap_err();
19622        assert!(
19623            matches!(sanity, crate::UpgradeError::FromNotBeforeVersao { .. }),
19624            "sanity: the versao-violating fixture alone must trip the \
19625             versao arm — got {sanity:?}"
19626        );
19627    }
19628
19629    #[test]
19630    fn validate_upgrade_from_versao_arm_fires_before_behavior_arm() {
19631        // Cross-arm ordering pin between the second and third arms of
19632        // the fold: a fixture carrying BOTH a versao-precedence
19633        // violation (`:from "0.2.0" >= :versao "0.1.0"`) AND a
19634        // would-be missing-callback violation (a `(:state-change …)`
19635        // instruction with no `:behavior :on-state-change`) surfaces
19636        // the versao diagnostic first through the compound gate.
19637        // Sanity assertion: the missing-callback fixture alone (with
19638        // the versao-precedence violation removed by bumping
19639        // `:versao` past `:from`) trips the behavior arm on its own
19640        // via the standalone
19641        // [`crate::upgrade::validate_upgrade_from_against_behavior`],
19642        // so the versao-first surfacing is a real ordering property,
19643        // not a case where the behavior arm silently accepts the
19644        // fixture.
19645        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19646        c.versao = "0.1.0".into();
19647        c.behavior = None;
19648        c.upgrade_from = vec![crate::UpgradeFromEntry {
19649            from: "0.2.0".into(),
19650            instructions: vec![
19651                crate::UpgradeInstruction::LoadModule {
19652                    module: "demo".into(),
19653                },
19654                crate::UpgradeInstruction::StateChange {
19655                    script: std::path::PathBuf::from("lib/m.lisp"),
19656                },
19657            ],
19658        }];
19659        let err = c.validate_upgrade_from().unwrap_err();
19660        assert!(
19661            matches!(
19662                err,
19663                crate::UpgradeError::FromNotBeforeVersao { ref from, .. } if from == "0.2.0"
19664            ),
19665            "versao arm must fire before behavior arm — expected \
19666             FromNotBeforeVersao on `0.2.0`, got {err:?}"
19667        );
19668        // Sanity: the same instructions under a `:versao` that
19669        // accepts the `:from` (so the versao arm passes) trips the
19670        // behavior arm — proves the versao-first surfacing above is a
19671        // real ordering property.
19672        let sanity = crate::upgrade::validate_upgrade_from_against_behavior(
19673            &[crate::UpgradeFromEntry {
19674                from: "0.2.0".into(),
19675                instructions: vec![
19676                    crate::UpgradeInstruction::LoadModule {
19677                        module: "demo".into(),
19678                    },
19679                    crate::UpgradeInstruction::StateChange {
19680                        script: std::path::PathBuf::from("lib/m.lisp"),
19681                    },
19682                ],
19683            }],
19684            None,
19685        )
19686        .unwrap_err();
19687        assert!(
19688            matches!(
19689                sanity,
19690                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback { .. }
19691            ),
19692            "sanity: the missing-callback fixture alone must trip the \
19693             behavior arm — got {sanity:?}"
19694        );
19695    }
19696
19697    #[test]
19698    fn validate_upgrade_from_accepts_clean_fixture() {
19699        // Positive control: a well-formed `:upgrade-from` (single entry
19700        // with `:from` strictly less than `:versao`, no
19701        // `:state-change` instruction so the behavior arm is vacuous)
19702        // passes the compound gate cleanly. A future tightening of any
19703        // one arm's accepted set surfaces here as a test failure
19704        // first. Mirrors the peer `validate_versao_accepts_canonical_forms`
19705        // positive-control posture on the sibling per-Caixa gate.
19706        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19707        c.versao = "0.2.0".into();
19708        c.upgrade_from = vec![crate::UpgradeFromEntry {
19709            from: "0.1.0".into(),
19710            instructions: vec![crate::UpgradeInstruction::Restart],
19711        }];
19712        c.validate_upgrade_from()
19713            .expect("clean fixture must pass the compound `:upgrade-from` gate");
19714    }
19715
19716    #[test]
19717    fn validate_upgrade_from_accepts_empty_upgrade_from() {
19718        // Positive control on the empty-list arm: a caixa without any
19719        // `:upgrade-from` block (the default `Vec::new()`
19720        // `#[serde(default)]` folds an omitted slot onto) passes the
19721        // compound gate cleanly regardless of `:versao` or `:behavior`
19722        // — each of the three standalone validators is vacuous on the
19723        // empty entry list. Pins the identity element of the fold on
19724        // the empty-slot side.
19725        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19726        assert!(
19727            c.upgrade_from().is_empty(),
19728            "template caixa must carry an empty :upgrade-from — got {:?}",
19729            c.upgrade_from()
19730        );
19731        c.validate_upgrade_from()
19732            .expect("empty :upgrade-from must pass the compound gate cleanly");
19733    }
19734
19735    // ── Caixa::validate_limits — compound per-Caixa entry gate on   ──
19736    // ── the M2 `:limits` slot: folds the                            ──
19737    // ── [`crate::LimitsSpec::validate`] four-axis cascade on the    ──
19738    // ── present-slot arm and the `Option::None` identity element on ──
19739    // ── the absent-slot arm onto one substrate primitive.           ──
19740    // ── Byte-for-byte equivalent to the pre-fold                    ──
19741    // ── `if let Some(l) = caixa.limits() { l.validate() }`          ──
19742    // ── unwrap-and-dispatch pattern at                              ──
19743    // ── `crate::layout::StandardLayout::verify` (`layout.rs`).      ──
19744
19745    #[test]
19746    fn validate_limits_folds_arm_matches_gate() {
19747        // Fail-before-pass-after per-arm equivalence pin on the
19748        // present-slot arm: a fixture whose `:limits` carries a
19749        // zero-floor-violating `:fuel` (`Some(0)`, which
19750        // [`crate::LimitsSpec::validate`] rejects through
19751        // [`crate::LimitsError::FuelZero`]) surfaces the same
19752        // [`crate::LimitsError`] byte-equal through both the compound
19753        // gate [`Caixa::validate_limits`] and the standalone
19754        // [`crate::LimitsSpec::validate`] gate on the same `LimitsSpec`
19755        // value. Pins the fold — a silent regression that de-folded
19756        // the present-slot arm would surface here as a mismatch
19757        // between the two dispatches. Sibling in shape to the peer
19758        // per-arm equivalence pins the
19759        // [`crate::AplicacaoSpec::validate_contratos`] /
19760        // [`crate::MeshPolicy::validate`] /
19761        // [`crate::SupervisorSpec::validate_children`] /
19762        // [`Caixa::validate_upgrade_from`] compound gates each carry
19763        // on their axes.
19764        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19765        let l = crate::LimitsSpec {
19766            memory: None,
19767            fuel: Some(0),
19768            wall_clock: None,
19769            cpu: None,
19770        };
19771        c.limits = Some(l);
19772        let via_method = c.validate_limits().unwrap_err();
19773        let via_standalone = l.validate().unwrap_err();
19774        assert_eq!(
19775            via_method, via_standalone,
19776            "Caixa::validate_limits must surface the present-slot \
19777             arm's diagnostic byte-equal to the standalone \
19778             `LimitsSpec::validate` on the same `LimitsSpec` value"
19779        );
19780        assert!(
19781            matches!(via_method, crate::LimitsError::FuelZero),
19782            "expected FuelZero on the zero-floor-violating `:fuel`, \
19783             got {via_method:?}"
19784        );
19785    }
19786
19787    #[test]
19788    fn validate_limits_accepts_none() {
19789        // Positive control on the absent-slot arm (the fold's identity
19790        // element): a caixa without any `:limits` block (the
19791        // canonical "no bound declared — engine-default applies"
19792        // author shape [`crate::LimitsSpec::is_empty`]'s per-axis
19793        // `None` cascade reads, and the shape the [`Caixa::template`]
19794        // scaffold emits by construction) passes the compound gate
19795        // cleanly, regardless of any per-axis defect a subsequent
19796        // `Some(_)` binding would surface. Pins the identity element
19797        // of the fold on the absent-slot side, matching the peer
19798        // `validate_upgrade_from_accepts_empty_upgrade_from` positive-
19799        // control posture on the sibling M2 slot.
19800        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19801        assert!(
19802            c.limits().is_none(),
19803            "template caixa must carry an absent :limits — got {:?}",
19804            c.limits()
19805        );
19806        c.validate_limits()
19807            .expect("absent :limits must pass the compound gate cleanly");
19808    }
19809
19810    #[test]
19811    fn validate_limits_accepts_clean_fixture() {
19812        // Positive control on the present-slot arm: a caixa whose
19813        // `:limits` is `Some(LimitsSpec::default())` (all four axes
19814        // `None` — every axis absent under the outer `Some(_)`
19815        // binding, so every present-slot arm on
19816        // [`crate::LimitsSpec::validate`] is vacuous) passes the
19817        // compound gate cleanly. A future tightening of any one axis
19818        // that surfaces a diagnostic on the all-`None` `LimitsSpec`
19819        // would land here as a test failure first. Pins the
19820        // present-slot arm's accept-shape on the canonical
19821        // "declared-but-empty" author fixture the
19822        // `limits_round_trip_via_json` peer already round-trips
19823        // (`caixa-core/src/manifest.rs:6971`).
19824        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19825        c.limits = Some(crate::LimitsSpec::default());
19826        c.validate_limits()
19827            .expect("Some(LimitsSpec::default()) must pass the compound gate cleanly");
19828    }
19829
19830    // ── Caixa::validate_behavior — compound per-Caixa entry gate on ──
19831    // ── the M2 `:behavior` slot's pure value-shape surface: folds   ──
19832    // ── the [`crate::BehaviorSpec::validate`] six-slot cascade on   ──
19833    // ── the present-slot arm and the `Option::None` identity        ──
19834    // ── element on the absent-slot arm onto one substrate primitive.──
19835    // ── Byte-for-byte equivalent to the pre-fold                    ──
19836    // ── `if let Some(b) = caixa.behavior() { b.validate() }`        ──
19837    // ── unwrap-and-dispatch pattern at                              ──
19838    // ── `crate::layout::StandardLayout::verify` (`layout.rs`). The  ──
19839    // ── on-disk callback-path existence walk stays open-coded at    ──
19840    // ── the layout altitude because it needs the                    ──
19841    // ── [`crate::layout::LayoutInvariants::exists`] filesystem       ──
19842    // ── oracle the pure typed-shape surface has no reference to —   ──
19843    // ── mirror of the peer M2 `:upgrade-from` per-instruction       ──
19844    // ── script-path existence probe that stayed at the layout       ──
19845    // ── altitude after the [`Caixa::validate_upgrade_from`] lift    ──
19846    // ── (d6801df) for the same reason.                              ──
19847
19848    #[test]
19849    fn validate_behavior_folds_arm_matches_gate() {
19850        // Fail-before-pass-after per-arm equivalence pin on the
19851        // present-slot arm: a fixture whose `:behavior` carries an
19852        // absolute-path `:on-init` (`"/etc/passwd"`, which
19853        // [`crate::BehaviorSpec::validate`] rejects through
19854        // [`crate::BehaviorError::AbsolutePath`]) surfaces the same
19855        // [`crate::BehaviorError`] byte-equal through both the
19856        // compound gate [`Caixa::validate_behavior`] and the standalone
19857        // [`crate::BehaviorSpec::validate`] gate on the same
19858        // `BehaviorSpec` value. Pins the fold — a silent regression
19859        // that de-folded the present-slot arm would surface here as a
19860        // mismatch between the two dispatches. Sibling in shape to the
19861        // peer per-arm equivalence pins the
19862        // [`Caixa::validate_limits`] (baa4688),
19863        // [`Caixa::validate_upgrade_from`] (d6801df),
19864        // [`crate::MeshPolicy::validate`],
19865        // [`crate::AplicacaoSpec::validate_contratos`], and
19866        // [`crate::SupervisorSpec::validate_children`] compound gates
19867        // each carry on their axes.
19868        use crate::BehaviorSpec;
19869        use std::path::PathBuf;
19870        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19871        let b = BehaviorSpec {
19872            on_init: Some(PathBuf::from("/etc/passwd")),
19873            ..Default::default()
19874        };
19875        c.behavior = Some(b.clone());
19876        let via_method = c.validate_behavior().unwrap_err();
19877        let via_standalone = b.validate().unwrap_err();
19878        assert_eq!(
19879            via_method, via_standalone,
19880            "Caixa::validate_behavior must surface the present-slot \
19881             arm's diagnostic byte-equal to the standalone \
19882             `BehaviorSpec::validate` on the same `BehaviorSpec` value"
19883        );
19884        assert!(
19885            matches!(via_method, crate::BehaviorError::AbsolutePath { .. }),
19886            "expected AbsolutePath on the absolute `:on-init` path, \
19887             got {via_method:?}"
19888        );
19889    }
19890
19891    #[test]
19892    fn validate_behavior_accepts_none() {
19893        // Positive control on the absent-slot arm (the fold's identity
19894        // element): a caixa without any `:behavior` block (the
19895        // canonical "no callback declared — the runtime falls back to
19896        // the wasm-engine's default per arm" author shape
19897        // [`crate::BehaviorSpec::is_empty`]'s per-slot `None` cascade
19898        // reads, and the shape the [`Caixa::template`] scaffold emits
19899        // by construction) passes the compound gate cleanly,
19900        // regardless of any per-slot defect a subsequent `Some(_)`
19901        // binding would surface. Pins the identity element of the fold
19902        // on the absent-slot side, matching the peer
19903        // `validate_limits_accepts_none` (baa4688) and
19904        // `validate_upgrade_from_accepts_empty_upgrade_from` (d6801df)
19905        // positive-control postures on the sibling M2 slots.
19906        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19907        assert!(
19908            c.behavior().is_none(),
19909            "template caixa must carry an absent :behavior — got {:?}",
19910            c.behavior()
19911        );
19912        c.validate_behavior()
19913            .expect("absent :behavior must pass the compound gate cleanly");
19914    }
19915
19916    #[test]
19917    fn validate_behavior_accepts_clean_fixture() {
19918        // Positive control on the present-slot arm: a caixa whose
19919        // `:behavior` is `Some(BehaviorSpec::default())` (all six
19920        // slots `None` — every slot absent under the outer `Some(_)`
19921        // binding, so every present-slot arm on
19922        // [`crate::BehaviorSpec::validate`] is vacuous) passes the
19923        // compound gate cleanly. A future tightening of any one arm
19924        // that surfaces a diagnostic on the all-`None` `BehaviorSpec`
19925        // would land here as a test failure first. Pins the
19926        // present-slot arm's accept-shape on the canonical
19927        // "declared-but-empty" author fixture the sibling
19928        // `empty_behavior_round_trip` peer already round-trips
19929        // (`caixa-core/src/behavior.rs` tests). Mirror of the peer
19930        // `validate_limits_accepts_clean_fixture` (baa4688)
19931        // positive-control posture on the sibling M2 `:limits` slot.
19932        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19933        c.behavior = Some(crate::BehaviorSpec::default());
19934        c.validate_behavior()
19935            .expect("Some(BehaviorSpec::default()) must pass the compound gate cleanly");
19936    }
19937
19938    // ── Caixa::validate_deps — compound per-Caixa entry gate on the ──
19939    // ── dep-graph axis: folds the two standalone validators         ──
19940    // ── (per-entry + within-list duplicate walk that this method    ──
19941    // ── opened on, cross-slot self-edge via                         ──
19942    // ── `crate::dep::validate_no_self_dep`) onto one substrate      ──
19943    // ── primitive. Byte-for-byte equivalent to the pre-fold         ──
19944    // ── two-block cascade at                                        ──
19945    // ── `crate::layout::StandardLayout::verify` under the same      ──
19946    // ── canonical dispatch order (per-entry → self-edge).           ──
19947
19948    #[test]
19949    fn validate_deps_folds_per_entry_arm_matches_gate() {
19950        // Fail-before-pass-after per-arm equivalence pin on the
19951        // per-entry + within-list duplicate axis: a fixture whose
19952        // `:deps` carries a per-entry-invalid `:versao` (`"^bad"`,
19953        // which [`crate::parse_requirement`] rejects) surfaces the
19954        // same [`crate::DepError`] through the compound gate
19955        // [`Caixa::validate_deps`] and the standalone per-entry walk
19956        // ([`Dep::validate`]) on the offending entry. Pins the
19957        // fold — a silent regression that de-folded the per-entry arm
19958        // would surface here as a mismatch between the two
19959        // dispatches. Sibling in shape to the peer
19960        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
19961        // per-arm equivalence pin (d6801df) on the M2
19962        // `:upgrade-from` compound gate's per-entry arm, extended
19963        // here onto the universal-axis `:deps` compound gate's
19964        // per-entry arm.
19965        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19966        c.deps = vec![Dep::simple("d", "^bad")];
19967        let via_method = c.validate_deps().unwrap_err();
19968        let via_standalone = c.deps()[0].validate().unwrap_err();
19969        assert_eq!(
19970            via_method, via_standalone,
19971            "Caixa::validate_deps must surface the per-entry arm's \
19972             diagnostic byte-equal to the standalone \
19973             `Dep::validate` on the same offending entry",
19974        );
19975        assert!(
19976            matches!(
19977                via_method,
19978                DepError::VersaoInvalid { ref nome, .. } if nome == "d"
19979            ),
19980            "expected VersaoInvalid on the malformed :versao, got {via_method:?}",
19981        );
19982    }
19983
19984    #[test]
19985    fn validate_deps_folds_self_edge_arm_matches_gate() {
19986        // Per-arm equivalence pin on the cross-slot self-edge axis:
19987        // a fixture whose `:deps` lists the caixa's own `:nome`
19988        // (a self-dep, which
19989        // [`crate::dep::validate_no_self_dep`] rejects as a
19990        // structurally-invalid one-node cycle in the lacre closure's
19991        // dep-graph) surfaces the same [`crate::DepError::DepIsSelf`]
19992        // through both the compound gate and the standalone
19993        // [`crate::dep::validate_no_self_dep`] gate keyed off the
19994        // same `(deps, deps_dev, nome)` triple. Pins the fold's
19995        // second arm — reaching this arm through the compound gate
19996        // requires the per-entry + within-list duplicate walk to
19997        // pass first, which itself pins one cross-arm ordering step.
19998        // Sibling in shape to the peer
19999        // `validate_upgrade_from_folds_versao_arm_matches_gate` /
20000        // `_folds_behavior_arm_matches_gate` cross-slot equivalence
20001        // pins (d6801df) on the M2 `:upgrade-from` compound gate's
20002        // cross-slot arms.
20003        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20004        c.deps = vec![Dep::simple("demo", "^0.1")];
20005        let via_method = c.validate_deps().unwrap_err();
20006        let via_standalone =
20007            crate::dep::validate_no_self_dep(c.deps(), c.deps_dev(), c.nome()).unwrap_err();
20008        assert_eq!(
20009            via_method, via_standalone,
20010            "Caixa::validate_deps must surface the cross-slot \
20011             self-edge diagnostic byte-equal to the standalone \
20012             `crate::dep::validate_no_self_dep` on the same \
20013             (deps, deps_dev, nome) triple",
20014        );
20015        assert!(
20016            matches!(
20017                via_method,
20018                DepError::DepIsSelf { ref nome, list }
20019                    if nome == "demo" && list == crate::render::DEP_AUTHOR_KEY_DEPS
20020            ),
20021            "expected DepIsSelf carrying (nome=\"demo\", list=\":deps\"), got {via_method:?}",
20022        );
20023    }
20024
20025    #[test]
20026    fn validate_deps_per_entry_arm_fires_before_self_edge_arm() {
20027        // Cross-arm ordering pin between the two arms of the fold:
20028        // a fixture carrying BOTH a per-entry-invalid `:versao`
20029        // (`"^bad"` — [`crate::parse_requirement`] rejects the
20030        // requirement grammar) on a non-self-dep entry AND a
20031        // would-be self-edge violation on a second entry (the
20032        // caixa's own `:nome` "demo") surfaces the per-entry
20033        // diagnostic first through the compound gate. Sanity
20034        // assertion: the second entry alone under the same parent
20035        // `:nome` trips the self-edge arm on its own via the
20036        // standalone [`crate::dep::validate_no_self_dep`], so the
20037        // per-entry-first surfacing is a real ordering property,
20038        // not a case where the self-edge arm silently accepts the
20039        // fixture. Pins the pre-fold layout wire-up's canonical
20040        // dispatch order (per-entry + within-list duplicate →
20041        // self-edge) as a property of the substrate primitive
20042        // rather than a convention of the layout call site. Sibling
20043        // in shape to
20044        // `validate_upgrade_from_per_entry_arm_fires_before_versao_arm`
20045        // (d6801df) on the M2 `:upgrade-from` compound gate's
20046        // per-arm ordering property.
20047        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20048        c.deps = vec![
20049            Dep::simple("orquestra", "^bad"),
20050            Dep::simple("demo", "^0.1"),
20051        ];
20052        let err = c.validate_deps().unwrap_err();
20053        assert!(
20054            matches!(
20055                err,
20056                DepError::VersaoInvalid { ref nome, .. } if nome == "orquestra"
20057            ),
20058            "per-entry arm must fire before self-edge arm — expected \
20059             VersaoInvalid on \"orquestra\", got {err:?}",
20060        );
20061        // Sanity: the self-referential entry alone under the same
20062        // parent `:nome` trips the self-edge arm on its own — proves
20063        // the per-entry-first surfacing above is a real ordering
20064        // property, not a case where the self-edge arm silently
20065        // accepts the fixture.
20066        let sanity = crate::dep::validate_no_self_dep(&[Dep::simple("demo", "^0.1")], &[], "demo")
20067            .unwrap_err();
20068        assert!(
20069            matches!(sanity, DepError::DepIsSelf { ref nome, .. } if nome == "demo"),
20070            "sanity: the self-referential entry alone must trip the \
20071             self-edge arm — got {sanity:?}",
20072        );
20073    }
20074
20075    #[test]
20076    fn validate_deps_accepts_clean_fixture() {
20077        // Positive control: a well-formed dep-graph (one `:deps`
20078        // entry naming a non-self DNS-1123 nome + Cargo-shaped
20079        // requirement, one `:deps-dev` entry on a distinct non-self
20080        // nome) passes the compound gate cleanly. A future
20081        // tightening of either arm's accepted set surfaces here as
20082        // a test failure first. Mirrors the peer
20083        // `validate_upgrade_from_accepts_clean_fixture` positive-
20084        // control posture on the sibling per-Caixa compound gate.
20085        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20086        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
20087        c.deps_dev = vec![Dep::simple("caixa-lint", "^0.2")];
20088        c.validate_deps()
20089            .expect("clean fixture must pass the compound `:deps` gate");
20090    }
20091
20092    #[test]
20093    fn validate_deps_accepts_empty_deps_lists() {
20094        // Positive control on the empty-list arm: a caixa without
20095        // any `:deps` or `:deps-dev` entries (the default
20096        // `Vec::new()` `#[serde(default)]` folds an omitted slot
20097        // onto) passes the compound gate cleanly regardless of
20098        // `:nome` — both the per-entry walk and the self-edge walk
20099        // are vacuous on the empty entry list. Pins the identity
20100        // element of the fold on the empty-slot side, peer with the
20101        // `validate_upgrade_from_accepts_empty_upgrade_from` empty-
20102        // arm positive control (d6801df) on the sibling
20103        // `:upgrade-from` compound gate.
20104        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20105        assert!(
20106            c.deps().is_empty(),
20107            "template caixa must carry an empty :deps — got {:?}",
20108            c.deps(),
20109        );
20110        assert!(
20111            c.deps_dev().is_empty(),
20112            "template caixa must carry an empty :deps-dev — got {:?}",
20113            c.deps_dev(),
20114        );
20115        c.validate_deps()
20116            .expect("empty :deps / :deps-dev must pass the compound gate cleanly");
20117    }
20118
20119    // ── Caixa::validate_aplicacao_shape — compound per-Caixa gate ────────
20120
20121    /// Build a minimal well-formed Aplicacao fixture on top of the
20122    /// canonical template. Every arm of the compound gate then patches
20123    /// exactly one axis away from clean so its per-arm diagnostic
20124    /// surfaces without collateral noise from a peer slot.
20125    fn aplicacao_fixture(nome: &str) -> Caixa {
20126        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
20127        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
20128        c.kind = CaixaKind::Aplicacao;
20129        c.bibliotecas = vec![];
20130        c.membros = vec![
20131            Membro {
20132                caixa: "checkout".into(),
20133                versao: "^0.1".into(),
20134            },
20135            Membro {
20136                caixa: "cart".into(),
20137                versao: "^0.1".into(),
20138            },
20139        ];
20140        // `:placement` defaults to `Replicated` with an empty
20141        // `:clusters` list which
20142        // [`crate::AplicacaoSpec::validate_placement`] refuses; every
20143        // per-strategy variant needs at least one named cluster (per
20144        // MESH-COMPOSITION §II.1). Pin a single-cluster `SingleNode`
20145        // placement so the typed-shape cascade passes cleanly and the
20146        // per-arm fixtures below can each patch exactly one axis.
20147        c.placement = Some(Placement {
20148            estrategia: PlacementStrategy::SingleNode,
20149            clusters: vec!["rio".into()],
20150            shard_key: None,
20151            affinity: None,
20152        });
20153        c
20154    }
20155
20156    #[test]
20157    fn validate_aplicacao_shape_folds_view_arm_matches_gate() {
20158        // Fail-before-pass-after per-arm equivalence pin on the
20159        // typed-shape cascade arm: a fixture whose typed
20160        // [`crate::AplicacaoSpec`] view fails
20161        // [`crate::AplicacaoSpec::validate`] (here — empty `:membros`,
20162        // which [`crate::AplicacaoSpec::validate_membros`] rejects as
20163        // [`crate::AplicacaoError::NoMembros`] at the first per-slot
20164        // gate) surfaces the same [`crate::AplicacaoError`] diagnostic
20165        // through both the compound gate
20166        // [`Caixa::validate_aplicacao_shape`] and the standalone
20167        // [`crate::AplicacaoSpec::validate`] on the same folded view.
20168        // Pins the fold — a silent regression that de-folded the
20169        // typed-shape arm would surface here as a mismatch between the
20170        // two dispatches. Sibling in shape to the peer
20171        // `validate_deps_folds_per_entry_arm_matches_gate` (b5dd55e) /
20172        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
20173        // (d6801df) per-arm equivalence pins on the sibling per-slot
20174        // compound gates.
20175        let mut c = aplicacao_fixture("demo");
20176        c.membros = vec![];
20177        let via_method = c.validate_aplicacao_shape().unwrap_err();
20178        let via_standalone = c.aplicacao_view().unwrap().validate().unwrap_err();
20179        assert_eq!(
20180            via_method, via_standalone,
20181            "Caixa::validate_aplicacao_shape must surface the typed-\
20182             shape arm's diagnostic byte-equal to the standalone \
20183             `AplicacaoSpec::validate` on the same folded view",
20184        );
20185        assert!(
20186            matches!(via_method, crate::AplicacaoError::NoMembros),
20187            "expected NoMembros on the empty :membros, got {via_method:?}",
20188        );
20189    }
20190
20191    #[test]
20192    fn validate_aplicacao_shape_folds_self_membership_arm_matches_gate() {
20193        // Per-arm equivalence pin on the cross-slot self-edge axis: a
20194        // fixture whose `:membros` names the Aplicacao's own `:nome`
20195        // (which [`crate::aplicacao::validate_no_self_membership`]
20196        // rejects as [`crate::AplicacaoError::MembroIsSelfAplicacao`],
20197        // a one-node lacre-closure recursion in the Aplicacao's
20198        // mesh-graph) surfaces the same
20199        // [`crate::AplicacaoError::MembroIsSelfAplicacao`] through both
20200        // the compound gate and the standalone
20201        // [`crate::aplicacao::validate_no_self_membership`] keyed off
20202        // the same `(membros, nome)` pair. Pins the fold's second arm
20203        // — reaching this arm through the compound gate requires the
20204        // typed-shape cascade to pass first, which itself pins one
20205        // cross-arm ordering step. Sibling in shape to the peer
20206        // `validate_deps_folds_self_edge_arm_matches_gate` (b5dd55e)
20207        // cross-slot equivalence pin on the sibling per-slot compound
20208        // gate.
20209        use crate::aplicacao::Membro;
20210        let mut c = aplicacao_fixture("demo");
20211        c.membros = vec![Membro {
20212            caixa: "demo".into(),
20213            versao: "^0.1".into(),
20214        }];
20215        let via_method = c.validate_aplicacao_shape().unwrap_err();
20216        let via_standalone =
20217            crate::aplicacao::validate_no_self_membership(c.membros(), c.nome()).unwrap_err();
20218        assert_eq!(
20219            via_method, via_standalone,
20220            "Caixa::validate_aplicacao_shape must surface the cross-\
20221             slot self-edge diagnostic byte-equal to the standalone \
20222             `aplicacao::validate_no_self_membership` on the same \
20223             (membros, nome) pair",
20224        );
20225        assert!(
20226            matches!(
20227                via_method,
20228                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa } if caixa == "demo"
20229            ),
20230            "expected MembroIsSelfAplicacao carrying (caixa=\"demo\"), \
20231             got {via_method:?}",
20232        );
20233    }
20234
20235    #[test]
20236    fn validate_aplicacao_shape_view_arm_fires_before_self_membership_arm() {
20237        // Cross-arm ordering pin between the two arms of the fold: a
20238        // fixture carrying BOTH a typed-shape violation (a `:contratos`
20239        // edge whose `:para` is not a declared member — rejected by
20240        // [`crate::AplicacaoSpec::validate_contratos`] as
20241        // [`crate::AplicacaoError::ContratoMemberMissing`]) AND a
20242        // would-be self-edge violation (a `:membros` entry naming the
20243        // caixa's own `:nome`) surfaces the typed-shape diagnostic
20244        // first through the compound gate. Sanity assertion: the
20245        // self-referential `:membros` entry alone under the same
20246        // parent `:nome` trips the self-edge arm on its own via the
20247        // standalone [`crate::aplicacao::validate_no_self_membership`],
20248        // so the typed-shape-first surfacing is a real ordering
20249        // property, not a case where the self-edge arm silently
20250        // accepts the fixture. Pins the pre-fold layout wire-up's
20251        // canonical dispatch order (typed-shape cascade → cross-slot
20252        // self-edge) as a property of the substrate primitive rather
20253        // than a convention of the layout call site. Sibling in shape
20254        // to `validate_deps_per_entry_arm_fires_before_self_edge_arm`
20255        // (b5dd55e) on the sibling per-slot compound gate's per-arm
20256        // ordering property.
20257        use crate::aplicacao::{Membro, WitContract};
20258        let mut c = aplicacao_fixture("demo");
20259        c.membros = vec![Membro {
20260            caixa: "demo".into(),
20261            versao: "^0.1".into(),
20262        }];
20263        c.contratos = vec![WitContract {
20264            de: "demo".into(),
20265            para: "orphan".into(),
20266            wit: "wasi:http/proxy".into(),
20267            endpoint: Some("/x".into()),
20268            subject: None,
20269            slot: None,
20270        }];
20271        let err = c.validate_aplicacao_shape().unwrap_err();
20272        assert!(
20273            matches!(
20274                err,
20275                crate::AplicacaoError::ContratoMemberMissing { ref caixa }
20276                    if caixa == "orphan"
20277            ),
20278            "typed-shape arm must fire before self-edge arm — expected \
20279             ContratoMemberMissing on \"orphan\", got {err:?}",
20280        );
20281        // Sanity: the self-referential `:membros` entry alone under
20282        // the same parent `:nome` trips the self-edge arm on its own
20283        // — proves the typed-shape-first surfacing above is a real
20284        // ordering property, not a case where the self-edge arm
20285        // silently accepts the fixture.
20286        let sanity = crate::aplicacao::validate_no_self_membership(
20287            &[Membro {
20288                caixa: "demo".into(),
20289                versao: "^0.1".into(),
20290            }],
20291            "demo",
20292        )
20293        .unwrap_err();
20294        assert!(
20295            matches!(
20296                sanity,
20297                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa }
20298                    if caixa == "demo"
20299            ),
20300            "sanity: the self-referential :membros entry alone must \
20301             trip the self-edge arm — got {sanity:?}",
20302        );
20303    }
20304
20305    #[test]
20306    fn validate_aplicacao_shape_accepts_non_aplicacao_kind() {
20307        // Positive control on the identity-element arm: every non-
20308        // Aplicacao kind passes the compound gate trivially — the
20309        // paired [`Caixa::aplicacao_view`] accessor returns `None`
20310        // off the Aplicacao arm (by construction, keyed on
20311        // `caixa.kind().is_aplicacao()`), so the fold short-circuits
20312        // to `Ok(())` without touching the mesh slots. Pins the
20313        // identity element on every non-Aplicacao kind — a future
20314        // refactor that made the mesh-slot cascade fire on the wrong
20315        // kind (say, on a `Servico` whose mesh slots happen to be
20316        // populated in a mis-authored manifest, which the peer
20317        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
20318        // coherence gate would refuse upstream anyway) surfaces here
20319        // as a test failure first. Peer with the
20320        // `validate_limits_accepts_none` / `validate_behavior_accepts_none`
20321        // identity-element pins on the sibling M2 `Option`-shaped
20322        // per-Caixa compound gates.
20323        for kind in [
20324            CaixaKind::Biblioteca,
20325            CaixaKind::Binario,
20326            CaixaKind::Servico,
20327            CaixaKind::Supervisor,
20328            CaixaKind::Acao,
20329        ] {
20330            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20331            c.kind = kind;
20332            assert!(
20333                c.aplicacao_view().is_none(),
20334                "aplicacao_view must return None off the Aplicacao arm \
20335                 for kind {kind:?}",
20336            );
20337            c.validate_aplicacao_shape().expect(
20338                "non-Aplicacao kinds must pass the compound gate as the fold's identity element",
20339            );
20340        }
20341    }
20342
20343    #[test]
20344    fn validate_aplicacao_shape_accepts_clean_fixture() {
20345        // Positive control: a well-formed Aplicacao (two DNS-1123
20346        // members with valid semver constraints, no `:contratos` /
20347        // `:entrada` / `:placement` / `:politicas` set — every
20348        // per-slot gate accepts the vacuous / omitted arm) passes the
20349        // compound gate cleanly. A future tightening of either arm's
20350        // accepted set surfaces here as a test failure first. Mirrors
20351        // the peer `validate_deps_accepts_clean_fixture` (b5dd55e) /
20352        // `validate_upgrade_from_accepts_clean_fixture` (d6801df)
20353        // positive-control postures on the sibling per-Caixa
20354        // compound gates.
20355        let c = aplicacao_fixture("demo");
20356        c.validate_aplicacao_shape()
20357            .expect("clean Aplicacao fixture must pass the compound gate");
20358    }
20359}