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 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 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 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 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 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 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 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 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    #[must_use]
2221    pub fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
2222        match list {
2223            crate::dep::DepList::Prod => self.deps(),
2224            crate::dep::DepList::Dev => self.deps_dev(),
2225        }
2226    }
2227
2228    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
2229    /// consumer that appends to one of the two dep-list axes keys off
2230    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
2231    /// method on the substrate primitive rather than the prior
2232    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
2233    /// else { &mut caixa.deps }` inline dispatch + open-coded
2234    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
2235    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
2236    /// a within-list name collision — the same `list: &'static str`
2237    /// diagnostic shape [`Self::validate_deps`]'s per-list
2238    /// [`crate::render::insert_first_seen`] walk raises on the peer
2239    /// parse-time within-list dedup axis, so a future author reading a
2240    /// `feira add` refusal and a `feira build` refusal reaches for the
2241    /// same corrective surface without switching diagnostic idioms.
2242    ///
2243    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
2244    /// closed-set typed carrier for the "runtime-closure `:deps` vs
2245    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
2246    /// dispatches on — the compiler-checked exhaustiveness on the
2247    /// enum's `match` arms is the build-time guarantee that no future
2248    /// per-list mutation-site regresses to a bare-`bool`-flag
2249    /// (`is_dev: bool`) inline dispatch that a future third
2250    /// dep-list axis (a `:deps-build` build-only closure once the
2251    /// substrate grows cross-artifact heterogeneous dep-graphs, per
2252    /// CAIXA-SDLC §I) would silently split at every consumer.
2253    ///
2254    /// Same "one typed dispatch on the substrate primitive, thin
2255    /// projections at each consumer" discipline the sibling per-slot
2256    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
2257    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
2258    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
2259    /// the substrate's first typed-mutation dispatch on the top-level
2260    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
2261    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
2262    /// diagnostic path routed no through-line back to the typed slot,
2263    /// so a future extension of either dep-list axis to a richer author
2264    /// surface (a per-cluster override the operator pins through a
2265    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
2266    /// roadmap acknowledges, an M4
2267    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
2268    /// admission-webhook that normalized the list at admission time)
2269    /// would have had to be threaded through the `feira add` mutation
2270    /// site in lockstep with every read consumer or one path would
2271    /// silently disagree with the other on which list a given dep lands
2272    /// in. Lifting the resolution rule to a typed method on the
2273    /// substrate primitive means every downstream dep-list-mutating
2274    /// consumer of the top-level manifest reaches for exactly one typed
2275    /// dispatch — the resolver's accept-set migrates as a unit on any
2276    /// future axis addition.
2277    ///
2278    /// # Errors
2279    ///
2280    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
2281    /// when another entry in the same list already carries the same
2282    /// `:nome` — the mutation is refused and the caller can surface the
2283    /// typed diagnostic to the author (the `feira add` verb routes the
2284    /// error through `anyhow::Error::from`, which preserves the
2285    /// canonical `#[error(...)]`-templated diagnostic body).
2286    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
2287        let target = match list {
2288            crate::dep::DepList::Prod => &mut self.deps,
2289            crate::dep::DepList::Dev => &mut self.deps_dev,
2290        };
2291        if target.iter().any(|d| d.nome() == dep.nome()) {
2292            return Err(DepError::DuplicateNome {
2293                nome: dep.nome().to_string(),
2294                list: list.as_str(),
2295            });
2296        }
2297        target.push(dep);
2298        Ok(())
2299    }
2300
2301    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
2302    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
2303    /// composite-reference accessor every consumer of the top-level
2304    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
2305    /// off — returns the author-declared `:limits` typed composite
2306    /// verbatim as an `Option<&LimitsSpec>` reference over the same
2307    /// backing storage the raw `self.limits.as_ref()` field access
2308    /// borrows from, with `None` naming the "no `:limits` block
2309    /// authored — every per-axis Lunatic-sandbox cap defers to the
2310    /// wasm-engine-default arm named on the per-axis
2311    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
2312    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
2313    /// docstrings" partition every downstream Servico-M2-overlay
2314    /// emitter treats as "emit nothing" and the sibling
2315    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
2316    /// treats as "skip the per-axis
2317    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
2318    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
2319    ///
2320    /// The outer `:limits` slot carries the M2 Servico-runtime typed
2321    /// composite — the load-bearing container of every Lunatic-shaped
2322    /// per-process wasm32-sandbox cap axis every long-running wasm
2323    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
2324    /// Lunatic per-process linear-memory / fuel / wall-clock /
2325    /// millicore cap primitives translated onto pleme-io's typed
2326    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
2327    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2328    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2329    /// chart both fan on). Every per-`:limits` axis threads through a
2330    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
2331    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
2332    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
2333    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
2334    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
2335    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
2336    /// consumer that reaches for a limits axis first passes through
2337    /// this outer accessor onto the composite and then dispatches
2338    /// onto the per-axis accessor — the two-level dispatch means
2339    /// every per-`:limits` reader now routes through a typed dispatch
2340    /// on the substrate primitive at both altitudes.
2341    ///
2342    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
2343    /// was accessed inline at three production sites — the
2344    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
2345    /// `if let Some(l) = &caixa.limits { … }` traversal head
2346    /// (caixa-core/src/layout.rs:882, which drives the per-axis
2347    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
2348    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
2349    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
2350    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
2351    /// [`LimitsSpec::validate`] fans onto), the
2352    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
2353    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
2354    /// head (caixa-core/src/render.rs:18504, which drives the
2355    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
2356    /// projection every `caixa-helm` / `caixa-flux` Servico values-
2357    /// block emitter fans on), and the
2358    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2359    /// set enumerator's `self.limits.is_some()` presence probe
2360    /// (caixa-core/src/manifest.rs:1788, which drives the
2361    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
2362    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2363    /// gate reads) — three open-coded outer-field accesses that
2364    /// expressed no compile-time link back to the typed slot at the
2365    /// [`Caixa`] altitude. A future extension of the `:limits` outer
2366    /// axis to a richer author surface (a multi-`:limits` list the M4
2367    /// CR materializer resolves per-CR at admission time so a Servico
2368    /// can expose a compute-heavy + IO-heavy limits pair, a per-
2369    /// cluster `:limits-overrides` slot the operator pins so a
2370    /// cluster-specific policy can tighten a caixa-declared cap
2371    /// without re-authoring the `caixa.lisp`, a promotion of the
2372    /// plain `Option<LimitsSpec>` to a richer
2373    /// `{static, dynamic}` partition once the wasm-engine's runtime-
2374    /// resolved dynamic-cap surface lands) would have had to be
2375    /// threaded through all three open-coded copies in lockstep or
2376    /// one consumer would silently disagree with the peers on which
2377    /// limits composite a given Caixa resolves to — the layout gate's
2378    /// per-axis bracket-dispatch seed reading the raw slot while the
2379    /// peer `servico_m2_overlay` emitter read an operator-resolved
2380    /// slot would silently split the build-time sandbox-shape gate
2381    /// from the runtime `ComputeUnit` CR emission gate, a three-
2382    /// consumer split at the layout gate, the M2 overlay emitter, and
2383    /// the declared-slot enumerator far from the source `caixa.lisp`
2384    /// with no field naming the limits-drift root cause. Lifting the
2385    /// resolution rule to a typed method on the substrate primitive
2386    /// means every downstream consumer of the caixa's per-`Caixa`
2387    /// Lunatic-sandboxing outer-composite surface reaches for exactly
2388    /// one typed dispatch — the resolver's accept-set migrates as a
2389    /// unit on any future axis addition.
2390    ///
2391    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
2392    /// composite-reference accessor — opens the outer-`Caixa`
2393    /// `Option<&Composite>` composite-reference projection pattern the
2394    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
2395    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
2396    /// [`crate::aplicacao::Placement`] / `:entrada`
2397    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
2398    /// fold on. Peer of the M3 mesh-slot outer-composite family the
2399    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2400    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2401    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2402    /// accessors already close on the outer [`crate::AplicacaoSpec`]
2403    /// altitude — extends that "one typed dispatch on the substrate
2404    /// primitive, thin projections at each consumer" discipline onto
2405    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2406    /// runtime slot family's outer-composite axis. Returns
2407    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2408    /// clone) because every downstream consumer of the limits
2409    /// composite treats it as a read-only per-axis dispatch source —
2410    /// the reference-view is the narrowest borrow that supports every
2411    /// present + roadmapped consumer (per-axis accessor dispatch,
2412    /// `.is_empty()`-gated overlay projection, presence-probe early
2413    /// return on the "author-omitted `:limits` ⇒ engine-default
2414    /// applies" partition) without cloning the composite through
2415    /// every consumer's fast path. The `Option` half of the return-
2416    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2417    /// engine-default applies" partition (not a default composite the
2418    /// downstream must reject on emptiness) — the accessor projects
2419    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2420    /// reference-return unchanged. Named `limits()` to match the
2421    /// storage field's name verbatim and the tatara-lisp author-
2422    /// surface term (`:limits`) the field's own docstring already
2423    /// carries.
2424    #[must_use]
2425    pub fn limits(&self) -> Option<&LimitsSpec> {
2426        self.limits.as_ref()
2427    }
2428
2429    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2430    /// composite OTP-`gen_server`-shaped callback-table optional-
2431    /// composite-reference accessor every consumer of the top-level
2432    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2433    /// keys off — returns the author-declared `:behavior` typed
2434    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2435    /// the same backing storage the raw `self.behavior.as_ref()` field
2436    /// access borrows from, with `None` naming the "no `:behavior`
2437    /// block authored — every per-callback OTP-shaped hook defers to
2438    /// the wasm-engine's runtime default arm named on the per-axis
2439    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2440    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2441    /// [`BehaviorSpec::on_state_change`] /
2442    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2443    /// partition every downstream Servico-M2-overlay emitter treats as
2444    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2445    /// per-`:behavior` shape gate treats as "skip the per-arm
2446    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2447    /// per-callback on-disk `MissingEntry` existence check".
2448    ///
2449    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2450    /// composite — the load-bearing container of every OTP-shaped
2451    /// per-Servico lifecycle-callback path axis every long-running wasm
2452    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2453    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2454    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2455    /// translated onto pleme-io's typed `:behavior :on-init` /
2456    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2457    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2458    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2459    /// chart both fan on). Every per-`:behavior` axis threads through a
2460    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2461    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2462    /// Every downstream consumer that reaches for a behavior axis
2463    /// first passes through this outer accessor onto the composite
2464    /// and then dispatches onto the per-callback accessor — the
2465    /// two-level dispatch means every per-`:behavior` reader now
2466    /// routes through a typed dispatch on the substrate primitive at
2467    /// both altitudes.
2468    ///
2469    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2470    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2471    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2472    /// keys the "per-version `:state-change` instruction must have a
2473    /// `:on-state-change` callback" precondition off this accessor's
2474    /// composite (the callback-side counterpart to the
2475    /// `:upgrade-from :instructions :state-change :script` refusal at
2476    /// the appup-side). Threading that gate's traversal input through
2477    /// this accessor closes the cross-slot invariant on the substrate
2478    /// primitive, not on the raw field.
2479    ///
2480    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2481    /// composite was accessed inline at four production sites — the
2482    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2483    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2484    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2485    /// `BehaviorError` refusal cascade + the per-callback on-disk
2486    /// [`crate::LayoutError::MissingEntry`] existence check under
2487    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2488    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2489    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2490    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2491    /// drives the `:state-change` ↔ `:on-state-change` precondition
2492    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2493    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2494    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2495    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2496    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2497    /// Servico values-block emitter fans on), and the
2498    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2499    /// set enumerator's `self.behavior.is_some()` presence probe
2500    /// (caixa-core/src/manifest.rs:1919, which drives the
2501    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2502    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2503    /// gate reads) — four open-coded outer-field accesses that
2504    /// expressed no compile-time link back to the typed slot at the
2505    /// [`Caixa`] altitude. A future extension of the `:behavior`
2506    /// outer axis to a richer author surface (a per-callback overlay
2507    /// resolver the operator materializes at admission time so a
2508    /// cluster-specific policy can inject a per-callback tracing
2509    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2510    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2511    /// dynamic}` partition once a runtime-resolved behavior-swap
2512    /// surface lands, the M4 per-callback middleware chain the
2513    /// caixa-operator's per-Servico admission webhook keys off) would
2514    /// have had to be threaded through all four open-coded copies in
2515    /// lockstep or one consumer would silently disagree with the
2516    /// peers on which behavior composite a given Caixa resolves to —
2517    /// the layout gate's per-callback existence-check seed reading
2518    /// the raw slot while the peer `servico_m2_overlay` emitter read
2519    /// an operator-resolved slot would silently split the build-time
2520    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2521    /// gate from the cross-slot `:state-change` composition gate from
2522    /// the M2 declared-slot enumerator, a four-consumer split far
2523    /// from the source `caixa.lisp` with no field naming the
2524    /// behavior-drift root cause. Lifting the resolution rule to a
2525    /// typed method on the substrate primitive means every downstream
2526    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2527    /// composite surface reaches for exactly one typed dispatch — the
2528    /// resolver's accept-set migrates as a unit on any future axis
2529    /// addition.
2530    ///
2531    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2532    /// composite-reference accessor — sibling to the opening
2533    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2534    /// `Option<&Composite>` composite-reference sub-family, extends
2535    /// the "one typed dispatch on the substrate primitive, thin
2536    /// projections at each consumer" discipline onto the second of
2537    /// the three M2 Servico-runtime slots. The remaining
2538    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2539    /// altitude — the M3 mesh-slot family (`:politicas`,
2540    /// `:placement`, `:entrada` — already closed on the inner
2541    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2542    /// d32111c) — remain the future sibling lifts on the outer
2543    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2544    /// the owning composite by copy or clone) because every
2545    /// downstream consumer of the behavior composite treats it as a
2546    /// read-only per-callback dispatch source — the reference-view is
2547    /// the narrowest borrow that supports every present + roadmapped
2548    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2549    /// overlay projection, presence-probe early return on the
2550    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2551    /// partition, cross-slot `:state-change` composition input)
2552    /// without cloning the composite through every consumer's fast
2553    /// path. The `Option` half of the return-type preserves the
2554    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2555    /// applies" partition (not a default composite the downstream
2556    /// must reject on emptiness) — the accessor projects the raw
2557    /// `Option<BehaviorSpec>` slot's presence bit through the
2558    /// reference-return unchanged. Named `behavior()` to match the
2559    /// storage field's name verbatim and the tatara-lisp author-
2560    /// surface term (`:behavior`) the field's own docstring already
2561    /// carries.
2562    #[must_use]
2563    pub fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2564        self.behavior.as_ref()
2565    }
2566
2567    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2568    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2569    /// reference accessor every consumer of the top-level manifest's
2570    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2571    /// reader keys off — returns the author-declared `:politicas` typed
2572    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2573    /// same backing storage the raw `self.politicas.as_ref()` field
2574    /// access borrows from, with `None` naming the "no `:politicas`
2575    /// block authored — every per-axis mesh-policy scalar defers to the
2576    /// cluster-default arm named on the per-axis
2577    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2578    /// [`crate::aplicacao::MeshPolicy::retries`] /
2579    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2580    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2581    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2582    /// docstrings" partition every downstream caixa-mesh /
2583    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2584    /// "emit no per-`:politicas` overlay" and the sibling
2585    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2586    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2587    /// arm.
2588    ///
2589    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2590    /// Aplicacao typed composite — the load-bearing container of every
2591    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2592    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2593    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2594    /// composite; §V — the "no infinite blocking" per-call deadline +
2595    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2596    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2597    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2598    /// threads through a lifted per-slot accessor on the
2599    /// [`crate::aplicacao::MeshPolicy`] type: the
2600    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2601    /// mTLS-enforcement toggle, the
2602    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2603    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2604    /// (7073d0f) Gateway-API per-call deadline, the
2605    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2606    /// Envoy-outlier-detection composite. Every downstream consumer
2607    /// that reaches for a mesh-policy axis first passes through this
2608    /// outer accessor onto the composite and then dispatches onto the
2609    /// per-axis accessor — the two-level dispatch means every per-
2610    /// `:politicas` reader now routes through a typed dispatch on the
2611    /// substrate primitive at both altitudes.
2612    ///
2613    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2614    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2615    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2616    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2617    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2618    /// composite whether or not the author declared the outer slot.
2619    /// The outer accessor preserves the "author-omitted vs authored-
2620    /// empty" partition the inner accessor's `is_empty()`-gated
2621    /// renderer overlay collapses — routing the presence bit through
2622    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2623    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2624    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2625    ///
2626    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2627    /// composite was accessed inline at two production sites — the
2628    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2629    /// `self.politicas.clone().unwrap_or_default()` traversal head
2630    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2631    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2632    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2633    /// then observes), and the [`Self::declared_mesh_slots`] M3
2634    /// declared-slot-set enumerator's `self.politicas.is_some()`
2635    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2636    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2637    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2638    /// coherence gate reads) — two open-coded outer-field accesses
2639    /// that expressed no compile-time link back to the typed slot at
2640    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2641    /// outer axis to a richer author surface (a per-cluster
2642    /// `:politicas-overrides` slot the operator materializes at
2643    /// admission time so a cluster-specific policy can tighten the
2644    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2645    /// promotion of the plain `Option<MeshPolicy>` to a richer
2646    /// `{static, dynamic}` partition once the M4 per-edge
2647    /// contrato-scoped policy-override surface lands, the M5 traffic-
2648    /// shaping composition the caixa-operator's per-Aplicacao mesh
2649    /// admission webhook keys off) would have had to be threaded
2650    /// through both open-coded copies in lockstep or the Aplicacao-
2651    /// composition seed's default-fold arm would silently disagree
2652    /// with the M3 declared-slot enumerator on which policy composite
2653    /// a given Caixa resolves to — the seed reading an operator-
2654    /// resolved slot while the enumerator's presence probe read the
2655    /// raw slot would silently split the build-time mesh-artifact
2656    /// emission gate from the M3 declared-slot enumerator's kind-
2657    /// coherence gate, a two-consumer split far from the source
2658    /// `caixa.lisp` with no field naming the policy-drift root cause.
2659    /// Lifting the resolution rule to a typed method on the substrate
2660    /// primitive means every downstream consumer of the caixa's per-
2661    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2662    /// reaches for exactly one typed dispatch — the resolver's
2663    /// accept-set migrates as a unit on any future axis addition.
2664    ///
2665    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2666    /// composite-reference accessor — sibling to the opening
2667    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2668    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2669    /// reference sub-family, extends the "one typed dispatch on the
2670    /// substrate primitive, thin projections at each consumer"
2671    /// discipline onto the first of the three M3 mesh-slot axes.
2672    /// Peer of the closed inner mesh-slot outer-composite family the
2673    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2674    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2675    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2676    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2677    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2678    /// mesh-slot arm of the composite-reference family the remaining
2679    /// two axes (`:placement`, `:entrada`) fold onto in future
2680    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2681    /// composite by copy or clone) because every downstream consumer
2682    /// of the mesh-policy composite treats it as a read-only per-axis
2683    /// dispatch source — the reference-view is the narrowest borrow
2684    /// that supports every present + roadmapped consumer (per-axis
2685    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2686    /// presence-probe early return on the "author-omitted `:politicas`
2687    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2688    /// seed's default-fold arm) without cloning the composite through
2689    /// every consumer's fast path. The `Option` half of the return-
2690    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2691    /// cluster-default applies" partition (not a default composite
2692    /// the downstream must reject on emptiness) — the accessor
2693    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2694    /// through the reference-return unchanged. Named `politicas()` to
2695    /// match the storage field's name verbatim and the tatara-lisp
2696    /// author-surface term (`:politicas`) the field's own docstring
2697    /// already carries.
2698    #[must_use]
2699    pub fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2700        self.politicas.as_ref()
2701    }
2702
2703    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2704    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2705    /// reference accessor every consumer of the top-level manifest's
2706    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2707    /// reader keys off — returns the author-declared `:placement` typed
2708    /// composite verbatim as an `Option<&Placement>` reference over the
2709    /// same backing storage the raw `self.placement.as_ref()` field
2710    /// access borrows from, with `None` naming the "no `:placement`
2711    /// block authored — every per-axis placement scalar defers to the
2712    /// cluster-default arm named on the per-axis
2713    /// [`crate::aplicacao::Placement::estrategia`] /
2714    /// [`crate::aplicacao::Placement::clusters`] /
2715    /// [`crate::aplicacao::Placement::affinity`] /
2716    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2717    /// docstrings" partition every downstream caixa-mesh /
2718    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2719    /// "emit no per-`:placement` overlay" and the sibling
2720    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2721    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2722    ///
2723    /// The outer `:placement` slot carries the M3 mesh-slot per-
2724    /// Aplicacao typed distribution composite — the load-bearing
2725    /// container of every where-does-this-Aplicacao-run axis every
2726    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2727    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2728    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2729    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2730    /// Aplicacao's typed distribution composite; §V CSE invariants —
2731    /// "distribution is a first-class typed composite, not a runtime
2732    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2733    /// typed inter-Servico contrato-edge overlay the per-cluster
2734    /// mesh renderer keys off). Every per-`:placement` axis threads
2735    /// through a lifted per-slot accessor on the
2736    /// [`crate::aplicacao::Placement`] type: the
2737    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2738    /// MESH-COMPOSITION distribution-strategy scalar, the
2739    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2740    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2741    /// M3-Adaptive-compression-hint optional-scalar, and the
2742    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2743    /// sharding extractor-expression optional-scalar. Every downstream
2744    /// consumer that reaches for a placement axis first passes through
2745    /// this outer accessor onto the composite and then dispatches onto
2746    /// the per-axis accessor — the two-level dispatch means every per-
2747    /// `:placement` reader now routes through a typed dispatch on the
2748    /// substrate primitive at both altitudes.
2749    ///
2750    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2751    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2752    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2753    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2754    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2755    /// whether or not the author declared the outer slot. The outer
2756    /// accessor preserves the "author-omitted vs authored-empty" partition
2757    /// the inner accessor collapses at the cluster-default fold —
2758    /// routing the presence bit through this accessor keeps the
2759    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2760    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2761    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2762    /// dispatch.
2763    ///
2764    /// Prior to this lift the `.placement` `Option<Placement>`
2765    /// composite was accessed inline at two production sites — the
2766    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2767    /// `self.placement.clone().unwrap_or_default()` traversal head
2768    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2769    /// the [`crate::aplicacao::Placement::default`] cluster-default
2770    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2771    /// then observes), and the [`Self::declared_mesh_slots`] M3
2772    /// declared-slot-set enumerator's `self.placement.is_some()`
2773    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2774    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2775    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2776    /// coherence gate reads) — two open-coded outer-field accesses
2777    /// that expressed no compile-time link back to the typed slot at
2778    /// the [`Caixa`] altitude. A future extension of the `:placement`
2779    /// outer axis to a richer author surface (a per-cluster
2780    /// `:placement-overrides` slot the operator materializes at
2781    /// admission time so a cluster-specific placement can tighten the
2782    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2783    /// per-tenant placement-alias table the M4
2784    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2785    /// per-CR at admission time, a promotion of the plain
2786    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2787    /// once Orleans-style virtual-actor dynamic placement comes into
2788    /// typed scope) would have had to be threaded through both open-
2789    /// coded copies in lockstep or the Aplicacao-composition seed's
2790    /// default-fold arm would silently disagree with the M3 declared-
2791    /// slot enumerator on which distribution composite a given Caixa
2792    /// resolves to — the seed reading an operator-resolved slot while
2793    /// the enumerator's presence probe read the raw slot would
2794    /// silently split the build-time distribution-artifact emission
2795    /// gate from the M3 declared-slot enumerator's kind-coherence
2796    /// gate, a two-consumer split far from the source `caixa.lisp`
2797    /// with no field naming the distribution-drift root cause.
2798    /// Lifting the resolution rule to a typed method on the substrate
2799    /// primitive means every downstream consumer of the caixa's per-
2800    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2801    /// reaches for exactly one typed dispatch — the resolver's
2802    /// accept-set migrates as a unit on any future axis addition.
2803    ///
2804    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2805    /// composite-reference accessor — sibling to the opening
2806    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2807    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2808    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2809    /// composite-reference sub-family, folds on the "one typed
2810    /// dispatch on the substrate primitive, thin projections at each
2811    /// consumer" discipline extended onto the second of the three M3
2812    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2813    /// composite family the sibling
2814    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2815    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2816    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2817    /// accessor pins already close on the inner
2818    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2819    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2820    /// [`Self::politicas`] opened, extending the discipline onto the
2821    /// second of the three M3 mesh-slot axes. The remaining M3
2822    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2823    /// discipline in the final sibling lift, closing the outer top-
2824    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2825    /// Returns `Option<&Placement>` (not the owning composite by copy
2826    /// or clone) because every downstream consumer of the placement
2827    /// composite treats it as a read-only per-axis dispatch source —
2828    /// the reference-view is the narrowest borrow that supports every
2829    /// present + roadmapped consumer (per-axis accessor dispatch,
2830    /// serde composite-serialization on the programs.yaml overlay,
2831    /// presence-probe early return on the "author-omitted `:placement`
2832    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2833    /// seed's default-fold arm) without cloning the composite through
2834    /// every consumer's fast path. The `Option` half of the return-
2835    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2836    /// cluster-default applies" partition (not a default composite
2837    /// the downstream must reject on emptiness) — the accessor
2838    /// projects the raw `Option<Placement>` slot's presence bit
2839    /// through the reference-return unchanged. Named `placement()` to
2840    /// match the storage field's name verbatim and the tatara-lisp
2841    /// author-surface term (`:placement`) the field's own docstring
2842    /// already carries.
2843    #[must_use]
2844    pub fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2845        self.placement.as_ref()
2846    }
2847
2848    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2849    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2850    /// composite-reference accessor every consumer of the top-level
2851    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2852    /// composite reader keys off — returns the author-declared
2853    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2854    /// reference over the same backing storage the raw
2855    /// `self.entrada.as_ref()` field access borrows from, with `None`
2856    /// naming the "no `:entrada` block authored — this Aplicacao is
2857    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2858    /// partition every downstream caixa-mesh Gateway-API artifact
2859    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2860    /// backend for this Aplicacao" and the sibling
2861    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2862    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2863    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2864    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2865    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2866    /// the same `Option<&Entrada>` presence bit unchanged).
2867    ///
2868    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2869    /// Aplicacao typed external-gateway composite — the load-bearing
2870    /// container of every how-does-the-outside-world-reach-this-
2871    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2872    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2873    /// external-entry composite; §V CSE invariants — "the external
2874    /// gateway is a first-class typed composite, not a per-Servico
2875    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2876    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2877    /// API renderer keys off). Every per-`:entrada` axis threads
2878    /// through a lifted per-slot accessor on the
2879    /// [`crate::aplicacao::Entrada`] type: the
2880    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2881    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2882    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2883    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2884    /// backend `trigger.service.port` scalar, and the
2885    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2886    /// resolver every HTTPRoute-aware renderer consumes. Every
2887    /// downstream consumer that reaches for an entry axis first passes
2888    /// through this outer accessor onto the composite and then
2889    /// dispatches onto the per-axis accessor — the two-level dispatch
2890    /// means every per-`:entrada` reader now routes through a typed
2891    /// dispatch on the substrate primitive at both altitudes.
2892    ///
2893    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2894    /// seed: the Aplicacao-view builder forwards the outer `Option`
2895    /// arm verbatim (no default fold — `:entrada` is inherently
2896    /// optional; a cluster-internal Aplicacao has no external gateway
2897    /// at all, not "an external gateway that defaults to nothing"), so
2898    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2899    /// `Option<&Entrada>`-return accessor observes the same presence
2900    /// bit whether or not the author declared the outer slot. Routing
2901    /// the presence bit through this accessor keeps the
2902    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2903    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2904    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2905    /// hostname/backend/path emission dispatch.
2906    ///
2907    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2908    /// was accessed inline at two production sites — the
2909    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2910    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2911    /// which drives the forward onto the peer inner
2912    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2913    /// Gateway-API fan-out then observes), and the
2914    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2915    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2916    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2917    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2918    /// kind-coherence gate reads) — two open-coded outer-field
2919    /// accesses that expressed no compile-time link back to the typed
2920    /// slot at the [`Caixa`] altitude. A future extension of the
2921    /// `:entrada` outer axis to a richer author surface (a per-cluster
2922    /// `:entrada-overrides` slot the operator materializes at admission
2923    /// time so a cluster-specific hostname can pin the caixa-declared
2924    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2925    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2926    /// CR materializer resolves per-CR at admission time, a promotion
2927    /// of the plain `Option<Entrada>` to a richer
2928    /// `{public, private, internal}` partition once Cilium-identity-
2929    /// scoped internal gateways come into typed scope) would have had
2930    /// to be threaded through both open-coded copies in lockstep or the
2931    /// Aplicacao-composition seed's forward arm would silently
2932    /// disagree with the M3 declared-slot enumerator on which external-
2933    /// gateway composite a given Caixa resolves to — the seed reading
2934    /// an operator-resolved slot while the enumerator's presence probe
2935    /// read the raw slot would silently split the build-time gateway-
2936    /// artifact emission gate from the M3 declared-slot enumerator's
2937    /// kind-coherence gate, a two-consumer split far from the source
2938    /// `caixa.lisp` with no field naming the entry-drift root cause.
2939    /// Lifting the resolution rule to a typed method on the substrate
2940    /// primitive means every downstream consumer of the caixa's per-
2941    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2942    /// surface reaches for exactly one typed dispatch — the resolver's
2943    /// accept-set migrates as a unit on any future axis addition.
2944    ///
2945    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2946    /// return composite-reference accessor — closes the outer-`Caixa`
2947    /// `Option<&Composite>` composite-reference sub-family opened by
2948    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2949    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2950    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2951    /// folds on the "one typed dispatch on the substrate primitive,
2952    /// thin projections at each consumer" discipline extended onto the
2953    /// third and final M3 mesh-slot axis. Peer of the closed inner
2954    /// mesh-slot outer-composite family the sibling
2955    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2956    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2957    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2958    /// accessor pins already close on the inner
2959    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2960    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2961    /// altitudes of the outer-composite reference-return discipline
2962    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2963    /// slot presence) now carry the full five-arm accept-set behind a
2964    /// typed dispatch on the substrate primitive. Returns
2965    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2966    /// because every downstream consumer of the entrada composite
2967    /// treats it as a read-only per-axis dispatch source — the
2968    /// reference-view is the narrowest borrow that supports every
2969    /// present + roadmapped consumer (per-axis accessor dispatch,
2970    /// serde composite-serialization on the programs.yaml overlay,
2971    /// presence-probe early return on the "author-omitted `:entrada`
2972    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2973    /// seed's forward arm) without cloning the composite through every
2974    /// consumer's fast path. The `Option` half of the return-type
2975    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2976    /// cluster-internal Aplicacao" partition (not a default composite
2977    /// the downstream must reject on emptiness — a cluster-internal
2978    /// Aplicacao has no external gateway at all, not "a default gateway
2979    /// that emits nothing"); the accessor projects the raw
2980    /// `Option<Entrada>` slot's presence bit through the reference-
2981    /// return unchanged. Named `entrada()` to match the storage field's
2982    /// name verbatim and the tatara-lisp author-surface term
2983    /// (`:entrada`) the field's own docstring already carries.
2984    #[must_use]
2985    pub fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
2986        self.entrada.as_ref()
2987    }
2988
2989    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
2990    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
2991    /// an `Option<&CiRun>`, borrowed from the typed slot's own
2992    /// `Option<CiRun>` storage. `None` when the slot is absent (every
2993    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
2994    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
2995    /// not silently accepted).
2996    ///
2997    /// Named `ci()` to match the storage field's name and the
2998    /// tatara-lisp author surface (`:ci`); mirrors the sibling
2999    /// `Option<&Composite>` accessors on this same `Caixa` altitude
3000    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
3001    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
3002    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
3003    /// at every consumer.
3004    #[must_use]
3005    pub fn ci(&self) -> Option<&canteiro_types::CiRun> {
3006        self.ci.as_ref()
3007    }
3008
3009    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
3010    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
3011    /// accessor every consumer of the top-level manifest's per-Supervisor
3012    /// restart-strategy axis keys off — returns the author-declared
3013    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
3014    /// `Copy`-projected from the typed slot's own
3015    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
3016    /// (`:estrategia` is a flat-spread supervisor-only slot every
3017    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
3018    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3019    /// still omit to defer to [`RestartStrategy::default`] —
3020    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
3021    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
3022    /// [`SupervisorSpec::default`]-inherited strategy without any silent
3023    /// promotion to a fresh explicit variant at the accessor boundary).
3024    ///
3025    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
3026    /// restart-strategy discriminant every substrate-side per-Supervisor
3027    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
3028    /// closed-set `one_for_one | one_for_all | rest_for_one |
3029    /// simple_one_for_one` algebra translated onto pleme-io's typed
3030    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
3031    /// slot algebra the operator's hierarchical reconciliation scheduler
3032    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
3033    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
3034    /// supervisor slots are flat on Caixa (vs nested under a
3035    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
3036    /// level of nesting"), so the accessor's altitude is the outer
3037    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
3038    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
3039    /// (eafb619) accessor keys off. The two typed axes — the outer
3040    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
3041    /// (author-omitted arm carried as `None`) and the inner post-
3042    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
3043    /// (`Option` collapsed through the [`Self::supervisor_view`]
3044    /// `unwrap_or_default()` fold) — now share one accessor discipline for
3045    /// the shared substrate concept "the author-declared OTP-shaped
3046    /// sibling-restart-strategy variant that partitions the downstream
3047    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
3048    /// `None` arm is the pre-composition presence bit every declared-slot
3049    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
3050    /// inner-altitude non-`Option` `RestartStrategy` is the post-
3051    /// composition partition-dispatch input every strategy-arm consumer
3052    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
3053    /// Supervisor sibling-restart branch, the future M4
3054    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
3055    /// webhook) fans on.
3056    ///
3057    /// Prior to this lift the `.estrategia` field was accessed inline at
3058    /// two production sites in `caixa-core/src/manifest.rs` — the
3059    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
3060    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
3061    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3062    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
3063    /// `SupervisorSpec` construction site at `estrategia:
3064    /// self.estrategia.unwrap_or_default()` (which composes the flat-
3065    /// spread outer author-surface `Option<RestartStrategy>` onto the
3066    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
3067    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
3068    /// coded field-accesses that expressed no compile-time link back to
3069    /// the typed slot. A future extension of the outer `:estrategia` axis
3070    /// to a richer author surface (a per-cluster strategy override the
3071    /// operator pins through a future `:estrategia-overrides` overlay the
3072    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
3073    /// a per-tenant strategy-alias table the M4 CR materializer resolves
3074    /// per-CR, a per-Supervisor dynamic strategy derivation the future
3075    /// adaptive-supervision engine computes from child-failure-history
3076    /// topology, a per-child-cohort strategy split the future
3077    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
3078    /// absorption roadmap acknowledges, a promotion of the plain
3079    /// `Option<RestartStrategy>` to a richer
3080    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
3081    /// operator-resolved overlay lands) would have had to be threaded
3082    /// through both open-coded copies in lockstep or the enumerator's
3083    /// presence probe and the composition site's `unwrap_or_default()`
3084    /// fold would silently disagree on which strategy a given [`Caixa`]
3085    /// resolves to (an author's `:estrategia OneForAll` would satisfy
3086    /// the enumerator's presence probe while the composition site
3087    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
3088    /// the resolution rule to a typed method on the substrate primitive
3089    /// means every downstream consumer of the caixa's per-`Caixa` outer-
3090    /// altitude sibling-restart-strategy surface reaches for exactly one
3091    /// typed dispatch — the resolver's accept-set migrates as a unit on
3092    /// any future axis addition.
3093    ///
3094    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3095    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3096    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
3097    /// projection pattern the sibling per-`Caixa` `:max-restarts`
3098    /// `Option<u32>` and (through the future duration-newtype landing)
3099    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
3100    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
3101    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
3102    /// the post-composition [`SupervisorSpec`] altitude — same "one
3103    /// typed dispatch on the substrate primitive, thin projections at
3104    /// each consumer" discipline extended onto the pre-composition outer
3105    /// author-surface [`Caixa`] altitude for the same OTP-shaped
3106    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
3107    /// `Option<&Composite>` composite-reference family the sibling
3108    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3109    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3110    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
3111    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
3112    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
3113    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
3114    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
3115    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
3116    /// pins on the inner-altitude per-`:placement` composite. Named
3117    /// `estrategia()` to match the storage field's name and the
3118    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
3119    /// / per-[`crate::aplicacao::Placement`] peer
3120    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
3121    /// verbatim; the accessor's identity name maps onto the canonical
3122    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
3123    /// docstring already carries.
3124    #[must_use]
3125    pub fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
3126        self.estrategia
3127    }
3128
3129    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
3130    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
3131    /// scalar accessor every consumer of the top-level manifest's per-
3132    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
3133    /// returns the author-declared `:max-restarts` typed `Option<u32>`
3134    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
3135    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
3136    /// accessor returns by value; no borrow of `&self` past the call).
3137    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
3138    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
3139    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3140    /// still omit to defer to the [`Self::supervisor_view`]
3141    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
3142    ///
3143    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
3144    /// `MaxIntensity` restart-budget count that pairs with the sibling
3145    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
3146    /// restart-intensity ratio the supervisor trips its own escalation on
3147    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
3148    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
3149    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
3150    /// reconciliation scheduler fans on). The slot is *flat-spread* on
3151    /// the outer top-level `Caixa` (per the field-shape docstring at
3152    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
3153    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
3154    /// accessor's altitude is the outer [`Caixa`] surface rather than the
3155    /// composed [`SupervisorSpec`] altitude the sibling
3156    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
3157    /// off. The two typed axes — the outer author-surface `Option<u32>`
3158    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
3159    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
3160    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
3161    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
3162    /// shared substrate concept "the author-declared OTP-shaped
3163    /// restart-budget count every downstream per-Supervisor consumer's
3164    /// restart-intensity budget-vs-count comparator fans on".
3165    ///
3166    /// Prior to this lift the `.max_restarts` field was accessed inline
3167    /// at two production sites in `caixa-core/src/manifest.rs` — the
3168    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
3169    /// presence-probe arm at `if self.max_restarts.is_some()` (which
3170    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3171    /// kind-coherence gate's per-slot label push) and the
3172    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
3173    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
3174    /// flat-spread outer author-surface `Option<u32>` onto the inner
3175    /// post-composition [`SupervisorSpec`] `u32` field the
3176    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
3177    /// coded field-accesses that expressed no compile-time link back to
3178    /// the typed slot. A future extension of the outer `:max-restarts`
3179    /// axis to a richer author surface (a per-cluster restart-budget
3180    /// override the operator pins through a future `:max-restarts-overrides`
3181    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
3182    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
3183    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
3184    /// budget derivation the future adaptive-supervision engine computes
3185    /// from child-failure-history topology, a promotion of the plain
3186    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
3187    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
3188    /// per-child-cohort roadmap lands) would have had to be threaded
3189    /// through both open-coded copies in lockstep or the enumerator's
3190    /// presence probe and the composition site's `unwrap_or(5)` fold
3191    /// would silently disagree on which restart-budget a given [`Caixa`]
3192    /// resolves to (an author's `:max-restarts 10` would satisfy the
3193    /// enumerator's presence probe while the composition site silently
3194    /// composed the OTP-canonical `5`, or vice versa). Lifting the
3195    /// resolution rule to a typed method on the substrate primitive means
3196    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
3197    /// restart-budget-count surface reaches for exactly one typed dispatch
3198    /// — the resolver's accept-set migrates as a unit on any future axis
3199    /// addition.
3200    ///
3201    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3202    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3203    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
3204    /// projection pattern the sibling per-`Caixa`
3205    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
3206    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
3207    /// Peer of the inner-altitude
3208    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
3209    /// on the post-composition [`SupervisorSpec`] altitude — same "one
3210    /// typed dispatch on the substrate primitive, thin projections at
3211    /// each consumer" discipline extended onto the pre-composition outer
3212    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
3213    /// shaped restart-budget-count axis. Named `max_restarts()` to match
3214    /// the storage field's name and the per-[`SupervisorSpec`] peer
3215    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
3216    /// discipline verbatim; the accessor's identity maps onto the
3217    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
3218    /// field's docstring already carries.
3219    #[must_use]
3220    pub const fn max_restarts(&self) -> Option<u32> {
3221        self.max_restarts
3222    }
3223
3224    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
3225    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
3226    /// denominator raw-duration-string scalar accessor every consumer of
3227    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
3228    /// window axis keys off — returns the author-declared `:restart-window`
3229    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
3230    /// from the typed slot's own `Option<String>` storage. `None` when
3231    /// the slot is absent (the canonical "never reset — every restart
3232    /// across the supervisor's lifetime counts against the sibling
3233    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
3234    /// `defcaixa` carries by `#[serde(default)]` and every
3235    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
3236    /// [`Self::supervisor_view`] `restart_window: None` composition
3237    /// through the [`crate::supervisor::duration_codec::parse`] soft-
3238    /// swallow `.and_then(|s| … .ok())` fold).
3239    ///
3240    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
3241    /// shaped `Period` sliding-observation-interval duration string that
3242    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
3243    /// budget count to form the `MaxIntensity / Period` restart-intensity
3244    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
3245    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
3246    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
3247    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
3248    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
3249    /// holds an `Option<Duration>` routed through the shared
3250    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
3251    /// — so the outer altitude's accessor returns `Option<&str>` (raw
3252    /// authoring surface) while the inner altitude's
3253    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
3254    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
3255    /// is closed by the sibling [`Self::validate_restart_window`] gate
3256    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
3257    /// the offending value; the view-construction path
3258    /// [`Self::supervisor_view`] soft-swallows the same parse error to
3259    /// `None` to keep the view best-effort.
3260    ///
3261    /// Prior to this lift the `.restart_window` field was accessed inline
3262    /// at three production sites in `caixa-core/src/manifest.rs` — the
3263    /// [`Self::declared_supervisor_slots`]
3264    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
3265    /// `if self.restart_window.is_some()` (which drives the
3266    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3267    /// coherence gate's per-slot label push), the
3268    /// [`Self::validate_restart_window`] `let Some(s) =
3269    /// self.restart_window.as_deref()` empty-and-shape gate binding
3270    /// (which folds the raw string through the shared
3271    /// [`crate::supervisor::duration_codec::parse`] to surface
3272    /// [`ManifestError::RestartWindowMalformed`] naming the offending
3273    /// value), and the [`Self::supervisor_view`] `self.restart_window
3274    /// .as_deref().and_then(…)` view-construction fold (which composes
3275    /// the flat-spread outer author-surface `Option<String>` onto the
3276    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
3277    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
3278    /// three open-coded field-accesses that expressed no compile-time
3279    /// link back to the typed slot. A future extension of the outer
3280    /// `:restart-window` axis to a richer author surface (a per-cluster
3281    /// window override, a per-tenant window-alias table, a per-Supervisor
3282    /// dynamic window derivation the future adaptive-supervision engine
3283    /// computes from child-failure-history topology, a promotion of the
3284    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
3285    /// once the future author-surface parser lands at the [`Caixa`]
3286    /// altitude and the raw-string form is retired) would have had to be
3287    /// threaded through every open-coded copy in lockstep or the three
3288    /// consumers would silently disagree on which raw string a given
3289    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
3290    /// method on the substrate primitive means every downstream consumer
3291    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
3292    /// string surface reaches for exactly one typed dispatch — the
3293    /// resolver's accept-set migrates as a unit on any future axis
3294    /// addition.
3295    ///
3296    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
3297    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
3298    /// spread projection pattern the sibling per-`Caixa`
3299    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
3300    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
3301    /// the sub-family onto the sibling `Option<&str>` raw-duration-
3302    /// string arm (the outer altitude's raw-string form; the inner
3303    /// altitude's parsed [`Duration`] form is the peer
3304    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
3305    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
3306    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
3307    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
3308    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
3309    /// sub-family already carries — same "one typed dispatch on the
3310    /// substrate primitive, thin projections at each consumer"
3311    /// discipline extended onto the M2 supervisor-tree flat-spread
3312    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
3313    /// to match the storage field's name and the per-[`SupervisorSpec`]
3314    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
3315    /// method-name discipline verbatim; the accessor's identity maps
3316    /// onto the canonical OTP-shape supervision vocabulary the
3317    /// `:restart-window` field's docstring already carries.
3318    #[must_use]
3319    pub const fn restart_window(&self) -> Option<&str> {
3320        match &self.restart_window {
3321            Some(s) => Some(s.as_str()),
3322            None => None,
3323        }
3324    }
3325
3326    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
3327    /// outer-composite OTP-appup-shaped per-prior-version migration-
3328    /// entry-list slice accessor every consumer of the top-level
3329    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
3330    /// slice-view keys off — returns the author-declared `:upgrade-from`
3331    /// typed `Vec<UpgradeFromEntry>` verbatim as a
3332    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
3333    /// the raw `self.upgrade_from.as_slice()` field access borrows
3334    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
3335    /// arm every `defcaixa` without an `:upgrade-from` block carries;
3336    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
3337    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
3338    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
3339    /// possibly empty — and the returned `&[UpgradeFromEntry]`
3340    /// degenerates to an empty slice on that arm without any silent
3341    /// `None` collapse).
3342    ///
3343    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
3344    /// migration block — the load-bearing container of every per-
3345    /// prior-`:versao` migration-instruction list the wasm-operator
3346    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
3347    /// `.appup` per-prior-version `LoadModule | StateChange |
3348    /// SoftPurge | Purge | Restart` instruction algebra translated
3349    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
3350    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
3351    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
3352    /// threads through a lifted per-entry accessor on the
3353    /// [`UpgradeFromEntry`] type: the
3354    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
3355    /// version scalar accessor and the
3356    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
3357    /// return per-entry instruction-list accessor (0137e5a). Every
3358    /// downstream consumer of the hot-upgrade path first passes
3359    /// through this outer accessor onto the slice and then dispatches
3360    /// per-entry through the inner accessors — the two-level dispatch
3361    /// means every per-`:upgrade-from` reader now routes through a
3362    /// typed dispatch on the substrate primitive at both altitudes.
3363    ///
3364    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
3365    /// slot was accessed inline at production sites across three
3366    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
3367    /// enumerator's `self.upgrade_from.is_empty()` presence probe
3368    /// (caixa-core/src/manifest.rs, which drives the
3369    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
3370    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
3371    /// gate reads), the [`crate::StandardLayout::verify`] per-
3372    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
3373    /// layout.rs, which fans onto the
3374    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
3375    /// cross-entry duplicate gate, the
3376    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
3377    /// SemVer-precedence cross-slot gate, the
3378    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
3379    /// `:state-change` ↔ `:on-state-change` cross-slot composition
3380    /// gate, and the per-instruction script-path existence-probe walk
3381    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
3382    /// resolve every declared migration script against the layout
3383    /// root), and the [`crate::render::servico_m2_overlay`] per-
3384    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
3385    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
3386    /// projection (caixa-core/src/render.rs, which drives the
3387    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
3388    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
3389    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
3390    /// A future extension of the outer `:upgrade-from` axis (a per-
3391    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
3392    /// resolves at admission time so a cluster-specific migration
3393    /// policy can tighten a caixa-declared step without re-authoring
3394    /// the `caixa.lisp`, promotion of the plain
3395    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
3396    /// partition once runtime-resolved hot-upgrade instructions land,
3397    /// per-entry priority annotation once multi-strategy fan-out
3398    /// lands) would have had to be threaded through all six open-
3399    /// coded copies in lockstep or one consumer would silently
3400    /// disagree with the peers on which upgrade slice a given Caixa
3401    /// resolves to — a six-consumer split at the enumerator, the
3402    /// three-stage validate pass, the script-path probe walk, and the
3403    /// M2 overlay emitter, far from the source `caixa.lisp` with no
3404    /// field naming the upgrade-drift root cause. Lifting the
3405    /// resolution rule to a typed method on the substrate primitive
3406    /// means every downstream consumer of the caixa's per-`Caixa`
3407    /// OTP-appup outer-slice surface reaches for exactly one typed
3408    /// dispatch — the resolver's accept-set migrates as a unit on any
3409    /// future axis addition.
3410    ///
3411    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3412    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3413    /// outer-`Caixa` `&[Composite]` composite-slice projection
3414    /// pattern the sibling `:children`
3415    /// [`crate::supervisor::ChildSpec`] / `:membros`
3416    /// [`crate::aplicacao::Membro`] / `:contratos`
3417    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3418    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3419    /// `Option<&Composite>` composite-reference family the sibling
3420    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3421    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3422    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3423    /// `Option<&Composite>` altitude, extended here to the outer-
3424    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3425    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3426    /// (0137e5a) — same "one typed dispatch on the substrate
3427    /// primitive, thin projections at each consumer" discipline
3428    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3429    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3430    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3431    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3432    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3433    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3434    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3435    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3436    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3437    /// slice" projection pattern onto the sibling M2 typed-composite-
3438    /// element axis (`UpgradeFromEntry` composite, matching the
3439    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3440    /// different altitude).
3441    ///
3442    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3443    /// because every downstream consumer of the hot-upgrade list
3444    /// treats it as a read-only sequence — the slice-view is the
3445    /// narrowest borrow that supports every present + roadmapped
3446    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3447    /// serialization through
3448    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3449    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3450    /// the typed view reaches for (the storage-side `Vec` remains
3451    /// reachable through the `pub upgrade_from` field for the
3452    /// mutation-carrying serde round-trip and per-test fixture-
3453    /// mutation paths). Named `upgrade_from()` to match the storage
3454    /// field's `snake_case` name; the kebab-case author-surface tag
3455    /// `:upgrade-from` is the same axis after tatara-lisp's
3456    /// kebab↔snake fold and the accessor's identity maps onto the
3457    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3458    /// already carries.
3459    #[must_use]
3460    pub fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3461        self.upgrade_from.as_slice()
3462    }
3463
3464    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3465    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3466    /// slice accessor every consumer of the top-level manifest's per-
3467    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3468    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3469    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3470    /// the same backing buffer the raw `self.children.as_slice()` field
3471    /// access borrows from. Empty-slice-carrying (the "no static children
3472    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3473    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3474    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3475    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3476    /// on those arms without any silent `None` collapse).
3477    ///
3478    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3479    /// static-child list — the load-bearing container of every per-
3480    /// child `{caixa, versao, restart}` triple the wasm-operator's
3481    /// hierarchical reconciler dispatches on at supervisor-tree
3482    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3483    /// static-child list translated onto pleme-io's typed
3484    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3485    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3486    /// dispatch fans on). Every per-child axis threads through a lifted
3487    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3488    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3489    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3490    /// version-requirement scalar accessor, and the
3491    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3492    /// per-child post-exit restart-decision-policy discriminant
3493    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3494    /// tree path first passes through this outer accessor onto the
3495    /// slice and then dispatches per-child through the inner accessors
3496    /// — the two-level dispatch means every per-`:children` reader now
3497    /// routes through a typed dispatch on the substrate primitive at
3498    /// both altitudes.
3499    ///
3500    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3501    /// accessed inline at three production sites across two files —
3502    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3503    /// declared-slot enumerator's `!self.children.is_empty()` presence
3504    /// probe (caixa-core/src/manifest.rs, which drives the
3505    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3506    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3507    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3508    /// per-supervisor typed-view composer's `self.children.clone()`
3509    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3510    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3511    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3512    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3513    /// `:children :caixa` self-parent refusal probe's
3514    /// `&caixa.children`-borrowed
3515    /// [`crate::supervisor::validate_no_self_supervision`] input
3516    /// (caixa-core/src/layout.rs, which pins the "no child names the
3517    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3518    /// extension of the outer `:children` axis (a per-cluster
3519    /// `:children-overrides` overlay the wasm-engine operator resolves
3520    /// at admission time so a cluster-specific child-set can tighten
3521    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3522    /// promotion of the plain `Vec<ChildSpec>` to a richer
3523    /// `{static, dynamic}` partition once Erlang/OTP's
3524    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3525    /// axis, per-child priority annotation once multi-strategy fan-out
3526    /// lands) would have had to be threaded through all three open-
3527    /// coded copies in lockstep or one consumer would silently
3528    /// disagree with the peers on which child slice a given Caixa
3529    /// resolves to — the enumerator's presence probe reading the raw
3530    /// slot while the peer view-composer's fold-in path read an
3531    /// operator-resolved slot would silently split the paired
3532    /// declared-slot enumerator and typed-view composition, and the
3533    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3534    /// refusal probe reading a third borrow would silently drift the
3535    /// cross-slot coherence gate's traversal input from the two peers,
3536    /// a three-consumer split at the enumerator, the view composer,
3537    /// and the self-parent gate far from the source `caixa.lisp` with
3538    /// no field naming the child-set-drift root cause. Lifting the
3539    /// resolution rule to a typed method on the substrate primitive
3540    /// means every downstream consumer of the caixa's per-`Caixa`
3541    /// OTP-supervisor outer-slice surface reaches for exactly one
3542    /// typed dispatch — the resolver's accept-set migrates as a unit
3543    /// on any future axis addition.
3544    ///
3545    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3546    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3547    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3548    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3549    /// at the outer altitude of the closed inner-`SupervisorSpec`
3550    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3551    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3552    /// borrow-shared" outer-accessor discipline extended onto the
3553    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3554    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3555    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3556    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3557    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3558    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3559    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3560    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3561    /// M2 typed-composite-element axis
3562    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3563    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3564    /// different altitude).
3565    ///
3566    /// Returns `&[crate::supervisor::ChildSpec]` (not
3567    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3568    /// child list treats it as a read-only sequence — the slice-view
3569    /// is the narrowest borrow that supports every present +
3570    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3571    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3572    /// input, `serde` slice-serialization) without leaking the backing
3573    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3574    /// reaches for (the storage-side `Vec` remains reachable through
3575    /// the `pub children` field for the mutation-carrying serde round-
3576    /// trip and per-test fixture-mutation paths, including the
3577    /// [`Self::supervisor_view`] fold-in path that clones the slot
3578    /// into the typed view). Named `children()` to match the storage
3579    /// field's name verbatim and the tatara-lisp author-surface term
3580    /// (`:children`) the field's own docstring already carries; the
3581    /// accessor's identity maps onto the canonical OTP supervision
3582    /// vocabulary the [`Caixa::children`] field's docstring already
3583    /// reaches for ("Static children of a supervisor").
3584    #[must_use]
3585    pub fn children(&self) -> &[crate::supervisor::ChildSpec] {
3586        self.children.as_slice()
3587    }
3588
3589    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3590    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3591    /// accessor every consumer of the top-level manifest's per-Aplicacao
3592    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3593    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3594    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3595    /// same backing buffer the raw `self.membros.as_slice()` field access
3596    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3597    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3598    /// and every partially-authored Aplicacao carries before the
3599    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3600    /// `&[Membro]` degenerates to an empty slice on those arms without any
3601    /// silent `None` collapse).
3602    ///
3603    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3604    /// per-Aplicacao member list — the load-bearing container of every
3605    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3606    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3607    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3608    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3609    /// the `:entrada :para` external-gateway destination validates
3610    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3611    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3612    /// threads through a lifted per-entry accessor on the
3613    /// [`crate::aplicacao::Membro`] type: the
3614    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3615    /// identity scalar accessor (4a32abf) and the peer
3616    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3617    /// version-requirement scalar accessor (a40b0e3). Every downstream
3618    /// consumer of the mesh-graph path first passes through this outer
3619    /// accessor onto the slice and then dispatches per-member through
3620    /// the inner accessors — the two-level dispatch means every per-
3621    /// `:membros` reader now routes through a typed dispatch on the
3622    /// substrate primitive at both altitudes.
3623    ///
3624    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3625    /// inline at three production sites across two files — the
3626    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3627    /// enumerator's `!self.membros.is_empty()` presence probe
3628    /// (caixa-core/src/manifest.rs, which drives the
3629    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3630    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3631    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3632    /// composer's `self.membros.clone()` per-member fold-in path
3633    /// (caixa-core/src/manifest.rs, which materializes the typed
3634    /// [`crate::aplicacao::AplicacaoSpec`] view every
3635    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3636    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3637    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3638    /// [`crate::aplicacao::validate_no_self_membership`] input
3639    /// (caixa-core/src/layout.rs, which pins the "no member names the
3640    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3641    /// extension of the outer `:membros` axis (a per-cluster
3642    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3643    /// admission time so a cluster-specific member-set can tighten a
3644    /// caixa-declared list without re-authoring the `caixa.lisp`,
3645    /// promotion of the plain `Vec<Membro>` to a richer
3646    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3647    /// members land as a typed axis, per-member priority annotation once
3648    /// multi-strategy fan-out lands) would have had to be threaded
3649    /// through all three open-coded copies in lockstep or one consumer
3650    /// would silently disagree with the peers on which member slice a
3651    /// given Caixa resolves to — the enumerator's presence probe reading
3652    /// the raw slot while the peer view-composer's fold-in path read an
3653    /// operator-resolved slot would silently split the paired
3654    /// declared-slot enumerator and typed-view composition, and the
3655    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3656    /// refusal probe reading a third borrow would silently drift the
3657    /// cross-slot coherence gate's traversal input from the two peers, a
3658    /// three-consumer split at the enumerator, the view composer, and
3659    /// the self-membership gate far from the source `caixa.lisp` with no
3660    /// field naming the member-set-drift root cause. Lifting the
3661    /// resolution rule to a typed method on the substrate primitive
3662    /// means every downstream consumer of the caixa's per-`Caixa`
3663    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3664    /// typed dispatch — the resolver's accept-set migrates as a unit on
3665    /// any future axis addition.
3666    ///
3667    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3668    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3669    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3670    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3671    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3672    /// altitude. Peer at the outer altitude of the closed inner-
3673    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3674    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3675    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3676    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3677    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3678    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3679    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3680    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3681    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3682    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3683    /// pattern onto the sibling M3 typed-composite-element axis
3684    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3685    /// [`crate::AplicacaoSpec::membros`] element type at a different
3686    /// altitude).
3687    ///
3688    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3689    /// because every downstream consumer of the member list treats it
3690    /// as a read-only sequence — the slice-view is the narrowest borrow
3691    /// that supports every present + roadmapped consumer (`.iter()`,
3692    /// `.len()`, `.is_empty()`, the
3693    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3694    /// input, `serde` slice-serialization) without leaking the backing
3695    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3696    /// reaches for (the storage-side `Vec` remains reachable through the
3697    /// `pub membros` field for the mutation-carrying serde round-trip
3698    /// and per-test fixture-mutation paths, including the
3699    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3700    /// the typed view). Named `membros()` to match the storage field's
3701    /// name verbatim and the tatara-lisp author-surface term
3702    /// (`:membros`) the field's own docstring already carries; the
3703    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3704    /// vocabulary the [`Caixa::membros`] field's docstring already
3705    /// reaches for ("Member Servicos that make up this Aplicacao").
3706    #[must_use]
3707    pub fn membros(&self) -> &[crate::aplicacao::Membro] {
3708        self.membros.as_slice()
3709    }
3710
3711    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3712    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3713    /// inter-Servico contract-list slice accessor every consumer of the
3714    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3715    /// slice-view keys off — returns the author-declared `:contratos`
3716    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3717    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3718    /// backing buffer the raw `self.contratos.as_slice()` field access
3719    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3720    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3721    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3722    /// single member with no inter-Servico edge carries; the returned
3723    /// `&[WitContract]` degenerates to an empty slice on those arms
3724    /// without any silent `None` collapse).
3725    ///
3726    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3727    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3728    /// container of every per-edge `{de, para, wit, endpoint | subject |
3729    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3730    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3731    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3732    /// adjacency-list seed dispatch on at mesh-artifact materialization
3733    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3734    /// `:membros` vertex set resolves against, closed by the
3735    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3736    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3737    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3738    /// per-edge axis threads through a lifted per-entry accessor on the
3739    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3740    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3741    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3742    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3743    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3744    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3745    /// and the WIT-world discriminant. Every downstream consumer of the
3746    /// mesh-graph edge path first passes through this outer accessor
3747    /// onto the slice and then dispatches per-contract through the
3748    /// inner accessors — the two-level dispatch means every
3749    /// per-`:contratos` reader now routes through a typed dispatch on
3750    /// the substrate primitive at both altitudes.
3751    ///
3752    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3753    /// accessed inline at two production sites in
3754    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3755    /// mesh-slot declared-slot enumerator's
3756    /// `!self.contratos.is_empty()` presence probe (which drives the
3757    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3758    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3759    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3760    /// typed-view composer's `self.contratos.clone()` per-contract
3761    /// fold-in path (which materializes the typed
3762    /// [`crate::aplicacao::AplicacaoSpec`] view every
3763    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3764    /// downstream `caixa-mesh` renderer dispatches on). A future
3765    /// extension of the outer `:contratos` axis (a per-cluster
3766    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3767    /// at admission time so a cluster-specific edge-set can tighten a
3768    /// caixa-declared list without re-authoring the `caixa.lisp`,
3769    /// promotion of the plain `Vec<WitContract>` to a richer
3770    /// `{static, dynamic}` partition once runtime-resolved contract
3771    /// edges land, per-edge policy annotation once the M4 per-edge
3772    /// policy overlay axis lands) would have had to be threaded through
3773    /// both open-coded copies in lockstep or one consumer would
3774    /// silently disagree with the peer on which edge slice a given
3775    /// Caixa resolves to — the enumerator's presence probe reading the
3776    /// raw slot while the peer view-composer's fold-in path read an
3777    /// operator-resolved slot would silently split the paired
3778    /// declared-slot enumerator and typed-view composition, a
3779    /// two-consumer split at the enumerator and the view composer far
3780    /// from the source `caixa.lisp` with no field naming the edge-set-
3781    /// drift root cause. Lifting the resolution rule to a typed method
3782    /// on the substrate primitive means every downstream consumer of
3783    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3784    /// reaches for exactly one typed dispatch — the resolver's
3785    /// accept-set migrates as a unit on any future axis addition.
3786    ///
3787    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3788    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3789    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3790    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3791    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3792    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3793    /// mesh-slot arm of the composite-slice sub-family the sibling
3794    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3795    /// Peer at the outer altitude of the closed inner-
3796    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3797    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3798    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3799    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3800    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3801    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3802    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3803    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3804    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3805    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3806    /// pattern onto the sibling M3 typed-composite-element axis
3807    /// ([`crate::aplicacao::WitContract`] composite, matching the
3808    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3809    /// different altitude).
3810    ///
3811    /// Returns `&[crate::aplicacao::WitContract]` (not
3812    /// `&Vec<WitContract>`) because every downstream consumer of the
3813    /// contract list treats it as a read-only sequence — the slice-view
3814    /// is the narrowest borrow that supports every present + roadmapped
3815    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3816    /// discriminant dispatch, `serde` slice-serialization) without
3817    /// leaking the backing `Vec`'s grow/push/reserve surface no
3818    /// consumer of the typed view reaches for (the storage-side `Vec`
3819    /// remains reachable through the `pub contratos` field for the
3820    /// mutation-carrying serde round-trip and per-test fixture-mutation
3821    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3822    /// clones the slot into the typed view). Named `contratos()` to
3823    /// match the storage field's name verbatim and the tatara-lisp
3824    /// author-surface term (`:contratos`) the field's own docstring
3825    /// already carries; the accessor's identity maps onto the canonical
3826    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3827    /// docstring already reaches for ("WIT-typed inter-Servico
3828    /// contracts").
3829    #[must_use]
3830    pub fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3831        self.contratos.as_slice()
3832    }
3833
3834    /// Compose the Aplicacao-related flat slots into a single typed
3835    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3836    /// downstream renderer consumption. Returns `None` when the
3837    /// caixa isn't a `:kind Aplicacao`.
3838    #[must_use]
3839    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3840        if !self.kind().is_aplicacao() {
3841            return None;
3842        }
3843        Some(crate::aplicacao::AplicacaoSpec {
3844            membros: self.membros().to_vec(),
3845            contratos: self.contratos().to_vec(),
3846            politicas: self.politicas().cloned().unwrap_or_default(),
3847            placement: self.placement().cloned().unwrap_or_default(),
3848            entrada: self.entrada().cloned(),
3849        })
3850    }
3851
3852    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3853    /// *declares* a value on, in canonical declaration order
3854    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3855    /// `:entrada`). A slot counts as declared when its backing field
3856    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3857    ///
3858    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3859    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3860    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3861    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3862    /// caixa-flux / caixa-helm renderers only emit them for an
3863    /// Aplicacao. On any *other* kind a declared mesh slot is the
3864    /// manifest field's documented "ignored otherwise" (see the
3865    /// `:membros` … `:entrada` field docs): it silently passes
3866    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3867    /// rendered — far from the source caixa.lisp.
3868    /// [`crate::StandardLayout::verify`] consults this to reject that
3869    /// silent-drop at caixa-build time
3870    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3871    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3872    /// a slot foreign to the kind is a build error, not a silent drop.
3873    ///
3874    /// Lifted as a typed method (rather than an inline disjunction at
3875    /// the verify call site) so the mesh-slot set lives in one place —
3876    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3877    /// overlay, distributed-app takeover config) is one push here, and
3878    /// every consumer reaching for "which mesh slots are set" (the
3879    /// verify gate, a future `feira lint` kind-coherence advisory)
3880    /// inherits the canonical order without rolling its own.
3881    ///
3882    /// Each per-arm kebab-case label is routed through the peer
3883    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3884    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3885    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3886    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3887    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3888    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3889    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3890    /// kebab-case label + renderer-side artifact key) route through one
3891    /// canonical declaration per arm — same discipline the peer
3892    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3893    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3894    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3895    /// axis, extended here to close the M3 mesh-slot author-facing-label
3896    /// axis so both altitudes of the typed-slot algebra
3897    /// (per-Servico M2 + per-Aplicacao M3) share the same
3898    /// "one canonical byte-string per arm, next to the axis" discipline.
3899    #[must_use]
3900    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3901        let mut slots = Vec::new();
3902        if !self.membros().is_empty() {
3903            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3904        }
3905        if !self.contratos().is_empty() {
3906            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3907        }
3908        if self.politicas().is_some() {
3909            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3910        }
3911        if self.placement().is_some() {
3912            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3913        }
3914        if self.entrada().is_some() {
3915            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3916        }
3917        slots
3918    }
3919
3920    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3921    /// caixa *declares* a value on, in canonical declaration order
3922    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3923    /// `:children`). A slot counts as declared when its backing field
3924    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3925    ///
3926    /// The supervisor-tree slots compose the typed OTP supervisor of a
3927    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3928    /// `:children` field docs above). [`Self::supervisor_view`] only
3929    /// folds them into a validatable [`SupervisorSpec`] when the kind
3930    /// matches (returns `None` otherwise), and the wasm-operator's
3931    /// hierarchical reconciler only consumes them for a Supervisor. On
3932    /// any *other* kind a declared supervisor slot is the manifest
3933    /// field's documented "ignored otherwise" (see the `:estrategia` …
3934    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3935    /// and then vanishes — never validated, never reconciled — far from
3936    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3937    /// this to reject that silent-drop at caixa-build time
3938    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3939    /// exact mirror of the [`Self::declared_mesh_slots`] /
3940    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3941    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3942    /// error, not a silent drop.
3943    #[must_use]
3944    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3945        let mut slots = Vec::new();
3946        if self.estrategia().is_some() {
3947            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3948        }
3949        if self.max_restarts().is_some() {
3950            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3951        }
3952        if self.restart_window().is_some() {
3953            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3954        }
3955        if !self.children().is_empty() {
3956            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3957        }
3958        slots
3959    }
3960
3961    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3962    /// caixa *declares* a value on, in canonical declaration order
3963    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3964    /// declared when its backing field carries a value — a `Some(...)`,
3965    /// or a non-empty `Vec`.
3966    ///
3967    /// The M2 slots configure the runtime of a long-running wasm
3968    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3969    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3970    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3971    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3972    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3973    /// emit these slots for a Servico; on any *other* kind a declared M2
3974    /// slot is the manifest field's documented "ignored otherwise": its
3975    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3976    /// but the value is never rendered into a chart / programs.yaml entry
3977    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
3978    /// vanishes, far from the source caixa.lisp.
3979    /// [`crate::StandardLayout::verify`] consults this to reject that
3980    /// silent-drop at caixa-build time
3981    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
3982    /// mirror of the [`Self::declared_mesh_slots`] /
3983    /// [`Self::declared_supervisor_slots`] gates on the peer
3984    /// kind-exclusive slot sets: a slot foreign to the kind is a build
3985    /// error, not a silent drop.
3986    ///
3987    /// Each per-arm kebab-case label is routed through the peer
3988    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
3989    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
3990    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
3991    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
3992    /// both halves of the M2 top-level slot's dual axis (author-facing
3993    /// kebab-case label + renderer-side camelCase overlay-container wire
3994    /// key) route through one canonical declaration per arm — same
3995    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
3996    /// author-label consts (889dc18) establish on the sibling
3997    /// per-callback axis inside the `:behavior` overlay block.
3998    #[must_use]
3999    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
4000        let mut slots = Vec::new();
4001        if self.limits().is_some() {
4002            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
4003        }
4004        if self.behavior().is_some() {
4005            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
4006        }
4007        if !self.upgrade_from().is_empty() {
4008            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
4009        }
4010        slots
4011    }
4012
4013    /// The kebab-case `:slot` tags of every code-surface slot this caixa
4014    /// declares a value on that its [`CaixaKind`] doesn't natively own,
4015    /// in canonical declaration order (`:exe` → `:servicos`). A
4016    /// code-surface slot is owned by exactly one kind: `:exe` by
4017    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
4018    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
4019    /// `ComputeUnit` daemon surface).
4020    ///
4021    /// Each is silently ignored when declared on the wrong kind: the
4022    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
4023    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
4024    /// code-running kind a declared `:exe` / `:servicos` is the manifest
4025    /// field's documented "ignored otherwise" — its path is checked for
4026    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
4027    /// (which run after [`Caixa::from_lisp`]), but the value is never
4028    /// rendered into a build target or programs.yaml entry. It silently
4029    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
4030    /// caixa.lisp, with no field naming which slot is foreign.
4031    ///
4032    /// [`crate::StandardLayout::verify`] consults this to reject that
4033    /// silent-drop at caixa-build time
4034    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
4035    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
4036    /// gates ([`Self::declared_servico_slots`] /
4037    /// [`Self::declared_supervisor_slots`] /
4038    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
4039    /// axis to be closed on the typed surface. The Supervisor /
4040    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
4041    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
4042    /// diagnostics — they fire ahead of this gate on the same `verify`
4043    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
4044    /// and this method is moot. For Biblioteca / Binario / Servico, this
4045    /// gate fires when a code-running kind declares another code-running
4046    /// kind's exclusive code surface.
4047    ///
4048    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
4049    /// may legitimately ship a `lib/` helper that the underlying
4050    /// substrate (the nix flake for Binario, the wasm component build
4051    /// for Servico) bundles into its build, so the slot's
4052    /// declared-on-wrong-kind cardinality isn't a structural error on
4053    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
4054    /// is the native case (the slot's owning kind). Supervisor /
4055    /// Aplicacao declaring `:bibliotecas` is gated upstream by
4056    /// [`crate::LayoutError::SupervisorOwnsCode`] /
4057    /// [`crate::LayoutError::AplicacaoOwnsCode`].
4058    ///
4059    /// Lifted as a typed method (rather than an inline disjunction at
4060    /// the verify call site) so the foreign-code-slot set lives in one
4061    /// place — a future kind that gains its own code-surface slot is
4062    /// one push here, and every consumer reaching for "which code
4063    /// surfaces are foreign to this kind" (the verify gate, a future
4064    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
4065    /// per-caixa build-target classifier) inherits the canonical order
4066    /// without rolling its own.
4067    #[must_use]
4068    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
4069        let mut slots = Vec::new();
4070        if !self.exe().is_empty() && !self.kind().requires_exe() {
4071            slots.push(":exe");
4072        }
4073        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
4074            slots.push(":servicos");
4075        }
4076        slots
4077    }
4078
4079    /// Validate every entry of `:deps` and `:deps-dev` through
4080    /// [`Dep::validate`] — closing the parity loop with the per-axis
4081    /// `:versao` gates already wired into the typed-graph
4082    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
4083    /// 9888b13) and typed supervisor tree
4084    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
4085    ///
4086    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
4087    /// were the only `:versao` axes still untyped past
4088    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
4089    /// as a String without parsing it, so a malformed-but-non-empty
4090    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
4091    /// silently passed parse and the `semver::Error` surfaced at
4092    /// lacre-resolve time, far from the source caixa.lisp, with no
4093    /// field naming which `:deps` entry carried the typo. Lifting the
4094    /// gate here makes the four `:versao` typed surfaces (`:deps`,
4095    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
4096    /// every requirement string past `validate_deps` is round-trippable
4097    /// through [`crate::parse_requirement`] without re-checking at the
4098    /// resolver layer.
4099    ///
4100    /// Both lists run through the same per-entry validator so a typo
4101    /// in `:deps-dev` surfaces with the same diagnostic as one in
4102    /// `:deps` — neither axis is a second-class citizen of the typed
4103    /// surface.
4104    ///
4105    /// Within each list, [`DepError::DuplicateNome`] closes the
4106    /// set-not-multiset discipline on the `:nome` axis: two entries
4107    /// naming the same caixa carry two `:versao` / `:fonte` / feature
4108    /// triples that the caixa-resolver's lacre pipeline collapses to one
4109    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
4110    /// silently overwrites the first at `concrete_versao`-resolve time
4111    /// (the same "second wins / one silently overwrites the other"
4112    /// shape the peer typed-graph duplicate gates already close on every
4113    /// other Vec-shaped authoring surface that keys by name). The
4114    /// duplicate check fires per-list and runs *after* each per-entry
4115    /// [`Dep::validate`] call so a malformed-and-duplicated entry
4116    /// surfaces its narrower per-entry diagnostic
4117    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
4118    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
4119    /// diagnostic — the canonical "per-entry shape before cross-entry
4120    /// uniqueness" precedence the peer `:children :caixa`
4121    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
4122    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
4123    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
4124    /// ([`crate::AplicacaoSpec::validate_placement`]),
4125    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
4126    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
4127    /// and the within-`:upgrade-from`-entry per-instruction-class
4128    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
4129    /// [`crate::UpgradeError::DuplicateStateChange`],
4130    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
4131    ///
4132    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
4133    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
4134    /// same name in both tables (the dev table's pin overrides the
4135    /// runtime table's pin in test/dev contexts), and caixa's surface
4136    /// mirrors that convention until a deliberate choice retires the
4137    /// override pattern. Only within-list duplicates are structurally
4138    /// incoherent — those are what this gate closes.
4139    pub fn validate_deps(&self) -> Result<(), DepError> {
4140        for &list in crate::dep::DepList::ALL {
4141            let mut seen = std::collections::HashSet::new();
4142            for dep in self.deps_of(list) {
4143                dep.validate()?;
4144                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
4145                    DepError::DuplicateNome {
4146                        nome: dep.nome().to_string(),
4147                        list: list.as_str(),
4148                    }
4149                })?;
4150            }
4151        }
4152        Ok(())
4153    }
4154
4155    /// Reject `:nome` values the K8s apiserver would refuse at admission
4156    /// time. The top-level Caixa identity flows directly into every
4157    /// substrate-side artifact's `metadata.name` axis: the
4158    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
4159    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
4160    /// aggregator keys ComputeUnit derivation off
4161    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
4162    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
4163    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
4164    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
4165    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
4166    /// ([`caixa-mesh::lib::cilium_network_policies`],
4167    /// [`caixa-mesh::lib::gateway_routes`]), and the default
4168    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
4169    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
4170    /// schema enforces the DNS-1123 label rule on admission; a
4171    /// structurally invalid `:nome` (`"MyApp"` — the canonical
4172    /// "I copied the display name verbatim" footgun, `"my_app"` — the
4173    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
4174    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
4175    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
4176    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
4177    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
4178    /// failure surfaced at `kubectl apply` time as a `metadata.name:
4179    /// Invalid value` rejection on whichever derived artifact admitted
4180    /// first, far from the source `caixa.lisp` and without any field
4181    /// naming the offending `:nome`.
4182    ///
4183    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
4184    /// substrate-side predicate the per-axis name gates already share:
4185    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
4186    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
4187    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
4188    /// diagnostic is self-locating (the offending `:nome` is named
4189    /// verbatim) and the author can grep their `caixa.lisp` for
4190    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
4191    /// every per-axis sibling gate already exposes
4192    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
4193    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
4194    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
4195    ///
4196    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
4197    /// derive macro stores the raw String) is gated by the narrower
4198    /// [`ManifestError::NomeEmpty`] arm before the predicate is
4199    /// consulted, mirroring the empty-first cascade every per-axis
4200    /// name gate already uses (e.g. `MembroCaixaEmpty` before
4201    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
4202    pub fn validate_nome(&self) -> Result<(), ManifestError> {
4203        // Routes through the shared
4204        // [`crate::render::require_valid_dns_1123_label`] gate the peer
4205        // name axes each land on so drift between the eight axes'
4206        // accepted DNS-1123-label sets is structurally impossible.
4207        let nome = self.nome();
4208        crate::render::require_valid_dns_1123_label(
4209            nome,
4210            || ManifestError::NomeEmpty,
4211            |reason| ManifestError::NomeInvalid {
4212                nome: nome.to_string(),
4213                reason,
4214            },
4215        )
4216    }
4217
4218    /// Reject `:nome` values whose joint length with the canonical
4219    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4220    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4221    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4222    /// substrate carries materializes the caixa's `:nome` through the
4223    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4224    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4225    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4226    /// `ChartDir.name` + `Chart.yaml::name`
4227    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4228    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4229    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4230    /// `oci://<registry>/lareira-<nome>` chart ref
4231    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4232    /// admission rule strict-parses against DNS-1123-label, the Helm
4233    /// operator's tracking-secret name is derived from `release_name`
4234    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4235    /// K8s object `metadata.name` axes embed the chart name as a
4236    /// prefix — every one fails admission on a > 63-byte chart name.
4237    ///
4238    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4239    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4240    /// `:nome` of 56–63 bytes silently passed validate (the inner
4241    /// DNS-1123 check accepts the bare `:nome`) but produced a
4242    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4243    /// rejected at admission — far from the source `caixa.lisp`, with
4244    /// no field naming the overflow root cause. The
4245    /// [`lareira_chart_name`] helper's own doc comment
4246    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4247    /// "the M4 admission webhook will pin the joint-length invariant
4248    /// when it lands". This gate lands the invariant at the
4249    /// manifest-validate layer rather than waiting for the apiserver
4250    /// — the same fail-at-the-source posture every peer per-axis
4251    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4252    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4253    /// `:edicao`, etc.) takes.
4254    ///
4255    /// Thin wrapper around
4256    /// [`crate::render::is_lareira_chart_name_shape`] (the
4257    /// substrate-side predicate that composes [`lareira_chart_name`] +
4258    /// [`is_dns_1123_label`] via the lifted
4259    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4260    /// shared parser-shaped reason into the
4261    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4262    /// diagnostic is self-locating (the offending `:nome` is named
4263    /// verbatim alongside the rendered chart name and the budget) and
4264    /// the author can shorten in one edit. The gate runs across every
4265    /// `:kind` — `:nome` is the substrate-wide identity axis any
4266    /// future renderer the substrate adds can derive a
4267    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4268    /// the drift footgun where a future kind grows a chart-emitting
4269    /// render path while the validate cascade doesn't catch it.
4270    ///
4271    /// Runs *after* [`Self::validate_nome`] so the narrower
4272    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4273    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4274    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4275    /// specific shape error rather than the chart-name-budget error,
4276    /// preserving the legitimate "well-shaped `:nome` that happens to
4277    /// overflow the joint cap" arm for this gate.
4278    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4279        let nome = self.nome();
4280        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
4281            ManifestError::NomeChartNameBudgetExceeded {
4282                nome: nome.to_string(),
4283                reason,
4284            }
4285        })
4286    }
4287
4288    /// Reject `:versao` values that don't parse as [`semver::Version`].
4289    /// The top-level Caixa version flows directly into every
4290    /// substrate-side artifact that carries a "this is which version of
4291    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4292    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4293    /// SemVer-2-strict at `helm template` / `helm install` time per
4294    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4295    /// `feira publish` Zig-style `v<versao>` git tag
4296    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4297    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4298    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4299    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4300    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4301    /// `skopeo push`, the lacre closure's pinned versions
4302    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4303    /// `:upgrade-from :from` references peers in this exact `versao`
4304    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4305    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4306    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4307    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4308    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4309    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4310    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4311    /// into the version field a peer `:deps :versao` accepts;
4312    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4313    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4314    /// derive macro stores the raw String) and the failure surfaced at
4315    /// the *first* downstream consumer that strict-parses it: at
4316    /// `helm install` time as a chart-version rejection, at
4317    /// `feira publish` time as a malformed git tag, at lacre-resolve
4318    /// time as a `semver::Error` not naming the offending caixa, at
4319    /// `feira upgrade --to <versao>` time as an unresolvable
4320    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4321    /// and without any field naming the offending `:versao`.
4322    ///
4323    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4324    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4325    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4326    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4327    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4328    /// variant, carrying the offending `:versao` verbatim + a
4329    /// parser-shaped reason naming the specific violation, so the
4330    /// diagnostic is self-locating (the author can grep their
4331    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4332    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4333    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4334    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4335    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4336    /// now structurally equivalent (every value past validate is
4337    /// round-trippable through [`semver::Version::parse`] without
4338    /// re-checking at the renderer, resolver, or operator hot-upgrade
4339    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4340    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4341    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4342    ///
4343    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4344    /// the derive macro stores the raw String) is gated by the
4345    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4346    /// consulted, mirroring the empty-first cascade every per-axis
4347    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4348    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4349    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4350    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4351        let versao = self.versao();
4352        if versao.is_empty() {
4353            return Err(ManifestError::VersaoEmpty);
4354        }
4355        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
4356            versao: versao.to_string(),
4357            reason: e.to_string(),
4358        })?;
4359        Ok(())
4360    }
4361
4362    /// Reject `:restart-window` values the shared
4363    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4364    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4365    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4366    /// `Option<Duration>` routed through the shared codec via `with =
4367    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4368    /// view-construction path ([`Self::supervisor_view`]) folds the
4369    /// raw string through the same shared codec and soft-swallows the
4370    /// parse error as `None` to keep the view best-effort. Without
4371    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4372    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4373    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4374    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4375    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4376    /// edge case) silently produced a `SupervisorSpec` with
4377    /// `restart_window: None`, indistinguishable from the canonical
4378    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4379    /// `MaxIntensity / Period` invariant turns into a never-reset
4380    /// supervisor far from the source `caixa.lisp`, with no field
4381    /// naming the offending `:restart-window`. Lifting the gate to a
4382    /// Caixa-level validator mirrors the trajectory of the peer
4383    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4384    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4385    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4386    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4387    ///
4388    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4389    /// (the shared codec backing `:supervisor :restart-window` as
4390    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4391    /// `:politicas :circuit-breaker :window` — all three covered by
4392    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4393    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4394    /// variant, carrying the offending raw string + a parser-shaped
4395    /// reason naming the canonical authoring form, so the diagnostic
4396    /// is self-locating (the author can grep their `caixa.lisp` for
4397    /// `:restart-window "<value>"` and fix it in one edit) and
4398    /// uniform with every other manifest-level validate diagnostic.
4399    /// With this gate the four `:restart-window`-shaped surfaces (the
4400    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4401    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4402    /// now structurally equivalent — every value past the codec is in
4403    /// one accepted set, by construction.
4404    ///
4405    /// `None` (the canonical "omit the slot to express no reset"
4406    /// shape) is accepted trivially — the gate is a no-op when the
4407    /// author didn't author a window. The empty string is rejected by
4408    /// the shared codec (its digit-only gate refuses an empty
4409    /// magnitude), surfacing the same `RestartWindowMalformed`
4410    /// diagnostic as every other rejected non-canonical shape.
4411    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4412        let Some(s) = self.restart_window() else {
4413            return Ok(());
4414        };
4415        crate::supervisor::duration_codec::parse(s)
4416            .map(|_| ())
4417            .map_err(|reason| ManifestError::RestartWindowMalformed {
4418                restart_window: s.to_string(),
4419                reason,
4420            })
4421    }
4422
4423    /// Reject per-entry values on the three Caixa-level code-surface
4424    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
4425    /// layout checker's `root.join(p)` sandbox would silently subvert.
4426    /// Same three structural footguns the peer
4427    /// [`BehaviorSpec::validate`] (b0c8389) and
4428    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
4429    /// (26da2c7) already close on the M2 `:behavior :on-*` and
4430    /// `:upgrade-from :state-change :script` axes, here lifted onto
4431    /// the three top-level code-path axes through the shared
4432    /// [`is_sandboxed_relative_path`] predicate:
4433    ///
4434    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
4435    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
4436    ///     [`Path::join`] as the base itself — `root.join("")` ==
4437    ///     `root`, so the existence check (`self.exists(&root)`)
4438    ///     trivially passes (the project root exists), and the layout
4439    ///     silently treats the project root as a biblioteca / exe /
4440    ///     servico entry. The `:bibliotecas` loop then hands the root
4441    ///     to `tatara_lisp::read` at `feira build` time as if the root
4442    ///     directory itself were a Lisp source file — a parse error
4443    ///     far from the source `caixa.lisp` with no field naming the
4444    ///     offending entry.
4445    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
4446    ///     [`Path::join`] *replaces* the base when the right-hand side
4447    ///     is absolute, so `root.join("/etc/passwd")` resolves to
4448    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
4449    ///     The existence check then silently consults whatever the
4450    ///     escaped path resolves to — for `:bibliotecas`, the layout
4451    ///     has no `starts_with`-fence (only `:exe` is fenced under
4452    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
4453    ///     `:bibliotecas` entry that happens to resolve on disk
4454    ///     silently passes. For `:exe` / `:servicos` the fence catches
4455    ///     the absolute case downstream as `ExeOutsideDir` /
4456    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
4457    ///     doesn't exist), but with a downstream-shaped diagnostic
4458    ///     that names the resolved escape path rather than the
4459    ///     authoring footgun at the source.
4460    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
4461    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
4462    ///     [`std::path::Component::ParentDir`] anywhere round-trips
4463    ///     through [`Path::join`] as a traversal above the caixa root.
4464    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
4465    ///     *component-aware* (not canonical-path-aware), so
4466    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
4467    ///     is **true** even though the canonical resolution
4468    ///     `{parent of root}/escape.lisp` lives outside the caixa root
4469    ///     — the fence silently lets the parent-escape through, and
4470    ///     the existence check passes if that escape-target happens
4471    ///     to exist. Caught regardless of where the `..` sits
4472    ///     (leading, mid-path, trailing) so the gate matches the peer
4473    ///     predicate's full coverage.
4474    ///
4475    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
4476    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
4477    /// same per-slot diagnostic shape every peer per-axis path-gate
4478    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
4479    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
4480    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
4481    /// order [`Caixa::declared_foreign_code_slots`] uses for its
4482    /// canonical foreign-code-slot diagnostic, so a manifest with
4483    /// multiple malformed slots surfaces the lexicographically-earliest
4484    /// slot's diagnostic deterministically.
4485    ///
4486    /// Lifted to the typed surface as a Caixa-level validator (peer
4487    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
4488    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
4489    /// and wired into [`crate::StandardLayout::verify`] before the
4490    /// existence-check loops so the diagnostic names the offending
4491    /// slot at the source caixa.lisp rather than reporting a
4492    /// downstream `MissingEntry` / `ExeOutsideDir` /
4493    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
4494    /// The fourth typed code-path surface — every author-supplied
4495    /// path on the manifest — is now structurally accept-shaped
4496    /// past validate, peer with `:behavior :on-*` and
4497    /// `:upgrade-from :state-change :script`.
4498    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
4499        /// Per-slot file-type contract for the three Caixa-level
4500        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
4501        /// Each variant names the predicate the per-entry file-type
4502        /// gate consults; [`Self::None`] opts the slot out of any
4503        /// file-type contract. Lifted as a typed local enum so the
4504        /// per-slot dispatch is exhaustive at the `match` — adding a
4505        /// future axis to the typed-substrate `:` slot set (the
4506        /// future `:assets` resource axis the M5 roadmap names, the
4507        /// future `:nix-flake` derivation axis the caixa-flake
4508        /// emitter consults) lands as one variant + one `match` arm,
4509        /// not a coordinated rewrite of every per-slot bool flag.
4510        ///
4511        /// Peer of the typed-substrate per-slot variant disciplines
4512        /// already established on this surface
4513        /// ([`crate::supervisor::RestartStrategy`] +
4514        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
4515        /// supervision-tree axis,
4516        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
4517        /// placement axis, [`crate::aplicacao::WitTarget`] on the
4518        /// `:contratos` payload-target axis): the typed `enum` is
4519        /// the substrate's single source of truth for the per-axis
4520        /// dispatch, and every consumer (the per-arm body here, the
4521        /// future feira-lint per-slot diagnostic renderer, the M4
4522        /// per-axis admission webhook) reaches for the same typed
4523        /// surface rather than re-deriving the partition from inline
4524        /// flag combinations.
4525        enum CodePathFileType {
4526            /// `:exe` — nix-build derivation output, no terminating-
4527            /// extension contract (the canonical `"exe/<name>"`
4528            /// fixtures the layout's `ExeOutsideDir` error message
4529            /// documents carry no extension by convention).
4530            None,
4531            /// `:bibliotecas` — tatara-lisp source files the
4532            /// `feira build` loop reads through `tatara_lisp::read`
4533            /// at parse time. Routes to [`is_lisp_extension`].
4534            LispSource,
4535            /// `:servicos` — ComputeUnit-CR YAML files the
4536            /// caixa-helm / caixa-flux renderers consume through
4537            /// `serde_yaml::from_str`. Routes to
4538            /// [`is_computeunit_yaml_extension`].
4539            ComputeUnitYaml,
4540        }
4541
4542        // The per-slot [`CodePathFileType`] selects which axes carry the
4543        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
4544        // source axis (the `feira build` loop at
4545        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
4546        // `tatara_lisp::read` at parse time) — the lifted
4547        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
4548        // `:exe` is the nix-built executable surface (per the canonical
4549        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
4550        // error message documents and every in-tree
4551        // `caixa_with_code_paths` positive control uses) — its file-type
4552        // contract is "nix-build derivation output", not a typed source
4553        // file, so [`CodePathFileType::None`] opts the slot out of any
4554        // file-type gate. `:servicos` is the `.computeunit.yaml`
4555        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
4556        // renderers consume each entry through `serde_yaml::from_str` as
4557        // a typed `ComputeUnit` CR) — the lifted
4558        // [`is_computeunit_yaml_extension`] predicate gates the compound
4559        // `.computeunit.yaml` suffix. All three axes are surfaced through
4560        // the same iteration so the sandbox-shape + duplicate gates
4561        // apply uniformly; the typed file-type dispatch fires per-slot
4562        // exactly where the downstream consumer's accepted set demands
4563        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
4564        // compounding lift on the peer 64772a9 `:bibliotecas`
4565        // `.lisp`-gate trajectory — the second of the three code-path
4566        // axes to land on a typed compound-suffix gate, with the same
4567        // self-locating per-slot diagnostic shape every peer per-axis
4568        // file-type lift uses (`*NonLispExtension { slot, path }` /
4569        // `*NonComputeUnitYamlExtension { slot, path }`).
4570        for (slot, list, file_type) in [
4571            (
4572                ":bibliotecas",
4573                &self.bibliotecas,
4574                CodePathFileType::LispSource,
4575            ),
4576            (":exe", &self.exe, CodePathFileType::None),
4577            (
4578                ":servicos",
4579                &self.servicos,
4580                CodePathFileType::ComputeUnitYaml,
4581            ),
4582        ] {
4583            // Per-slot set-not-multiset gate on the typed code-path axis.
4584            // Every peer Vec-shaped author-supplied list past validate is
4585            // a set, not a multiset: `:membros :caixa`
4586            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
4587            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
4588            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
4589            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
4590            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
4591            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
4592            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
4593            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
4594            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
4595            // the three code-path lists are the last Vec-shaped author-
4596            // supplied slots on the typed Caixa surface still admitting a
4597            // duplicate entry silently. Scope is per-list (`:bibliotecas`
4598            // duplicates are flagged within `:bibliotecas`, not across
4599            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
4600            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
4601            // legitimate dev-vs-runtime shape on the dep axis, fenced
4602            // separately by [`crate::dep::validate_no_self_dep`]). On the
4603            // code-path axis a cross-slot collision is structurally
4604            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
4605            // fence — `:exe` and `:servicos` entries are confined to their
4606            // own directory trees, so the only way a string could appear
4607            // on two code-path lists is the (rare, structurally invalid)
4608            // case where `:bibliotecas` carries an `"exe/<x>"` or
4609            // `"servicos/<x>.yaml"`-shaped path.
4610            //
4611            // Without the gate three authoring footguns silently passed:
4612            //
4613            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
4614            //     canonical copy-paste-the-wrong-file footgun. `feira
4615            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
4616            //     list and re-parses the same file twice, wasting work
4617            //     and silently masking the author's intent to declare a
4618            //     *second* biblioteca.
4619            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
4620            //     Binario surface. The future `caixa-flake` `nix flake`
4621            //     emitter that materializes each `:exe` entry as a flake
4622            //     `packages.<exe-name>` derivation would collide on the
4623            //     duplicate package name and surface a flake-eval error
4624            //     far from the source `caixa.lisp`.
4625            //   - `:servicos ("servicos/x.computeunit.yaml"
4626            //     "servicos/x.computeunit.yaml")` — the same footgun on
4627            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
4628            //     renderers already refuse `:servicos.len() != 1` with
4629            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
4630            //     that diagnostic surfaces "too many servicos" without
4631            //     naming "duplicate entry" — the typed self-locating
4632            //     "which entry is the duplicate" framing only lands at
4633            //     this gate.
4634            //
4635            // Same `seen.insert(entry.as_str())` shape every peer per-list
4636            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
4637            // 86c769b, `:deps` 359fba5) and the same "structural shape
4638            // checks fire before the duplicate check on the same entry"
4639            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
4640            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
4641            // empty entry first, not the duplicate on the later pair).
4642            let mut seen = std::collections::HashSet::new();
4643            for entry in list {
4644                let path = Path::new(entry);
4645                match is_sandboxed_relative_path(path) {
4646                    Ok(()) => {}
4647                    Err(PathShapeViolation::Empty) => {
4648                        return Err(ManifestError::CodePathEmpty { slot });
4649                    }
4650                    Err(PathShapeViolation::Absolute) => {
4651                        return Err(ManifestError::CodePathAbsolute {
4652                            slot,
4653                            path: path.to_path_buf(),
4654                        });
4655                    }
4656                    Err(PathShapeViolation::ParentEscape) => {
4657                        return Err(ManifestError::CodePathParentEscape {
4658                            slot,
4659                            path: path.to_path_buf(),
4660                        });
4661                    }
4662                }
4663                // The per-slot file-type gate dispatched through the
4664                // typed [`CodePathFileType`] selector above. Each variant
4665                // routes to the lifted predicate the downstream consumer
4666                // demands:
4667                //
4668                //   - [`LispSource`] → [`is_lisp_extension`] for
4669                //     `:bibliotecas` (the `feira build` loop's
4670                //     `tatara_lisp::read` consumer);
4671                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
4672                //     for `:servicos` (the caixa-helm / caixa-flux
4673                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
4674                //     accepted set);
4675                //   - [`None`] for `:exe` — the nix-build derivation-
4676                //     output axis has no terminating-extension contract.
4677                //
4678                // Fires after the sandbox-shape arms so a path that is
4679                // *both* sandbox-escaping and wrong-extension surfaces
4680                // the more fundamental sandbox-shape diagnostic first
4681                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
4682                // `ParentEscape` → `NonLispExtension` arm-ordering on
4683                // `:behavior :on-*` c97815a, and `EmptyScript` →
4684                // `AbsoluteScript` → `ParentEscapeScript` →
4685                // `NonLispExtensionScript` on
4686                // `:upgrade-from :state-change :script` 33cc830), and
4687                // before the duplicate gate so the narrower per-entry
4688                // file-type shape dominates the cross-entry uniqueness
4689                // diagnostic (a
4690                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
4691                // `:servicos` surfaces
4692                // `CodePathNonComputeUnitYamlExtension` on the first
4693                // entry rather than `CodePathDuplicate` on the pair —
4694                // peer with the 64772a9 `:bibliotecas`
4695                // `("lib/x.txt" "lib/x.txt")` ordering).
4696                match file_type {
4697                    CodePathFileType::None => {}
4698                    CodePathFileType::LispSource => {
4699                        if !is_lisp_extension(path) {
4700                            return Err(ManifestError::CodePathNonLispExtension {
4701                                slot,
4702                                path: path.to_path_buf(),
4703                            });
4704                        }
4705                    }
4706                    CodePathFileType::ComputeUnitYaml => {
4707                        if !is_computeunit_yaml_extension(path) {
4708                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
4709                                slot,
4710                                path: path.to_path_buf(),
4711                            });
4712                        }
4713                    }
4714                }
4715                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
4716                    ManifestError::CodePathDuplicate {
4717                        slot,
4718                        path: path.to_path_buf(),
4719                    }
4720                })?;
4721            }
4722        }
4723        Ok(())
4724    }
4725
4726    /// Reject `:etiquetas` lists with an empty entry or with two entries
4727    /// agreeing on the same string. `:etiquetas` is the universal
4728    /// registry-search-tag axis on [`Caixa`] (every kind carries the
4729    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4730    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
4731    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
4732    /// a [`std::collections::BTreeSet`] alongside the four substrate-
4733    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
4734    /// Two authoring footguns silently passed validate without this gate:
4735    ///
4736    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
4737    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
4738    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
4739    ///     `chart.metadata.keywords` admits the value without a strict
4740    ///     parser-side gate, but the empty keyword has no operational
4741    ///     meaning — it indexes nothing in the future caixa-registry
4742    ///     search axis and clutters the rendered chart with a no-op tag.
4743    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
4744    ///     copy-paste-the-wrong-tag footgun) silently passed validate
4745    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
4746    ///     at chart render — a "second wins / one silently disappears"
4747    ///     shape divergent from every peer typed-graph set gate
4748    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
4749    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
4750    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4751    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4752    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4753    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
4754    ///     on `:upgrade-from`, the per-instruction-class singularity
4755    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
4756    ///     [`crate::UpgradeError::DuplicateStateChange`] /
4757    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
4758    ///     discipline is uniform: every Vec-shaped author-supplied list
4759    ///     past validate is set-not-multiset, by construction.
4760    ///
4761    /// Past the empty arm the gate enforces the chart-keyword shape
4762    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
4763    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
4764    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
4765    /// continuation. Closes the canonical paste-from-doc footguns the
4766    /// bare empty + duplicate arms left open: paste-from-aligned-doc
4767    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
4768    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
4769    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
4770    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
4771    /// — the author meant three separate list entries), path-separator
4772    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
4773    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
4774    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
4775    /// control bytes that would silently land as malformed search tags
4776    /// in the rendered Chart.yaml `keywords:` array and break the
4777    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
4778    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
4779    /// established on the sibling universal-axis `Vec<String>` surface
4780    /// — the second universal-axis Vec<String> surface to land the
4781    /// empty-first-then-shape-then-duplicate per-entry cascade.
4782    ///
4783    /// Same empty-first cascade discipline every peer per-axis gate
4784    /// uses: the per-entry empty arm fires before the per-entry shape
4785    /// arm fires before the cross-entry duplicate arm, so an
4786    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
4787    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
4788    /// has no value" defect) before either the shape or the duplicate
4789    /// diagnostic. Walks the list in declaration order so the
4790    /// first-collision diagnostic surfaces the lexicographically-
4791    /// earliest offending position, peer with every other duplicate
4792    /// gate on this surface.
4793    ///
4794    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
4795    /// caixa-build gate alongside the peer universal gates
4796    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4797    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
4798    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
4799    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4800    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4801    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4802    /// slot sets. The future caixa-registry search axis can reach for
4803    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
4804    /// chart-keyword-shaped string without re-deriving the precondition.
4805    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
4806        let mut seen = std::collections::HashSet::new();
4807        for etiqueta in self.etiquetas() {
4808            if etiqueta.is_empty() {
4809                return Err(ManifestError::EtiquetaEmpty);
4810            }
4811            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
4812                ManifestError::EtiquetaInvalid {
4813                    etiqueta: etiqueta.clone(),
4814                    reason,
4815                }
4816            })?;
4817            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
4818                ManifestError::EtiquetaDuplicate {
4819                    etiqueta: etiqueta.clone(),
4820                }
4821            })?;
4822        }
4823        Ok(())
4824    }
4825
4826    /// Reject `:autores` lists with an empty entry or with two entries
4827    /// agreeing on the same string. `:autores` is the universal
4828    /// maintainer-axis on [`Caixa`] (every kind carries the
4829    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4830    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
4831    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
4832    /// to a `Maintainer { name, email: None }` without dedup). Two
4833    /// authoring footguns silently passed validate without this gate:
4834    ///
4835    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
4836    ///     blank-doc footgun) rendered as
4837    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
4838    ///     empty maintainer name has no operational meaning — it
4839    ///     identifies no one in the substrate's authorship index and
4840    ///     clutters the rendered chart with a no-op maintainer.
4841    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
4842    ///     the copy-paste-the-wrong-author footgun) silently passed
4843    ///     validate and rendered as two identical maintainer entries.
4844    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
4845    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
4846    ///     rendered `keywords:` array at chart-render time), the
4847    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
4848    ///     entries stack verbatim in the chart, divergent from every
4849    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
4850    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
4851    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4852    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4853    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4854    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
4855    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
4856    ///     `:etiquetas`).
4857    ///
4858    /// Past the empty arm the gate enforces the chart-maintainer-name
4859    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
4860    /// the structural single-line printable-UTF-8 floor every realistic
4861    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
4862    /// or trailing whitespace, no ASCII control characters anywhere,
4863    /// Unicode bytes accepted. Closes the canonical paste-from-doc
4864    /// footguns the bare empty + duplicate arms left open:
4865    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
4866    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
4867    /// pasted a multi-line block of author records into one `:autores`
4868    /// entry instead of splitting into one entry per author),
4869    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
4870    /// and the paste-from-binary-blob control bytes that would silently
4871    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
4872    /// `maintainers:` array. Mirrors the shape-predicate cascade
4873    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4874    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
4875    /// establish past their own empty arms on the sibling universal-axis
4876    /// `Option<String>` surfaces — the first universal-axis Vec<String>
4877    /// surface to land the empty-first-then-shape-then-duplicate per-entry
4878    /// cascade.
4879    ///
4880    /// Same empty-first cascade discipline every peer per-axis gate
4881    /// uses: the per-entry empty arm fires before the per-entry shape
4882    /// arm before the cross-entry duplicate arm. Walks the list in
4883    /// declaration order so the first-collision diagnostic surfaces the
4884    /// lexicographically-earliest offending position, peer with every
4885    /// other duplicate gate on this surface.
4886    ///
4887    /// Universal-axis (every kind carries `:autores`), so wired at the
4888    /// caixa-build gate alongside the peer universal gates
4889    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4890    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4891    /// [`Self::validate_code_paths`] — before the kind-coherence gates
4892    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4893    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4894    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4895    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4896    /// slot sets.
4897    pub fn validate_autores(&self) -> Result<(), ManifestError> {
4898        let mut seen = std::collections::HashSet::new();
4899        for autor in self.autores() {
4900            if autor.is_empty() {
4901                return Err(ManifestError::AutorEmpty);
4902            }
4903            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
4904                ManifestError::AutorInvalid {
4905                    autor: autor.clone(),
4906                    reason,
4907                }
4908            })?;
4909            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
4910                ManifestError::AutorDuplicate {
4911                    autor: autor.clone(),
4912                }
4913            })?;
4914        }
4915        Ok(())
4916    }
4917
4918    /// Reject `:repositorio` values whose shape the shared
4919    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
4920    /// `repositorio: Option<String>` slot on [`Caixa`] is the
4921    /// universal git-shaped homepage axis every kind carries — the
4922    /// substrate routes the same string through two load-bearing
4923    /// consumers:
4924    ///
4925    ///   - [`caixa-helm`] folds it verbatim into the rendered
4926    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
4927    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
4928    ///     the chart `README.md` `repo = …` interpolation
4929    ///     (`caixa-helm/src/lib.rs:359`).
4930    ///   - [`caixa-flux`] folds it verbatim into the standalone
4931    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
4932    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
4933    ///     `GitRepository.spec.url` the cluster's source-controller
4934    ///     polls — the load-bearing deploy-time axis.
4935    ///
4936    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4937    /// substitute a placeholder when the slot is absent (`None` → the
4938    /// fallback fires); a `Some("")` *skips the fallback* and silently
4939    /// passes the empty string through to `Chart.yaml home: ""` /
4940    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
4941    /// controller both reject the empty URL far from the source
4942    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
4943    /// Similarly a malformed `:repositorio` (whitespace, control char,
4944    /// missing `:` separator, leading `-`) silently lands in the
4945    /// rendered artifacts and breaks at `git clone` / `helm template`
4946    /// / `flux reconcile` time.
4947    ///
4948    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
4949    /// same shared predicate the peer [`crate::DepSource::validate`]
4950    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
4951    /// gate the two `git URL`-shaped surfaces on the typed Caixa
4952    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
4953    /// structurally equivalent: every value past validate is
4954    /// guaranteed-acceptable by the predicate's union of constraints
4955    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
4956    /// control chars, ASCII only, no leading `:`, contains a `:`
4957    /// separator). The predicate accepts every documented authoring
4958    /// shape — `github:org/repo` shorthand, `https://host/path`,
4959    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
4960    /// scp-style SSH, `file:///path` — and refuses the canonical
4961    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
4962    /// injection footguns at validate time. Maps the predicate's
4963    /// `String` reason verbatim into the
4964    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
4965    /// offending value + parser-shaped reason so the diagnostic is
4966    /// self-locating (the author can grep their `caixa.lisp` for
4967    /// `:repositorio "<value>"` and fix it in one edit).
4968    ///
4969    /// `None` (the canonical "omit the slot to express no published
4970    /// homepage" shape) is accepted trivially — the gate is a no-op
4971    /// when the author didn't declare a value. `Some("")` is gated by
4972    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
4973    /// shape predicate is consulted, mirroring the empty-first cascade
4974    /// every peer per-axis identity gate uses
4975    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
4976    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
4977    /// [`crate::DepError::FonteRepoEmpty`] →
4978    /// [`crate::DepError::FonteRepoInvalid`]).
4979    ///
4980    /// Universal-axis (every kind carries `:repositorio`), so wired at
4981    /// the caixa-build gate alongside the peer universal gates
4982    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4983    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4984    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
4985    /// before the kind-coherence gates
4986    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4987    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4988    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4989    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4990    /// specific slot sets.
4991    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
4992        let Some(s) = self.repositorio() else {
4993            return Ok(());
4994        };
4995        if s.is_empty() {
4996            return Err(ManifestError::RepositorioEmpty);
4997        }
4998        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
4999            repositorio: s.to_string(),
5000            reason,
5001        })
5002    }
5003
5004    /// Reject `:descricao` values that are the empty string. The flat
5005    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
5006    /// free-form-prose homepage axis every kind carries — the
5007    /// substrate routes the same string through two load-bearing
5008    /// consumers in the [`caixa-helm`] renderer:
5009    ///
5010    ///   - `build_chart_yaml` folds it verbatim into the rendered
5011    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
5012    ///     field (`caixa-helm/src/lib.rs:232-235`).
5013    ///   - `build_readme` folds it verbatim into the rendered chart
5014    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
5015    ///
5016    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
5017    /// substitute a `caixa.nome`-derived placeholder when the slot is
5018    /// absent (`None` → the fallback fires); a `Some("")` *skips the
5019    /// fallback* and silently passes the empty string through to
5020    /// `Chart.yaml description: ""` / a blank chart `README.md`
5021    /// header. Helm's chart spec requires a non-empty `description:`
5022    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
5023    /// `WARNING [chart.metadata.description]: description is required`),
5024    /// so the empty `Some("")` silently lands in the rendered
5025    /// artifacts and breaks at `helm lint` / `helm install` time far
5026    /// from the source `caixa.lisp`, with no field naming the
5027    /// offending `:descricao`.
5028    ///
5029    /// `None` (the canonical "omit the slot to defer to the renderer's
5030    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
5031    /// the gate is a no-op when the author didn't declare a value.
5032    /// `Some("")` is gated by the narrower
5033    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
5034    /// shape every peer per-axis empty gate uses
5035    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5036    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5037    /// [`ManifestError::RepositorioEmpty`]).
5038    ///
5039    /// Universal-axis (every kind carries `:descricao`), so wired at
5040    /// the caixa-build gate alongside the peer universal gates
5041    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5042    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5043    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5044    /// [`Self::validate_code_paths`] — before the kind-coherence
5045    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5046    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5047    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5048    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5049    /// specific slot sets.
5050    ///
5051    /// Past the empty arm the gate enforces the chart-description
5052    /// shape predicate via [`crate::render::is_chart_description_shape`]:
5053    /// the structural single-line UTF-8 floor every realistic chart
5054    /// description in the wild matches — 1..=512 bytes, no leading
5055    /// or trailing whitespace, no ASCII control characters anywhere
5056    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
5057    /// carriage return, and every other control byte), Unicode
5058    /// continuation bytes accepted (the canonical fixtures carry
5059    /// `→` and `—`). Closes the canonical paste-from-doc footguns
5060    /// the bare empty-arm gate left open: paste-from-aligned-doc
5061    /// leading / trailing whitespace (`" Checkout flow."`,
5062    /// `"Checkout flow. "`), paste-from-multiline-doc newline
5063    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
5064    /// (`"Checkout\rflow."`), tab-from-aligned-doc
5065    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
5066    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
5067    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
5068    /// [`Self::validate_edicao`] establish past their own empty arms
5069    /// on the sibling universal-axis `Option<String>` Caixa-level
5070    /// value-shape surfaces.
5071    ///
5072    /// The empty-first cascade discipline mirrors every peer per-axis
5073    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
5074    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
5075    /// diagnostic surfaces on `Some("")` rather than the broader
5076    /// shape-predicate diagnostic — peer with how
5077    /// [`ManifestError::LicencaEmpty`] runs before
5078    /// [`ManifestError::LicencaInvalid`],
5079    /// [`ManifestError::EdicaoEmpty`] runs before
5080    /// [`ManifestError::EdicaoInvalid`],
5081    /// [`ManifestError::RepositorioEmpty`] runs before
5082    /// [`ManifestError::RepositorioInvalid`].
5083    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
5084        let Some(s) = self.descricao() else {
5085            return Ok(());
5086        };
5087        if s.is_empty() {
5088            return Err(ManifestError::DescricaoEmpty);
5089        }
5090        crate::render::is_chart_description_shape(s).map_err(|reason| {
5091            ManifestError::DescricaoInvalid {
5092                descricao: s.to_string(),
5093                reason,
5094            }
5095        })?;
5096        Ok(())
5097    }
5098
5099    /// Reject `:licenca` values that are the empty string. The flat
5100    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
5101    /// SPDX-shaped license-expression axis every kind carries — the
5102    /// substrate routes the same string through the [`caixa-helm`]
5103    /// renderer's `build_readme` which folds it verbatim into the
5104    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
5105    /// section (`caixa-helm/src/lib.rs:361`) via
5106    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
5107    /// fallback only fires on `None`; a `Some("")` *skips the
5108    /// fallback* and silently passes the empty string through to a
5109    /// chart `README.md` whose `License` section renders as the bare
5110    /// trailing period (`.\n`) — peer footgun with the
5111    /// `Some("")`-skips-`unwrap_or_else` shape the
5112    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
5113    /// gates close on the sibling free-form-prose and git-URL axes.
5114    ///
5115    /// `None` (the canonical "omit the slot to defer to the
5116    /// renderer's `MIT` fallback" shape every existing fixture
5117    /// carries) is accepted trivially — the gate is a no-op when the
5118    /// author didn't declare a value. `Some("")` is gated by the
5119    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
5120    /// empty-arm shape every peer per-axis empty gate uses
5121    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5122    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5123    /// [`ManifestError::RepositorioEmpty`],
5124    /// [`ManifestError::DescricaoEmpty`]).
5125    ///
5126    /// Universal-axis (every kind carries `:licenca`), so wired at
5127    /// the caixa-build gate alongside the peer universal gates
5128    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5129    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5130    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5131    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
5132    /// — before the kind-coherence gates
5133    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5134    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5135    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5136    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5137    /// specific slot sets.
5138    ///
5139    /// Past the empty arm the gate enforces the SPDX-expression shape
5140    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
5141    /// structural alphabet floor every realistic SPDX expression in
5142    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
5143    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
5144    /// single ASCII space (token separator). Closes the canonical
5145    /// paste-from-doc footguns the bare empty-arm gate left open:
5146    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
5147    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
5148    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
5149    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
5150    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
5151    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
5152    /// Apache-2.0"`), and semicolon-list-separator confusion
5153    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
5154    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
5155    /// establish past their own empty arms.
5156    ///
5157    /// The empty-first cascade discipline mirrors every peer per-axis
5158    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
5159    /// [`ManifestError::LicencaInvalid`], so the narrower empty
5160    /// diagnostic surfaces on `Some("")` rather than the broader
5161    /// shape-predicate diagnostic — peer with how
5162    /// [`ManifestError::EdicaoEmpty`] runs before
5163    /// [`ManifestError::EdicaoInvalid`],
5164    /// [`ManifestError::RepositorioEmpty`] runs before
5165    /// [`ManifestError::RepositorioInvalid`].
5166    ///
5167    /// A future tightening on this axis can extend the alphabet
5168    /// floor into a full SPDX expression parser + license-id
5169    /// allowlist (rejecting alphabet-valid values that don't name a
5170    /// real SPDX license identifier — e.g., `"NotAReal"` is
5171    /// alphabet-valid but no `NotAReal` license-id exists). That
5172    /// parser only becomes meaningful past a real SPDX-spec
5173    /// dependency; this gate establishes the structural floor by
5174    /// refusing every non-SPDX-alphabet value at validate time.
5175    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
5176        let Some(s) = self.licenca() else {
5177            return Ok(());
5178        };
5179        if s.is_empty() {
5180            return Err(ManifestError::LicencaEmpty);
5181        }
5182        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
5183            ManifestError::LicencaInvalid {
5184                licenca: s.to_string(),
5185                reason,
5186            }
5187        })?;
5188        Ok(())
5189    }
5190
5191    /// Reject `:edicao` values that are the empty string. The flat
5192    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
5193    /// language-edition axis every kind carries — it determines the
5194    /// tatara-lisp macro surface + compatibility flags the substrate
5195    /// applies when building a caixa, and lands verbatim in the
5196    /// `Caixa::template` author-time scaffold (the canonical
5197    /// `:edicao "2026"` line every `feira init` emits via
5198    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
5199    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
5200    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
5201    /// `caixa-core/src/render.rs:2510`) via
5202    /// `edicao: Some("2026".into())`.
5203    ///
5204    /// `None` (the canonical "omit the slot to defer to the
5205    /// substrate's default edition" shape every existing
5206    /// [`caixa-resolver`] integration test fixture carries via
5207    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5208    /// is accepted trivially — the gate is a no-op when the author
5209    /// didn't declare a value. `Some("")` is gated by the narrower
5210    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
5211    /// shape every peer per-axis empty gate uses
5212    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5213    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5214    /// [`ManifestError::RepositorioEmpty`],
5215    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
5216    ///
5217    /// Universal-axis (every kind carries `:edicao`), so wired at
5218    /// the caixa-build gate alongside the peer universal gates
5219    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5220    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5221    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5222    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
5223    /// [`Self::validate_code_paths`] — before the kind-coherence
5224    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5225    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5226    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5227    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5228    /// specific slot sets.
5229    ///
5230    /// Past the empty arm the gate enforces the canonical year-shape
5231    /// predicate: every documented tatara-lisp edition is a 4-digit
5232    /// ASCII decimal year (`"2026"` is the only edition currently
5233    /// minted; future-introduced siblings will follow the same
5234    /// shape, peer with Cargo's `[package] edition` grammar which
5235    /// every value Cargo has ever accepted matches — `"2015"`,
5236    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
5237    /// 4 ASCII decimal bytes is rejected with the narrower
5238    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
5239    /// shape-predicate cascade [`Self::validate_repositorio`]
5240    /// establishes past its own empty arm
5241    /// ([`ManifestError::RepositorioEmpty`] →
5242    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
5243    /// paste-from-doc footguns the bare empty-arm gate left open:
5244    ///
5245    ///   - leading / trailing whitespace from a paste-from-doc
5246    ///     (`"2026 "`, `" 2026"`)
5247    ///   - control characters / CRLF from a paste-from-multiline-doc
5248    ///     (`"2026\n"`)
5249    ///   - non-ASCII look-alikes from a fullwidth keyboard
5250    ///     (`"2026"`) which would silently land as a non-ASCII
5251    ///     string in the rendered caixa.lisp
5252    ///   - free-form non-year values (`"x"`, `"latest"`,
5253    ///     `"nightly"`) that have no operational meaning on the
5254    ///     substrate's build-time edition selector
5255    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
5256    ///     `"r2026"`) — common version-tag idioms that don't apply
5257    ///     to the year-shaped edition axis
5258    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
5259    ///     edition is a year, not a fractional version
5260    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
5261    ///     `"00026"`) that don't name a year
5262    ///
5263    /// `None` (the canonical "omit the slot to defer to the
5264    /// substrate's default edition" shape every existing
5265    /// [`caixa-resolver`] integration test fixture carries via
5266    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5267    /// is accepted trivially — the gate is a no-op when the author
5268    /// didn't declare a value. The empty-first cascade discipline
5269    /// mirrors every peer per-axis identity gate:
5270    /// [`ManifestError::EdicaoEmpty`] runs before
5271    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
5272    /// diagnostic surfaces on `Some("")` rather than the broader
5273    /// shape-predicate diagnostic — peer with how
5274    /// [`ManifestError::NomeEmpty`] runs before
5275    /// [`ManifestError::NomeInvalid`],
5276    /// [`ManifestError::VersaoEmpty`] runs before
5277    /// [`ManifestError::VersaoInvalid`],
5278    /// [`ManifestError::RepositorioEmpty`] runs before
5279    /// [`ManifestError::RepositorioInvalid`].
5280    ///
5281    /// A future tightening on this axis can extend the shape
5282    /// predicate into a known-edition allowlist (rejecting
5283    /// year-shaped values that don't name a tatara-lisp edition
5284    /// the substrate actually understands — e.g., `"1999"` is
5285    /// year-shaped but no `1999` edition exists). That allowlist
5286    /// only becomes meaningful past the introduction of a sibling
5287    /// edition to `"2026"`; this gate establishes the structural
5288    /// floor by refusing every non-year-shaped value at validate
5289    /// time.
5290    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
5291        let Some(s) = self.edicao() else {
5292            return Ok(());
5293        };
5294        if s.is_empty() {
5295            return Err(ManifestError::EdicaoEmpty);
5296        }
5297        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
5298            return Err(ManifestError::EdicaoInvalid {
5299                edicao: s.to_string(),
5300                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
5301            });
5302        }
5303        Ok(())
5304    }
5305
5306    /// Compose the supervisor-related flat slots into a single
5307    /// [`SupervisorSpec`] for validation. Returns `None` when the
5308    /// caixa isn't a `:kind Supervisor`.
5309    ///
5310    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
5311    /// simple (one form, no nested `:supervisor (…)` block); this view
5312    /// is the "typed shape" the operator + supervisor reconciler
5313    /// consume.
5314    #[must_use]
5315    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
5316        if !self.kind().is_supervisor() {
5317            return None;
5318        }
5319        // Fold through the shared `supervisor::duration_codec::parse`
5320        // — the same parser the serde-routed `with = "duration_codec"`
5321        // on `SupervisorSpec::restart_window`, the `:politicas
5322        // :timeout` codec, and the `:politicas :circuit-breaker
5323        // :window` codec all consume. The prior inline f64-shaped
5324        // duplicate (`parse_window_inline`) admitted every magnitude
5325        // the integer-magnitude gate (1c55a2a) rejects on the three
5326        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
5327        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
5328        // `None` (i.e. "no reset"), divergent from the shared codec's
5329        // integer-magnitude discipline by construction. The fold
5330        // closes the divergence: every value the typed
5331        // `SupervisorSpec` carries past `supervisor_view` is in the
5332        // shared codec's accepted set. The `.ok()` here preserves the
5333        // existing soft-swallow shape on this view-construction path;
5334        // the new [`Caixa::validate_restart_window`] (sibling of
5335        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
5336        // the offending raw string at build time so authoring tools
5337        // (`feira lint`, the future layout-side wire-up) surface a
5338        // self-locating diagnostic instead of a silently dropped
5339        // window.
5340        let restart_window = self
5341            .restart_window()
5342            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
5343        Some(SupervisorSpec {
5344            // Route the author-omitted `:estrategia` arm through the
5345            // substrate-canonical
5346            // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
5347            // `pub const` rather than the transitively-derived
5348            // [`RestartStrategy::default`] route the prior
5349            // `.unwrap_or_default()` fold reached for — one source of
5350            // truth for the Erlang/OTP `one_for_one` half of Learn You
5351            // Some Erlang's `{one_for_one, intensity, 5, 60}` worker-
5352            // supervisor canonical default that also backs the
5353            // [`crate::supervisor::Default for RestartStrategy`] impl
5354            // and the [`crate::supervisor::Default for SupervisorSpec`]
5355            // impl's struct-literal `estrategia` field, all now routed
5356            // through the same lifted constant. Prior to the lift the
5357            // composition site carried `.unwrap_or_default()` with no
5358            // compile-time link back to the shared OTP-canonical
5359            // default that the peer paired
5360            // `.unwrap_or(SUPERVISOR_MAX_RESTARTS_DEFAULT)` (b698ec0)
5361            // arm on the sibling `:max-restarts` axis routes through —
5362            // so a future rebrand of the OTP-canonical strategy default
5363            // (a widening to `rest_for_one` once the substrate
5364            // discovers startup-order-coupled child cohorts as the more
5365            // common shape, a per-cluster overlay the operator pins
5366            // through the MESH-COMPOSITION §III.2 supervision-canary
5367            // `:estrategia-overrides` roadmap slot) would have had to
5368            // migrate the paired `MaxIntensity` + `Period` halves
5369            // through the lifted constants and the `one_for_one` half
5370            // through a `RestartStrategy::default()` route in lockstep
5371            // or the three halves of the same OTP-canonical default
5372            // would silently drift out of pairing. Byte-parity against
5373            // the lifted constant closes the split. Pinned by
5374            // [`supervisor_view_estrategia_fallback_routes_through_lifted_default`]
5375            // in the tests module.
5376            estrategia: self
5377                .estrategia()
5378                .unwrap_or(crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT),
5379            // Route the author-omitted `:max-restarts` arm through the
5380            // substrate-canonical [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`]
5381            // typed `pub const` rather than the raw `5` literal — one
5382            // source of truth for the Erlang/OTP-canonical
5383            // `{intensity, 5, 60}` `MaxIntensity` default that also
5384            // backs the serde-side wire-format author-omitted arm on
5385            // [`crate::supervisor::SupervisorSpec::max_restarts`] via
5386            // `#[serde(default = "default_max_restarts")]` and the
5387            // [`Default for SupervisorSpec`] impl's struct-literal
5388            // default field. Prior to the lift the composition site
5389            // carried a raw `5` with no compile-time link back to the
5390            // serde-side default, so a future rebrand of the OTP-
5391            // canonical default (a tightening to Elixir's `3`, a
5392            // widening to a per-cluster overlay the operator pins
5393            // through the MESH-COMPOSITION §III.2 supervision-canary
5394            // `:supervisor :max-restarts-overrides` roadmap slot)
5395            // would have had to be threaded through both open-coded
5396            // copies in lockstep or the wire-format author-omitted arm
5397            // and this view-construction author-omitted arm would
5398            // silently disagree on which restart-budget an omitted
5399            // `:max-restarts` resolves to. Pinned by
5400            // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
5401            // in the tests module.
5402            max_restarts: self
5403                .max_restarts()
5404                .unwrap_or(crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT),
5405            restart_window,
5406            children: self.children().to_vec(),
5407        })
5408    }
5409
5410    /// A minimal starter manifest emitted by `feira init`.
5411    #[must_use]
5412    pub fn template(nome: &str) -> String {
5413        format!(
5414            "(defcaixa\n  \
5415               :nome        {nome:?}\n  \
5416               :versao      \"0.1.0\"\n  \
5417               :kind        Biblioteca\n  \
5418               :edicao      \"2026\"\n  \
5419               :descricao   \"FIXME — describe this caixa\"\n  \
5420               :autores     ()\n  \
5421               :etiquetas   ()\n  \
5422               :deps        ()\n  \
5423               :deps-dev    ()\n  \
5424               :bibliotecas (\"lib/{nome}.lisp\"))\n"
5425        )
5426    }
5427
5428    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
5429    /// back after mutation (e.g. `feira add`).
5430    ///
5431    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
5432    /// The derive-macro `compile_from_sexp` path is the inverse, so any
5433    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
5434    #[must_use]
5435    pub fn to_lisp(&self) -> String {
5436        let json = serde_json::to_value(self).expect("Caixa serialize");
5437        let sexp = tatara_lisp::domain::json_to_sexp(&json);
5438        let tatara_lisp::Sexp::List(items) = sexp else {
5439            return format!("(defcaixa {sexp})\n");
5440        };
5441        let mut out = String::from("(defcaixa");
5442        let mut i = 0;
5443        while i + 1 < items.len() {
5444            out.push_str("\n  ");
5445            out.push_str(&items[i].to_string());
5446            out.push(' ');
5447            out.push_str(&items[i + 1].to_string());
5448            i += 2;
5449        }
5450        out.push_str(")\n");
5451        out
5452    }
5453}
5454
5455/// Errors raised by top-level [`Caixa`] validators that don't fit
5456/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
5457/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
5458/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
5459/// through every substrate-side artifact's `metadata.name` /
5460/// version derivation.
5461///
5462/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
5463/// doc-comment anticipates) can hold one of each per-axis error
5464/// family without reshaping individual diagnostics; this enum is
5465/// the first such per-Caixa-identity family.
5466#[derive(Debug, Error, PartialEq, Eq)]
5467pub enum ManifestError {
5468    #[error(
5469        ":nome is empty (every caixa must name itself; the value flows \
5470         into every K8s artifact's `metadata.name` derivation and into \
5471         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
5472    )]
5473    NomeEmpty,
5474    #[error(
5475        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
5476         apiserver enforces this rule on every `metadata.name` the \
5477         caixa's substrate-side renderers derive from `:nome` — the \
5478         `lareira-<nome>` Helm chart name, the programs.yaml entry \
5479         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
5480         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
5481         name; use a lowercase alphanumeric + hyphen identifier like \
5482         `\"checkout\"` or `\"cart-v2\"`)"
5483    )]
5484    NomeInvalid { nome: String, reason: String },
5485    #[error(
5486        ":nome {nome:?} overflows the joint-length budget on the canonical \
5487         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
5488         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
5489         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
5490         `chart:` slot, `caixa-tatara`'s `release_name` + \
5491         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
5492         joint name through the canonical `lareira_chart_name` helper, and \
5493         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
5494         DNS-1123 label cap on every chart-name-derived `metadata.name` \
5495         reject any joint name exceeding 63 bytes; the narrower \
5496         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
5497         arm gates the chart-name budget downstream renderers inherit)"
5498    )]
5499    NomeChartNameBudgetExceeded { nome: String, reason: String },
5500    #[error(
5501        ":versao is empty (every caixa must pin its own version; the value flows \
5502         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
5503         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
5504         `:latest` tags, the lacre closure's `concrete_versao`, and the \
5505         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
5506    )]
5507    VersaoEmpty,
5508    #[error(
5509        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
5510         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
5511         with optional `-prerelease` and `+build` — across every artifact derived \
5512         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
5513         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
5514         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
5515         and the `:upgrade-from :from` peers that match against this exact shape; \
5516         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
5517         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
5518         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
5519    )]
5520    VersaoInvalid { versao: String, reason: String },
5521    #[error(
5522        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
5523         substrate consumes this string through the shared \
5524         `supervisor::duration_codec` — the same parser routed via `with = \
5525         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
5526         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
5527         the canonical authoring form is `<integer><unit>` where the unit is one \
5528         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
5529         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
5530         Without this gate a malformed `:restart-window` silently produced a \
5531         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
5532         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
5533         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
5534         layer with the offending value named verbatim. Omit the slot entirely to \
5535         express \"no reset\"; carry a positive integer duration to express the \
5536         sliding window)"
5537    )]
5538    RestartWindowMalformed {
5539        restart_window: String,
5540        reason: String,
5541    },
5542    #[error(
5543        "{slot} entry is an empty path string — every {slot} entry must name \
5544         a file relative to the caixa root; omit the entry to omit the file \
5545         (the layout checker's `root.join(\"\")` resolves to the caixa root \
5546         itself, so an empty entry silently aliases the project root as a \
5547         declared {slot} file, then fails downstream at parse / existence \
5548         time with a diagnostic that names the root rather than the offending \
5549         entry)"
5550    )]
5551    CodePathEmpty { slot: &'static str },
5552    #[error(
5553        "{slot} entry {} is an absolute path — entries must be relative to \
5554         the caixa root, since `Path::join` replaces the base with an absolute \
5555         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
5556         outside the caixa root sandbox; rewrite the entry as a relative path \
5557         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
5558         `\"servicos/<name>.computeunit.yaml\"`)",
5559        path.display()
5560    )]
5561    CodePathAbsolute { slot: &'static str, path: PathBuf },
5562    #[error(
5563        "{slot} entry {} contains a `..` component — entries must not traverse \
5564         above the caixa root (the layout's `starts_with(<dir>)` fence on \
5565         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
5566         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
5567         has no such fence, so a leading `..` escapes unconditionally if the \
5568         resolved target happens to exist)",
5569        path.display()
5570    )]
5571    CodePathParentEscape { slot: &'static str, path: PathBuf },
5572    #[error(
5573        "{slot} entry {} does not terminate in the `.lisp` extension — every \
5574         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
5575         loop reads through `tatara_lisp::read` at parse time, so any other \
5576         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
5577         structurally a parser error far from the source caixa.lisp, with \
5578         no field naming the offending `:bibliotecas` entry. Pin a relative \
5579         path under the caixa root whose terminating extension is \
5580         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
5581         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
5582         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
5583         (33cc830) axes already carry through the same lifted \
5584         `is_lisp_extension` predicate",
5585        path.display()
5586    )]
5587    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
5588    #[error(
5589        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
5590         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
5591         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
5592         through `serde_yaml::from_str` at chart / FluxCD bundle render \
5593         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
5594         off-by-one-segment `.computeunit-yaml`, the editor-backup \
5595         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
5596         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
5597         source caixa.lisp, with no field naming the offending `:servicos` \
5598         entry. Pin a relative path under the caixa root whose terminating \
5599         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
5600         `\"servicos/<name>.computeunit.yaml\"`, \
5601         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
5602         contract the sibling `:bibliotecas` axis (64772a9) already carries \
5603         on the tatara-lisp-source axis through the peer lifted \
5604         `is_lisp_extension` predicate, here on the compound-suffix axis \
5605         `Path::extension` can't express on its own through the lifted \
5606         `is_computeunit_yaml_extension` predicate",
5607        path.display()
5608    )]
5609    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
5610    #[error(
5611        "{slot} entry {} appears more than once (the code-path list is \
5612         a set, not a multiset; every peer Vec-shaped author-supplied \
5613         list past validate is set-not-multiset — `:membros :caixa`, \
5614         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5615         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
5616         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
5617         code-path lists are the last Vec-shaped author-supplied slots on \
5618         the typed Caixa surface still admitting a duplicate entry. \
5619         `:bibliotecas` duplicates re-parse the same file at \
5620         `feira build` time and silently mask the author's intent to \
5621         declare a *second* biblioteca; `:exe` duplicates collide on the \
5622         flake `packages.<name>` derivation key at the future \
5623         `caixa-flake` materializer; `:servicos` duplicates surface as the \
5624         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
5625         rejection far from the source `caixa.lisp`. Drop the duplicate \
5626         or rename it to the actual second file intended)",
5627        path.display()
5628    )]
5629    CodePathDuplicate { slot: &'static str, path: PathBuf },
5630    #[error(
5631        ":etiquetas entry is empty (every tag must carry a non-empty \
5632         registry-search identifier; the empty entry has no operational \
5633         meaning — it indexes nothing in the future caixa-registry search \
5634         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
5635         with a no-op tag; omit the entry to express \"no tag on this \
5636         position\")"
5637    )]
5638    EtiquetaEmpty,
5639    #[error(
5640        ":etiquetas entry {etiqueta:?} appears more than once (the \
5641         registry-search tag set is a set, not a multiset; duplicate \
5642         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
5643         at chart render — a \"second wins / one silently disappears\" \
5644         shape divergent from every peer typed-graph set gate \
5645         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
5646         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
5647         duplicate or rename it to the actual tag intended)"
5648    )]
5649    EtiquetaDuplicate { etiqueta: String },
5650    #[error(
5651        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
5652         {reason} (the substrate consumes this string through the shared \
5653         `crate::render::is_chart_keyword_shape` predicate — the same \
5654         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
5655         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
5656         continuation. The canonical authoring shapes are short kebab-case \
5657         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
5658         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
5659         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
5660         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
5661         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
5662         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
5663         `\"mesh,http,grpc\"` — the author meant to author three separate \
5664         list entries; path-separator confusion `\"caixa/servico\"`; \
5665         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
5666         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
5667         `\"café\"` — every legitimate search tag is strict ASCII; \
5668         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
5669         passed `from_lisp` + `validate_etiquetas` + \
5670         `StandardLayout::verify` and landed in the rendered \
5671         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
5672         malformed search tag — Artifact Hub's keyword index + the future \
5673         caixa-registry's keyword index would either silently drop the \
5674         tag or fail to index it far from the source caixa.lisp; the gate \
5675         moves the diagnostic to the manifest layer with the offending \
5676         value named verbatim)"
5677    )]
5678    EtiquetaInvalid { etiqueta: String, reason: String },
5679    #[error(
5680        ":autores entry is empty (every maintainer must carry a non-empty \
5681         identifier; the empty entry has no operational meaning — it \
5682         identifies no one in the substrate's authorship index and renders \
5683         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
5684         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
5685         omit the entry to express \"no maintainer on this position\")"
5686    )]
5687    AutorEmpty,
5688    #[error(
5689        ":autores entry {autor:?} appears more than once (the maintainer \
5690         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
5691         `maintainers:` rendering does *no* dedup — duplicate entries \
5692         stack verbatim in `Chart.yaml` as two identical \
5693         `Maintainer {{ name, email: None }}` records, divergent from every \
5694         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
5695         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5696         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
5697         rename it to the actual author intended)"
5698    )]
5699    AutorDuplicate { autor: String },
5700    #[error(
5701        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
5702         {reason} (the substrate consumes this string through the shared \
5703         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
5704         single-line-UTF-8 floor every realistic chart maintainer name carries: \
5705         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
5706         characters anywhere, Unicode bytes accepted. The canonical authoring \
5707         shapes are short single-line identifiers like `\"pleme-io\"`, \
5708         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
5709         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
5710         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
5711         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
5712         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
5713         records into one entry instead of splitting into one entry per author; \
5714         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
5715         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
5716         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5717         `validate_autores` + `StandardLayout::verify` and landed in the \
5718         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
5719         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
5720         round-trip — every chart-aware UI (`helm list`, `helm search`, \
5721         Artifact Hub maintainer index) would render the maintainer name in a \
5722         single-line column far from the source caixa.lisp; the gate moves the \
5723         diagnostic to the manifest layer with the offending value named \
5724         verbatim)"
5725    )]
5726    AutorInvalid { autor: String, reason: String },
5727    #[error(
5728        ":repositorio is the empty string (every published caixa names its \
5729         git source via a non-empty `:repositorio` locator — the value \
5730         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
5731         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
5732         `GitRepository.spec.url` via `caixa-flux`'s \
5733         `ClusterBundleOpts::for_caixa`; both consumers' \
5734         `Option::unwrap_or_else` fallbacks only fire when the slot is \
5735         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
5736         `url: \"\"` in the rendered artifacts and breaks at `helm \
5737         template` / FluxCD source-controller reconcile time far from the \
5738         source caixa.lisp; omit the slot entirely to defer to the \
5739         renderer's `https://github.com/pleme-io/<nome>` / \
5740         `caixa.nome`-derived fallback, or carry a canonical authoring \
5741         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
5742         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
5743         `\"file:///path\"`)"
5744    )]
5745    RepositorioEmpty,
5746    #[error(
5747        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
5748         (the substrate consumes this string through the shared \
5749         `crate::render::is_git_repo_url` predicate — the same parser the \
5750         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
5751         value through via `DepSource::validate`; the canonical authoring \
5752         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
5753         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
5754         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
5755         scp-style SSH form. Without this gate a malformed `:repositorio` \
5756         (whitespace from a paste-from-doc; control characters / CRLF \
5757         from a paste-from-multiline-doc; a leading `-` from a \
5758         CLI-argument-injection footgun; a missing `:` separator from a \
5759         bare `org/repo` shape git treats as a relative filesystem path) \
5760         silently landed in the rendered `Chart.yaml home:` and the \
5761         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
5762         FluxCD reconcile time far from the source caixa.lisp; the gate \
5763         moves the diagnostic to the manifest layer with the offending \
5764         value named verbatim)"
5765    )]
5766    RepositorioInvalid { repositorio: String, reason: String },
5767    #[error(
5768        ":descricao is the empty string (every published caixa names \
5769         its purpose via a non-empty `:descricao` summary — the value \
5770         flows verbatim into the rendered `lareira-<nome>` Helm \
5771         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
5772         `build_chart_yaml` and into the chart `README.md` header via \
5773         `build_readme`; both consumers' `Option::unwrap_or_else` \
5774         `caixa.nome`-derived fallbacks only fire when the slot is \
5775         `None`, so an empty `Some(\"\")` silently lands as \
5776         `description: \"\"` / a blank `README.md` header in the \
5777         rendered artifacts and breaks at `helm lint` time \
5778         (`WARNING [chart.metadata.description]: description is \
5779         required` on `apiVersion: v2` charts) far from the source \
5780         caixa.lisp; omit the slot entirely to defer to the \
5781         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
5782         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
5783         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
5784         Servico.\"`)"
5785    )]
5786    DescricaoEmpty,
5787    #[error(
5788        ":descricao {descricao:?} is not a valid chart-description shape: \
5789         {reason} (the substrate consumes this string through the shared \
5790         `crate::render::is_chart_description_shape` predicate — the same \
5791         single-line-UTF-8 floor every realistic chart description carries: \
5792         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
5793         characters anywhere, Unicode prose bytes accepted. The canonical \
5794         authoring shapes are short single-line summaries like `\"Canonical \
5795         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
5796         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
5797         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
5798         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
5799         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
5800         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
5801         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
5802         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5803         `validate_descricao` + `StandardLayout::verify` and landed in the \
5804         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
5805         field + `README.md` header paragraph as a YAML-illegal multi-line \
5806         scalar or a silently-trimmed whitespace round-trip — every \
5807         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
5808         render the description in a single-line column far from the source \
5809         caixa.lisp; the gate moves the diagnostic to the manifest layer \
5810         with the offending value named verbatim)"
5811    )]
5812    DescricaoInvalid { descricao: String, reason: String },
5813    #[error(
5814        ":licenca is the empty string (every published caixa names \
5815         its license via a non-empty `:licenca` SPDX expression — the \
5816         value flows verbatim into the rendered `lareira-<nome>` Helm \
5817         chart's `README.md` `## License` section via `caixa-helm`'s \
5818         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
5819         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
5820         only fires when the slot is `None`, so an empty `Some(\"\")` \
5821         silently lands as a bare trailing period in the rendered \
5822         chart `README.md` `License` section far from the source \
5823         caixa.lisp; omit the slot entirely to defer to the \
5824         renderer's `MIT` fallback, or carry a canonical SPDX \
5825         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
5826         `\"Apache-2.0 OR MIT\"`)"
5827    )]
5828    LicencaEmpty,
5829    #[error(
5830        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
5831         (the substrate consumes this string through the shared \
5832         `crate::render::is_spdx_expression_shape` predicate — the same \
5833         alphabet-floor parser every peer per-axis value-shape gate routes \
5834         its value through; the canonical authoring shapes are single \
5835         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
5836         compound expressions like `\"Apache-2.0 OR MIT\"`, \
5837         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
5838         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
5839         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
5840         like `\"LicenseRef-MyLicense\"` / \
5841         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
5842         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
5843         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
5844         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
5845         a smart-quote paste; underscore-instead-of-hyphen typo \
5846         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
5847         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
5848         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
5849         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
5850         `README.md` `## License` section + a future SPDX-aware \
5851         `Chart.yaml license:` emitter would refuse the value at \
5852         `helm lint` time far from the source caixa.lisp; the gate moves \
5853         the diagnostic to the manifest layer with the offending value \
5854         named verbatim)"
5855    )]
5856    LicencaInvalid { licenca: String, reason: String },
5857    #[error(
5858        ":edicao is the empty string (every published caixa names \
5859         its language edition via a non-empty `:edicao` value — the \
5860         edition determines the tatara-lisp macro surface + \
5861         compatibility flags the substrate applies when building \
5862         the caixa; the canonical `Caixa::template` scaffold every \
5863         `feira init` emits carries `:edicao \"2026\"` verbatim and \
5864         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
5865         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
5866         construction, so an empty `Some(\"\")` silently lands as a \
5867         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
5868         a future renderer-side consumer that folds it through \
5869         `Option::unwrap_or_else` will skip the fallback and pass the \
5870         empty edition through to the substrate's build-time edition \
5871         selector far from the source caixa.lisp; omit the slot \
5872         entirely to defer to the substrate's default edition, or \
5873         carry a canonical edition like `\"2026\"`)"
5874    )]
5875    EdicaoEmpty,
5876    #[error(
5877        ":edicao {edicao:?} is not a valid edition: {reason} (every \
5878         documented tatara-lisp edition is a 4-digit ASCII decimal \
5879         year — `\"2026\"` is the only edition currently minted; \
5880         future-introduced siblings will follow the same shape, peer \
5881         with Cargo's `[package] edition` grammar which every value \
5882         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
5883         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
5884         paste-from-doc footguns silently passed: a trailing space \
5885         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
5886         from a paste-from-multiline-doc, a fullwidth-keyboard \
5887         look-alike (`\"2026\"`), a free-form non-year value \
5888         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
5889         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
5890         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
5891         wrong-length numeric value (`\"26\"`, `\"202\"`, \
5892         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
5893         rendered caixa.lisp and broke at the substrate's \
5894         build-time edition selector far from the source caixa.lisp; \
5895         omit the slot entirely to defer to the substrate's default \
5896         edition, or carry a canonical 4-digit ASCII decimal year \
5897         like `\"2026\"`)"
5898    )]
5899    EdicaoInvalid { edicao: String, reason: String },
5900}
5901
5902#[cfg(test)]
5903mod tests {
5904    use super::*;
5905
5906    #[test]
5907    fn template_round_trips() {
5908        let src = Caixa::template("demo");
5909        let c = Caixa::from_lisp(&src).expect("template must parse");
5910        assert_eq!(c.nome, "demo");
5911        assert_eq!(c.versao, "0.1.0");
5912        assert_eq!(c.kind, CaixaKind::Biblioteca);
5913        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
5914        assert!(c.deps.is_empty());
5915        assert!(c.deps_dev.is_empty());
5916    }
5917
5918    #[test]
5919    fn caixa_universal_axis_scalar_accessor_pair_is_const_fn() {
5920        // Fail-before-pass-after pin on [`Caixa::nome`] +
5921        // [`Caixa::versao`]'s `const`-eval-surface posture. Each
5922        // accessor projects the top-level manifest's per-`:nome` /
5923        // per-`:versao` [`String`] storage through the `pub const fn`
5924        // [`String::as_str`] (const-stable since Rust 1.87, well within
5925        // the workspace MSRV) — any future accidental downgrade to
5926        // non-`const` fails the corresponding `<name>_via_const_fn`
5927        // wrapper at caixa-core build time with E0015 (`cannot call
5928        // non-const method`), strictly stronger than a runtime
5929        // `assert!`. Sibling of the peer per-M2/M3-slot `String → &str`
5930        // scalar-accessor family pins on the sibling `const`-eval-
5931        // surface passes ([`crate::CaixaVersion::as_str`] at the
5932        // typed-newtype wrapper, [`crate::aplicacao::Membro::nome`] /
5933        // [`crate::aplicacao::Membro::versao_requirement`] at the M3
5934        // membership axis, [`crate::aplicacao::Entrada::hostname`] /
5935        // [`crate::aplicacao::Entrada::destination`] at the M3 ingress
5936        // axis, [`crate::supervisor::ChildSpec::nome`] /
5937        // [`crate::supervisor::ChildSpec::versao_requirement`] at the
5938        // M2 supervisor-tree axis,
5939        // [`crate::upgrade::UpgradeFromEntry::prior_versao`] at the M2
5940        // upgrade axis, [`crate::dep::Dep::nome`] /
5941        // [`crate::dep::Dep::versao_requirement`] at the dep-graph
5942        // axis, and the per-`:contratos`
5943        // [`crate::aplicacao::WitContract::source`] /
5944        // [`crate::aplicacao::WitContract::destination`] /
5945        // [`crate::aplicacao::WitContract::world_ref`] trio the
5946        // sibling pin at 279823b already anchors).
5947        const fn nome_via_const_fn(c: &Caixa) -> &str {
5948            c.nome()
5949        }
5950        const fn versao_via_const_fn(c: &Caixa) -> &str {
5951            c.versao()
5952        }
5953        let src = Caixa::template("demo");
5954        let c = Caixa::from_lisp(&src).expect("template must parse");
5955        assert_eq!(nome_via_const_fn(&c), c.nome());
5956        assert_eq!(versao_via_const_fn(&c), c.versao());
5957        assert_eq!(c.nome(), "demo");
5958        assert_eq!(c.versao(), "0.1.0");
5959    }
5960
5961    #[test]
5962    fn caixa_option_string_scalar_accessor_family_is_const_fn() {
5963        // Fail-before-pass-after pin on the five per-`Caixa`
5964        // `Option<String> → Option<&str>` scalar accessors
5965        // ([`Caixa::licenca`] / [`Caixa::repositorio`] /
5966        // [`Caixa::descricao`] / [`Caixa::edicao`] on the top-level
5967        // manifest's optional universal-axis surface, plus
5968        // [`Caixa::restart_window`] on the M2 supervisor-tree
5969        // per-`SupervisorSpec` peer raw-window-string projection axis).
5970        // Each accessor destructures the typed slot's `Option<String>`
5971        // storage through the `match &self.<field> { Some(s) =>
5972        // Some(s.as_str()), None => None }` shape — routing through
5973        // [`String::as_str`] (const-stable since Rust 1.87, well within
5974        // the workspace MSRV) rather than the non-const
5975        // [`Option::as_deref`] the pre-lift bodies carried — and any
5976        // future accidental downgrade to non-`const` fails the
5977        // corresponding `<name>_via_const_fn` wrapper at caixa-core
5978        // build time with E0015 (`cannot call non-const method`),
5979        // strictly stronger than a runtime `assert!` and strictly
5980        // stronger than a module-scope `const _: () = assert!(…)` pin
5981        // (which cannot be formed on a `&Caixa` fixture because the
5982        // type's `String` / `Option<String>` carriers rule out
5983        // `const`-context value construction; the `const fn` wrapper
5984        // is the load-bearing shape that side-steps the destructor-in-
5985        // const restriction on the value axis while still pinning the
5986        // `const`-fn posture on the callee — mirror of the sibling
5987        // [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
5988        // pin's discipline verbatim on the peer non-`Option`
5989        // `String → &str` axis at the same struct).
5990        //
5991        // Peer of the sibling per-M2/M3-slot `Option<String> →
5992        // Option<&str>` accessor family pin
5993        // [`m3_option_string_scalar_accessor_family_is_const_fn`] on
5994        // the M3 mesh-slot atom axes ([`WitContract::endpoint`] /
5995        // [`WitContract::subject`] / [`WitContract::slot`] on the
5996        // per-`:contratos` payload-carrier trio,
5997        // [`Placement::shard_key`] / [`Placement::affinity`] on the
5998        // per-`:placement` optional-scalar pair).
5999        const fn licenca_via_const_fn(c: &Caixa) -> Option<&str> {
6000            c.licenca()
6001        }
6002        const fn repositorio_via_const_fn(c: &Caixa) -> Option<&str> {
6003            c.repositorio()
6004        }
6005        const fn descricao_via_const_fn(c: &Caixa) -> Option<&str> {
6006            c.descricao()
6007        }
6008        const fn edicao_via_const_fn(c: &Caixa) -> Option<&str> {
6009            c.edicao()
6010        }
6011        const fn restart_window_via_const_fn(c: &Caixa) -> Option<&str> {
6012            c.restart_window()
6013        }
6014        // Sweep both the `Some`-carrying arm (author-declared slot,
6015        // the byte-string projection payload) and the `None`-carrying
6016        // arm (author-omitted slot, the default-path projection) on
6017        // every accessor so the `const fn` wrapper family pins each
6018        // axis's canonical two-arm partition through the same const
6019        // dispatch as the runtime path.
6020        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6021        c1.licenca = Some("MIT".to_string());
6022        c1.repositorio = Some("https://github.com/pleme-io/demo".to_string());
6023        c1.descricao = Some("demo caixa".to_string());
6024        c1.edicao = Some("2024".to_string());
6025        c1.restart_window = Some("60s".to_string());
6026        assert_eq!(licenca_via_const_fn(&c1), c1.licenca());
6027        assert_eq!(repositorio_via_const_fn(&c1), c1.repositorio());
6028        assert_eq!(descricao_via_const_fn(&c1), c1.descricao());
6029        assert_eq!(edicao_via_const_fn(&c1), c1.edicao());
6030        assert_eq!(restart_window_via_const_fn(&c1), c1.restart_window());
6031        assert_eq!(c1.licenca(), Some("MIT"));
6032        assert_eq!(c1.repositorio(), Some("https://github.com/pleme-io/demo"));
6033        assert_eq!(c1.descricao(), Some("demo caixa"));
6034        assert_eq!(c1.edicao(), Some("2024"));
6035        assert_eq!(c1.restart_window(), Some("60s"));
6036        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6037        c2.licenca = None;
6038        c2.repositorio = None;
6039        c2.descricao = None;
6040        c2.edicao = None;
6041        c2.restart_window = None;
6042        assert_eq!(licenca_via_const_fn(&c2), None);
6043        assert_eq!(repositorio_via_const_fn(&c2), None);
6044        assert_eq!(descricao_via_const_fn(&c2), None);
6045        assert_eq!(edicao_via_const_fn(&c2), None);
6046        assert_eq!(restart_window_via_const_fn(&c2), None);
6047    }
6048
6049    #[test]
6050    fn register_populates_registry() {
6051        Caixa::register().expect("first register call in this test process must succeed");
6052        let kws = tatara_lisp::domain::registered_keywords();
6053        assert!(kws.contains(&"defcaixa"));
6054    }
6055
6056    #[test]
6057    fn to_lisp_round_trips() {
6058        let src = Caixa::template("demo");
6059        let c1 = Caixa::from_lisp(&src).unwrap();
6060        let emitted = c1.to_lisp();
6061        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
6062        assert_eq!(c1, c2);
6063    }
6064
6065    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
6066    //
6067    // The compounding pin: the variant stores only the typed
6068    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
6069    // (canonical keyword, description, consumer) routes through the enum's
6070    // own accessors at Display time. Prior to that closure the variant
6071    // carried each accessor's return value as a stored `&'static str`
6072    // snapshot alongside `dialeto`; a caller could construct the variant
6073    // with a snapshot that drifted from what `dialeto`'s accessors would
6074    // return, and every downstream user-facing projection would silently
6075    // disagree with the classification. Storing only the axis makes the
6076    // drift structurally impossible.
6077
6078    #[test]
6079    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
6080        // Single-field construction is the whole compounding shape — a
6081        // future re-introduction of a snapshot field (a `palavra_canonica:
6082        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
6083        // would re-open the drift surface and this construction would fail
6084        // to compile with "missing field" until every snapshot was seeded
6085        // at the call site again. The compile-time guarantee is the
6086        // invariant; the assertion below only witnesses that the
6087        // construction is well-formed after the closure.
6088        let err = LeituraError::DialetoEstrangeiro {
6089            dialeto: crate::dialeto::CaixaDialeto::Molde,
6090        };
6091        assert!(matches!(
6092            err,
6093            LeituraError::DialetoEstrangeiro {
6094                dialeto: crate::dialeto::CaixaDialeto::Molde,
6095            }
6096        ));
6097    }
6098
6099    #[test]
6100    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
6101        // For every foreign-dialect classification the variant surfaces —
6102        // [`crate::dialeto::CaixaDialeto::Molde`] and
6103        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
6104        // variants [`Caixa::from_lisp`] raises this error for — the
6105        // rendered [`std::fmt::Display`] byte-string must interpolate each
6106        // typed accessor's return verbatim. A future re-introduction of a
6107        // stored `&'static str` snapshot alongside `dialeto` that Display
6108        // read instead of the accessor would fail this pin as soon as the
6109        // two disagreed; a future accessor rebrand (a per-dialect
6110        // consumer rename, a canonical-keyword shift once the substrate
6111        // migration named in [`crate::dialeto`] completes) reaches every
6112        // consumer through one typed dispatch and this pin verifies the
6113        // display path is one of them.
6114        for d in [
6115            crate::dialeto::CaixaDialeto::Molde,
6116            crate::dialeto::CaixaDialeto::MoldePosicional,
6117        ] {
6118            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
6119            assert!(
6120                rendered.contains(d.palavra_canonica()),
6121                "Display must interpolate `dialeto.palavra_canonica()` \
6122                 verbatim — a stored snapshot would silently drift from \
6123                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
6124            );
6125            assert!(
6126                rendered.contains(d.descricao()),
6127                "Display must interpolate `dialeto.descricao()` verbatim. \
6128                 dialect: {d}, rendered: {rendered:?}"
6129            );
6130            assert!(
6131                rendered.contains(d.consumidor()),
6132                "Display must interpolate `dialeto.consumidor()` verbatim. \
6133                 dialect: {d}, rendered: {rendered:?}"
6134            );
6135        }
6136    }
6137
6138    #[test]
6139    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
6140        // The end-to-end pin the compounding closure defends: a
6141        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
6142        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
6143        // rendered Display byte-string names the Molde accessors'
6144        // returns verbatim. Any future path that constructed the variant
6145        // with a mismatched snapshot (a stored `palavra_canonica:
6146        // "defcaixa"` on a `Molde` classification) would land Display
6147        // pointing at `defcaixa` while the typed axis said `Molde` — the
6148        // exact drift the closure removes.
6149        let src = r#"
6150          (defcaixa
6151            :name "x"
6152            :kind :Biblioteca
6153            :ecosystem :rust-single-crate
6154            :package {:name "x" :version "0.1.0"})
6155        "#;
6156        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
6157        match err {
6158            LeituraError::DialetoEstrangeiro { dialeto } => {
6159                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
6160                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
6161                assert!(rendered.contains(dialeto.palavra_canonica()));
6162                assert!(rendered.contains(dialeto.consumidor()));
6163                assert!(rendered.contains(dialeto.descricao()));
6164            }
6165            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
6166        }
6167    }
6168
6169    #[test]
6170    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
6171        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
6172        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
6173        // positional-arity `defmolde` form written under a `(defcaixa …)`
6174        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
6175        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
6176        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
6177        // so no test exercised the positional-arity path through
6178        // `Caixa::from_lisp` specifically; the sibling
6179        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
6180        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
6181        // two arms route through the lifted
6182        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
6183        // typed predicate — the same predicate the pre-lift `foreign =>`
6184        // wildcard resolved to today — and this pin makes the
6185        // positional-arity arm's byte-shape at the gate explicit rather
6186        // than implied by wildcard-absorption. A future regression that
6187        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
6188        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
6189        // from the two-arity closure) would fail this pin at caixa-core
6190        // test time rather than surfacing far from the change as a
6191        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
6192        // …)` silently parsing past the derive.
6193        let src = r#"
6194          (defcaixa todoku-go
6195            :kind :Biblioteca
6196            :ecosystem :go
6197            :package {:name "todoku-go" :version "0.3.0"})
6198        "#;
6199        let err =
6200            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
6201        match err {
6202            LeituraError::DialetoEstrangeiro { dialeto } => {
6203                assert_eq!(
6204                    dialeto,
6205                    crate::dialeto::CaixaDialeto::MoldePosicional,
6206                    "DialetoEstrangeiro must carry the MoldePosicional \
6207                     variant verbatim — the positional-arity `defmolde` \
6208                     form under a `(defcaixa …)` head is the \
6209                     `MoldePosicional` arm's canonical byte-shape"
6210                );
6211                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
6212                assert!(
6213                    rendered.contains(dialeto.palavra_canonica()),
6214                    "Display must interpolate `dialeto.palavra_canonica()` \
6215                     verbatim on the MoldePosicional arm; rendered: \
6216                     {rendered:?}"
6217                );
6218                assert!(
6219                    rendered.contains(dialeto.consumidor()),
6220                    "Display must interpolate `dialeto.consumidor()` \
6221                     verbatim on the MoldePosicional arm; rendered: \
6222                     {rendered:?}"
6223                );
6224                assert!(
6225                    rendered.contains(dialeto.descricao()),
6226                    "Display must interpolate `dialeto.descricao()` \
6227                     verbatim on the MoldePosicional arm; rendered: \
6228                     {rendered:?}"
6229                );
6230            }
6231            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
6232        }
6233    }
6234
6235    #[test]
6236    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
6237        // Load-bearing byte-parity pin: for every arm in
6238        // [`crate::dialeto::CaixaDialeto::ALL`], the
6239        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
6240        // partition must agree with the lifted
6241        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
6242        // typed predicate — i.e. from_lisp raises
6243        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
6244        // `d.is_molde_family()` returns `true`, and does NOT raise
6245        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
6246        // predicate returns `false` (the arm's source falls through to
6247        // the derive — parses cleanly on
6248        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
6249        // [`LeituraError::Leitura`] on
6250        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
6251        //
6252        // Pre-lift the gate hand-rolled a three-arm match
6253        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
6254        // whose `foreign =>` wildcard expressed no compile-time link
6255        // back to the substrate primitive's arm-family; a future fifth
6256        // dialect the [`crate::dialeto`] module doc's "third dialect"
6257        // hazard actualises would fall silently onto the wildcard
6258        // regardless of whether it belonged to the `defmolde` family or
6259        // to a distinct `defcaixa`-family. Post-lift the partition
6260        // resolves through
6261        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
6262        // typed dispatch, and this pin refuses any future regression
6263        // that silently split the from_lisp partition from the typed
6264        // predicate — the two paths now migrate as one on any future
6265        // arm addition.
6266        //
6267        // Sibling in shape to the peer
6268        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
6269        // (e9d2315) that pins the same byte-parity between
6270        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
6271        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
6272        // `== "defmolde"` classifier — extends the discipline from the
6273        // two paths within the [`crate::dialeto`] primitive onto the
6274        // third external consumer of the `defmolde`-family partition
6275        // (the [`Caixa::from_lisp`] gate that raises
6276        // [`LeituraError::DialetoEstrangeiro`]).
6277        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
6278            (
6279                crate::dialeto::CaixaDialeto::Pacote,
6280                r#"
6281                  (defcaixa
6282                    :nome   "checkout"
6283                    :versao "0.1.0"
6284                    :kind   Biblioteca
6285                    :edicao "2026"
6286                    :descricao "canonical Pacote source"
6287                    :autores ()
6288                    :etiquetas ()
6289                    :deps ()
6290                    :deps-dev ()
6291                    :bibliotecas ("lib/checkout.lisp"))
6292                "#,
6293            ),
6294            (
6295                crate::dialeto::CaixaDialeto::Molde,
6296                r#"
6297                  (defcaixa
6298                    :name "base64"
6299                    :kind :Biblioteca
6300                    :ecosystem :rust-single-crate
6301                    :package {:name "base64" :version "0.22.1"}
6302                    :workflows [:auto-release])
6303                "#,
6304            ),
6305            (
6306                crate::dialeto::CaixaDialeto::MoldePosicional,
6307                r#"
6308                  (defcaixa todoku-go
6309                    :kind :Biblioteca
6310                    :ecosystem :go
6311                    :package {:name "todoku-go" :version "0.3.0"})
6312                "#,
6313            ),
6314            (
6315                crate::dialeto::CaixaDialeto::Desconhecido,
6316                r#"(defcaixa :licenca "MIT")"#,
6317            ),
6318        ];
6319
6320        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
6321        // must appear in the fixture table so the pin's arm-set stays
6322        // synchronised with the enum's arm-set. Fails at test time if a
6323        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
6324        // (with a corresponding `is_molde_family` return) forgot to
6325        // extend this fixture table with a canonical source for the new
6326        // arm — the pin cannot cover an arm it has no source for.
6327        for &expected in crate::dialeto::CaixaDialeto::ALL {
6328            assert!(
6329                fixtures.iter().any(|(d, _)| *d == expected),
6330                "fixture table must carry a canonical source for every \
6331                 CaixaDialeto arm; missing: {expected:?}"
6332            );
6333        }
6334
6335        for &(expected_dialect, src) in fixtures {
6336            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
6337                panic!(
6338                    "fixture source for {expected_dialect:?} must classify \
6339                     cleanly, got err: {err:?}"
6340                )
6341            });
6342            assert_eq!(
6343                classified, expected_dialect,
6344                "fixture source for {expected_dialect:?} must classify as \
6345                 {expected_dialect:?} (drift here defeats the byte-parity \
6346                 pin below — a source labelled for one arm but classifying \
6347                 as another would silently satisfy or violate the pin for \
6348                 the wrong reason)"
6349            );
6350
6351            let outcome = Caixa::from_lisp(src);
6352            match (expected_dialect.is_molde_family(), &outcome) {
6353                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
6354                    assert_eq!(
6355                        *dialeto, expected_dialect,
6356                        "DialetoEstrangeiro must carry the same typed arm \
6357                         the classifier returned — a drift here would let \
6358                         from_lisp raise the error while pointing at the \
6359                         wrong dialect (e.g. rejecting a \
6360                         MoldePosicional source as Molde). arm: \
6361                         {expected_dialect:?}"
6362                    );
6363                }
6364                (true, other) => panic!(
6365                    "arm {expected_dialect:?} has is_molde_family() = true \
6366                     so from_lisp must raise DialetoEstrangeiro carrying \
6367                     {expected_dialect:?}; got: {other:?}"
6368                ),
6369                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
6370                    "arm {expected_dialect:?} has is_molde_family() = false \
6371                     so from_lisp must NOT raise DialetoEstrangeiro; got \
6372                     one carrying: {dialeto:?}. This means the typed \
6373                     predicate and the from_lisp partition disagree on \
6374                     this arm — exactly the drift this pin refuses."
6375                ),
6376                (false, _) => {
6377                    // A non-molde arm's source falls through to the
6378                    // derive: Pacote sources parse to Ok(_); Desconhecido
6379                    // sources surface as LeituraError::Leitura from the
6380                    // derive's own unknown-keyword rejection. Either
6381                    // shape is acceptable here — the pin's promise is
6382                    // narrower: "no DialetoEstrangeiro on
6383                    // is_molde_family() == false".
6384                }
6385            }
6386        }
6387    }
6388
6389    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
6390
6391    #[test]
6392    fn limits_round_trip_via_json() {
6393        use crate::LimitsSpec;
6394        use std::time::Duration;
6395        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6396        c.limits = Some(LimitsSpec {
6397            memory: Some(64 * 1024 * 1024),
6398            fuel: Some(1_000_000),
6399            wall_clock: Some(Duration::from_secs(30)),
6400            cpu: Some(500),
6401        });
6402        let json = serde_json::to_string(&c).unwrap();
6403        assert!(json.contains("\"limits\""));
6404        assert!(json.contains("\"64MiB\""));
6405        assert!(json.contains("\"30s\""));
6406        assert!(json.contains("\"500m\""));
6407        let back: Caixa = serde_json::from_str(&json).unwrap();
6408        assert_eq!(c.limits, back.limits);
6409    }
6410
6411    #[test]
6412    fn behavior_round_trip_via_json() {
6413        use crate::BehaviorSpec;
6414        use std::path::PathBuf;
6415        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6416        c.behavior = Some(BehaviorSpec {
6417            on_init: Some(PathBuf::from("lib/init.lisp")),
6418            on_call: Some(PathBuf::from("lib/handlers.lisp")),
6419            ..Default::default()
6420        });
6421        let json = serde_json::to_string(&c).unwrap();
6422        let back: Caixa = serde_json::from_str(&json).unwrap();
6423        assert_eq!(c.behavior, back.behavior);
6424    }
6425
6426    #[test]
6427    fn upgrade_from_round_trip_via_json() {
6428        use crate::{UpgradeFromEntry, UpgradeInstruction};
6429        use std::path::PathBuf;
6430        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6431        c.upgrade_from = vec![UpgradeFromEntry {
6432            from: "0.1.0".into(),
6433            instructions: vec![
6434                UpgradeInstruction::LoadModule {
6435                    module: "demo".into(),
6436                },
6437                UpgradeInstruction::StateChange {
6438                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
6439                },
6440                UpgradeInstruction::SoftPurge {
6441                    module: "demo-old".into(),
6442                },
6443            ],
6444        }];
6445        let json = serde_json::to_string(&c).unwrap();
6446        let back: Caixa = serde_json::from_str(&json).unwrap();
6447        assert_eq!(c.upgrade_from, back.upgrade_from);
6448    }
6449
6450    #[test]
6451    fn supervisor_view_returns_typed_shape() {
6452        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
6453        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
6454        c.kind = CaixaKind::Supervisor;
6455        c.bibliotecas.clear();
6456        c.estrategia = Some(RestartStrategy::OneForOne);
6457        c.max_restarts = Some(5);
6458        c.restart_window = Some("60s".into());
6459        c.children = vec![ChildSpec {
6460            caixa: "worker".into(),
6461            versao: "^0.1".into(),
6462            restart: RestartPolicy::Permanent,
6463        }];
6464        let view = c.supervisor_view().expect("Supervisor kind has a view");
6465        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
6466        assert_eq!(view.max_restarts, 5);
6467        assert_eq!(
6468            view.restart_window,
6469            Some(std::time::Duration::from_secs(60))
6470        );
6471        assert_eq!(view.children.len(), 1);
6472        view.validate().unwrap();
6473    }
6474
6475    #[test]
6476    fn supervisor_view_none_for_non_supervisor_kinds() {
6477        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6478        assert!(c.supervisor_view().is_none());
6479    }
6480
6481    #[test]
6482    fn declared_mesh_slots_empty_for_bare_caixa() {
6483        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6484        assert!(c.declared_mesh_slots().is_empty());
6485    }
6486
6487    #[test]
6488    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
6489        use crate::{Entrada, Membro};
6490        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6491        // Set a non-adjacent pair (:membros + :entrada) to pin that the
6492        // canonical declaration order is preserved regardless of which
6493        // subset is populated.
6494        c.membros = vec![Membro {
6495            caixa: "a".into(),
6496            versao: "^0.1".into(),
6497        }];
6498        c.entrada = Some(Entrada {
6499            host: "x.example.com".into(),
6500            para: "a".into(),
6501            paths: vec![],
6502            port: 8080,
6503        });
6504        assert_eq!(
6505            c.declared_mesh_slots(),
6506            vec![
6507                crate::render::M3_AUTHOR_KEY_MEMBROS,
6508                crate::render::M3_AUTHOR_KEY_ENTRADA,
6509            ]
6510        );
6511    }
6512
6513    #[test]
6514    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6515        // Scalar-value pin: the five author-facing kebab-case labels the
6516        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
6517        // mesh slot axis, one arm per typed slot. Mirrors the peer
6518        // scalar-value pin the sibling
6519        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
6520        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
6521        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
6522        // carry (f49c8b0), so both altitudes of the typed-slot algebra
6523        // (per-Servico M2 + per-Aplicacao M3) share the same
6524        // "one canonical byte-string per arm" discipline. A future
6525        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
6526        // `:politicas` → `:policies`, `:placement` → `:distribution`,
6527        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
6528        // and every consumer that reaches for the label picks it up at
6529        // build time rather than at runtime as a downstream mismatch.
6530        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
6531        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
6532        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
6533        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
6534        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
6535    }
6536
6537    #[test]
6538    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
6539        // Production-through-const pin: the five per-arm labels the
6540        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
6541        // `Vec` route through the lifted
6542        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
6543        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
6544        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
6545        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
6546        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
6547        // declaration order. A future re-order or drift at the tagger
6548        // (a rename that reaches the tagger but not the const, or vice
6549        // versa) surfaces here at build time rather than at runtime as
6550        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
6551        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6552        // commit. Mirror of the peer
6553        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6554        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
6555        // axis.
6556        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
6557        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6558        c.membros = vec![Membro {
6559            caixa: "a".into(),
6560            versao: "^0.1".into(),
6561        }];
6562        c.contratos = vec![WitContract {
6563            de: "a".into(),
6564            para: "a".into(),
6565            wit: "wasi:http/proxy".into(),
6566            endpoint: Some("/x".into()),
6567            subject: None,
6568            slot: None,
6569        }];
6570        c.politicas = Some(MeshPolicy::default());
6571        c.placement = Some(Placement {
6572            estrategia: PlacementStrategy::Replicated,
6573            clusters: vec!["rio".into()],
6574            affinity: None,
6575            shard_key: None,
6576        });
6577        c.entrada = Some(Entrada {
6578            host: "x.example.com".into(),
6579            para: "a".into(),
6580            paths: vec![],
6581            port: 8080,
6582        });
6583        assert_eq!(
6584            c.declared_mesh_slots(),
6585            vec![
6586                crate::render::M3_AUTHOR_KEY_MEMBROS,
6587                crate::render::M3_AUTHOR_KEY_CONTRATOS,
6588                crate::render::M3_AUTHOR_KEY_POLITICAS,
6589                crate::render::M3_AUTHOR_KEY_PLACEMENT,
6590                crate::render::M3_AUTHOR_KEY_ENTRADA,
6591            ]
6592        );
6593    }
6594
6595    #[test]
6596    fn declared_supervisor_slots_empty_for_bare_caixa() {
6597        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6598        assert!(c.declared_supervisor_slots().is_empty());
6599    }
6600
6601    #[test]
6602    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
6603        use crate::RestartStrategy;
6604        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6605        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
6606        // that the canonical declaration order is preserved regardless
6607        // of which subset is populated.
6608        c.estrategia = Some(RestartStrategy::OneForOne);
6609        c.restart_window = Some("60s".into());
6610        assert_eq!(
6611            c.declared_supervisor_slots(),
6612            vec![
6613                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6614                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6615            ]
6616        );
6617    }
6618
6619    #[test]
6620    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6621        // Scalar-value pin: the four author-facing kebab-case labels the
6622        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
6623        // supervision-tree slot axis, one arm per typed slot. Mirrors the
6624        // peer scalar-value pins the sibling
6625        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
6626        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
6627        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
6628        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
6629        // top-level M3 slot consts carry, so all three kind-scoped
6630        // typed-slot-family author-facing-label axes route through one
6631        // canonical per-arm declaration. A future rebrand
6632        // (`:estrategia` → `:strategy` for English uniformity,
6633        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
6634        // `MaxIntensity` name, `:restart-window` → `:period` matching
6635        // OTP's `Period` name, `:children` → `:workers` matching Elixir
6636        // idiom) lands as an edit to exactly one const, and every
6637        // consumer that reaches for the label picks it up at build time
6638        // rather than at runtime as a downstream mismatch.
6639        assert_eq!(
6640            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6641            ":estrategia"
6642        );
6643        assert_eq!(
6644            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6645            ":max-restarts"
6646        );
6647        assert_eq!(
6648            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6649            ":restart-window"
6650        );
6651        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
6652    }
6653
6654    #[test]
6655    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
6656        // Production-through-const pin: the four per-arm labels the
6657        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
6658        // return `Vec` route through the lifted
6659        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
6660        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
6661        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
6662        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
6663        // canonical declaration order. A future re-order or drift at the
6664        // tagger (a rename that reaches the tagger but not the const, or
6665        // vice versa) surfaces here at build time rather than at runtime
6666        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
6667        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6668        // commit. Mirror of the peer
6669        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6670        // (f49c8b0) and
6671        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
6672        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
6673        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
6674        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6675        c.estrategia = Some(RestartStrategy::OneForOne);
6676        c.max_restarts = Some(5);
6677        c.restart_window = Some("60s".into());
6678        c.children = vec![ChildSpec {
6679            caixa: "worker".into(),
6680            versao: "^0.1".into(),
6681            restart: RestartPolicy::Permanent,
6682        }];
6683        assert_eq!(
6684            c.declared_supervisor_slots(),
6685            vec![
6686                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6687                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6688                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6689                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
6690            ]
6691        );
6692    }
6693
6694    #[test]
6695    fn declared_servico_slots_empty_for_bare_caixa() {
6696        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6697        assert!(c.declared_servico_slots().is_empty());
6698    }
6699
6700    #[test]
6701    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
6702        use crate::{UpgradeFromEntry, UpgradeInstruction};
6703        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6704        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
6705        // the canonical declaration order is preserved regardless of
6706        // which subset is populated.
6707        c.limits = Some(crate::LimitsSpec {
6708            fuel: Some(1_000_000),
6709            ..Default::default()
6710        });
6711        c.upgrade_from = vec![UpgradeFromEntry {
6712            from: "0.1.0".into(),
6713            instructions: vec![UpgradeInstruction::Restart],
6714        }];
6715        assert_eq!(
6716            c.declared_servico_slots(),
6717            vec![
6718                crate::render::M2_AUTHOR_KEY_LIMITS,
6719                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6720            ]
6721        );
6722    }
6723
6724    #[test]
6725    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6726        // Scalar-value pin: the three author-facing kebab-case labels
6727        // the `(defcaixa … :<slot> (…))` surface admits on the M2
6728        // top-level slot axis, one arm per typed slot. Mirrors the peer
6729        // scalar-value pin the sibling renderer-side
6730        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
6731        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
6732        // consts carry, so both halves of the M2 top-level slot dual
6733        // axis (author-facing kebab-case label + renderer-side
6734        // camelCase overlay-container wire key) route through one
6735        // canonical per-arm declaration. A future rebrand
6736        // (`:limits` → `:sandbox` matching Lunatic per-process
6737        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
6738        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
6739        // matching Erlang's verbatim appup name) lands as an edit to
6740        // exactly one const, and every consumer that reaches for the
6741        // label picks it up at build time rather than at runtime as a
6742        // downstream mismatch.
6743        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
6744        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
6745        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
6746    }
6747
6748    #[test]
6749    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
6750        // Production-through-const pin: the three per-arm labels the
6751        // [`Caixa::declared_servico_slots`] tagger pushes onto its
6752        // return `Vec` route through the lifted
6753        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
6754        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
6755        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
6756        // declaration order. A future re-order or drift at the tagger
6757        // (a rename that reaches the tagger but not the const, or vice
6758        // versa) surfaces here at build time rather than at runtime as
6759        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
6760        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6761        // commit. Mirror of the peer
6762        // [`crate::behavior::BehaviorSpec::declared_slots`] production
6763        // tagger pin (889dc18) on the sibling per-callback axis.
6764        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
6765        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6766        c.limits = Some(crate::LimitsSpec {
6767            fuel: Some(1_000_000),
6768            ..Default::default()
6769        });
6770        c.behavior = Some(BehaviorSpec {
6771            on_init: Some(PathBuf::from("lib/init.lisp")),
6772            ..Default::default()
6773        });
6774        c.upgrade_from = vec![UpgradeFromEntry {
6775            from: "0.1.0".into(),
6776            instructions: vec![UpgradeInstruction::Restart],
6777        }];
6778        assert_eq!(
6779            c.declared_servico_slots(),
6780            vec![
6781                crate::render::M2_AUTHOR_KEY_LIMITS,
6782                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
6783                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6784            ]
6785        );
6786    }
6787
6788    #[test]
6789    fn existing_manifests_unaffected_by_new_optional_slots() {
6790        // Regression test: a caixa.lisp authored before M2 typed slots
6791        // should still parse + serialize cleanly. The bare `defcaixa`
6792        // emitted by `Caixa::template` has none of the new fields.
6793        let src = Caixa::template("legacy");
6794        let c = Caixa::from_lisp(&src).unwrap();
6795        assert!(c.limits.is_none());
6796        assert!(c.behavior.is_none());
6797        assert!(c.upgrade_from.is_empty());
6798        assert!(c.estrategia.is_none());
6799        assert!(c.children.is_empty());
6800
6801        // And to_lisp emits a manifest with the new slots in the
6802        // empty/default state — round-trippable.
6803        let emitted = c.to_lisp();
6804        let back = Caixa::from_lisp(&emitted).unwrap();
6805        assert_eq!(c, back);
6806    }
6807
6808    #[test]
6809    fn validate_deps_accepts_canonical_caixa() {
6810        // Positive control: the bare template — zero deps, zero
6811        // deps_dev — passes the gate trivially. A future axis added to
6812        // `Dep::validate` mustn't regress an empty-deps caixa to a
6813        // build error.
6814        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6815        c.validate_deps().unwrap();
6816    }
6817
6818    #[test]
6819    fn validate_deps_rejects_invalid_versao_in_deps() {
6820        // Fail-before-pass-after pin: a malformed `:deps :versao`
6821        // surfaces at validate_deps() time, not at lacre-resolve time.
6822        // Mirrors `rejects_invalid_membro_versao_requirement` and
6823        // `validate_rejects_invalid_child_versao_requirement` on the
6824        // other two `:versao` axes.
6825        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6826        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
6827        let err = c.validate_deps().unwrap_err();
6828        assert!(
6829            matches!(
6830                err,
6831                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6832                    if nome == "caixa-teia" && versao == "^bad-version"
6833            ),
6834            "got {err:?}"
6835        );
6836    }
6837
6838    #[test]
6839    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
6840        // Parity pin: `:deps-dev` must run through the same per-entry
6841        // validator as `:deps` — a typo in either axis surfaces the
6842        // same diagnostic. Without this leg, `:deps-dev` would be a
6843        // second-class citizen of the typed surface and an author
6844        // could land a build that passes validate_deps but fails at
6845        // `feira lock`-time when the dev-dep is resolved for a test
6846        // build.
6847        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6848        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
6849        let err = c.validate_deps().unwrap_err();
6850        assert!(
6851            matches!(
6852                err,
6853                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6854                    if nome == "tatara-check" && versao == "^^0.1"
6855            ),
6856            "got {err:?}"
6857        );
6858    }
6859
6860    #[test]
6861    fn validate_deps_runs_deps_before_deps_dev() {
6862        // Order pin: when both lists carry typos, the `:deps`
6863        // diagnostic surfaces first. The author's mental model is
6864        // "runtime deps are load-bearing; dev deps are scaffolding";
6865        // surfacing the runtime axis first matches that hierarchy.
6866        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6867        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
6868        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
6869        let err = c.validate_deps().unwrap_err();
6870        assert!(
6871            matches!(
6872                err,
6873                crate::dep::DepError::VersaoInvalid { ref nome, .. }
6874                    if nome == "runtime-dep"
6875            ),
6876            "expected `:deps` typo to surface first, got {err:?}"
6877        );
6878    }
6879
6880    #[test]
6881    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
6882        // Positive control sweep across both lists. Pin every
6883        // canonical Cargo-shaped form so a future tightening of the
6884        // accepted set surfaces here as a test failure (parity with
6885        // `accepts_canonical_membro_versao_forms` and
6886        // `validate_accepts_canonical_child_versao_forms`).
6887        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6888        c.deps = vec![
6889            Dep::simple("caret", "^0.1"),
6890            Dep::simple("tilde", "~0.1.2"),
6891            Dep::simple("exact", "0.1.0"),
6892            Dep::simple("wildcard", "*"),
6893            Dep::simple("multi-range", ">=0.1, <2"),
6894        ];
6895        c.deps_dev = vec![
6896            Dep::simple("dev-caret", "^0.1"),
6897            Dep::simple("dev-wildcard", "*"),
6898        ];
6899        c.validate_deps().unwrap();
6900    }
6901
6902    #[test]
6903    fn validate_deps_diagnostic_carries_offending_dep() {
6904        // Diagnostic-shape pin: the error names the offending entry's
6905        // `:nome` + `:versao` verbatim and carries a non-empty
6906        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
6907        // run can render the diagnostic without re-parsing.
6908        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6909        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
6910        let err = c.validate_deps().unwrap_err();
6911        let crate::dep::DepError::VersaoInvalid {
6912            nome,
6913            versao,
6914            reason,
6915        } = err
6916        else {
6917            panic!("expected VersaoInvalid, got other variant");
6918        };
6919        assert_eq!(nome, "caixa-teia");
6920        assert_eq!(versao, "not-a-req");
6921        assert!(
6922            !reason.is_empty(),
6923            "VersaoInvalid `reason` must carry the parser's wording verbatim"
6924        );
6925    }
6926
6927    #[test]
6928    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
6929        // Cross-axis pin: `validate_deps` walks both :deps and
6930        // :deps-dev through `Dep::validate`, and the new fonte gate
6931        // (`:tag` + `:branch` both set — the canonical "pin drift"
6932        // footgun) must surface from the :deps-dev arm with the
6933        // offending entry's :nome named. Pin the :deps-dev arm
6934        // explicitly so a future shortcut that only walks :deps
6935        // surfaces here as a regression.
6936        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6937        c.deps_dev = vec![Dep {
6938            nome: "dev-only".into(),
6939            versao: "^0.1".into(),
6940            fonte: Some(crate::DepSource::Git {
6941                repo: "github:p/x".into(),
6942                tag: Some("v1".into()),
6943                rev: None,
6944                branch: Some("main".into()),
6945            }),
6946            opcional: false,
6947            caracteristicas: vec![],
6948        }];
6949        let err = c.validate_deps().unwrap_err();
6950        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
6951            panic!("expected FontePinAmbiguous from :deps-dev walk");
6952        };
6953        assert_eq!(nome, "dev-only");
6954        assert!(pins.contains(":tag") && pins.contains(":branch"));
6955    }
6956
6957    #[test]
6958    fn validate_deps_rejects_empty_repo_in_deps() {
6959        // Parity pin on the :deps arm: an empty :repo on the runtime
6960        // deps list surfaces the same FonteRepoEmpty diagnostic the
6961        // dep.rs per-entry tests pin, naming the offending entry.
6962        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6963        c.deps = vec![Dep {
6964            nome: "runtime".into(),
6965            versao: "^0.1".into(),
6966            fonte: Some(crate::DepSource::Git {
6967                repo: String::new(),
6968                tag: Some("v1".into()),
6969                rev: None,
6970                branch: None,
6971            }),
6972            opcional: false,
6973            caracteristicas: vec![],
6974        }];
6975        let err = c.validate_deps().unwrap_err();
6976        assert!(
6977            matches!(
6978                err,
6979                crate::dep::DepError::FonteRepoEmpty { ref nome }
6980                    if nome == "runtime"
6981            ),
6982            "got {err:?}"
6983        );
6984    }
6985
6986    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
6987
6988    #[test]
6989    fn validate_deps_rejects_duplicate_nome_in_deps() {
6990        // Fail-before-pass-after pin: two `:deps` entries naming the same
6991        // caixa carry two `:versao` / `:fonte` / feature triples that the
6992        // caixa-resolver's lacre pipeline collapses (the second silently
6993        // overwrites the first at `concrete_versao`-resolve time). The
6994        // gate surfaces the duplicate at validate-time, naming the
6995        // offending caixa + the list, before the resolver-side silent
6996        // drop. Mirrors the peer typed-graph duplicate gates
6997        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
6998        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6999        c.deps = vec![
7000            Dep::simple("caixa-teia", "^0.1"),
7001            Dep::simple("caixa-teia", "^0.2"),
7002        ];
7003        let err = c.validate_deps().unwrap_err();
7004        assert!(
7005            matches!(
7006                err,
7007                crate::dep::DepError::DuplicateNome { ref nome, list }
7008                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
7009            ),
7010            "got {err:?}"
7011        );
7012    }
7013
7014    #[test]
7015    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
7016        // Parity pin: `:deps-dev` runs through the same per-list
7017        // duplicate check as `:deps` — neither axis is a second-class
7018        // citizen of the set-not-multiset discipline.
7019        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7020        c.deps_dev = vec![
7021            Dep::simple("tatara-check", "*"),
7022            Dep::simple("tatara-check", "^0.1"),
7023        ];
7024        let err = c.validate_deps().unwrap_err();
7025        assert!(
7026            matches!(
7027                err,
7028                crate::dep::DepError::DuplicateNome { ref nome, list }
7029                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
7030            ),
7031            "got {err:?}"
7032        );
7033    }
7034
7035    #[test]
7036    fn validate_deps_accepts_cross_list_same_nome() {
7037        // The Cargo `[dependencies]` + `[dev-dependencies]` override
7038        // convention is preserved: a name appearing in *both* lists is
7039        // valid (the dev-pin overrides at test/dev time). Only
7040        // within-list duplicates are structurally incoherent — pin the
7041        // permissive cross-list semantics so a future shortcut that
7042        // collapses the two seen-sets into one surfaces here as a test
7043        // failure.
7044        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7045        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
7046        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
7047        c.validate_deps().unwrap();
7048    }
7049
7050    #[test]
7051    fn validate_deps_accepts_distinct_nome_in_both_lists() {
7052        // Positive control: distinct names within each list pass — the
7053        // gate's identity element on the canonical authoring shape.
7054        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7055        c.deps = vec![
7056            Dep::simple("caixa-teia", "^0.1"),
7057            Dep::simple("pleme-mesh", "*"),
7058        ];
7059        c.deps_dev = vec![
7060            Dep::simple("tatara-check", "*"),
7061            Dep::simple("dev-shim", "^0.1"),
7062        ];
7063        c.validate_deps().unwrap();
7064    }
7065
7066    #[test]
7067    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
7068        // Diagnostic-precedence pin: a malformed `:versao` on the
7069        // duplicating entry surfaces its narrower `VersaoInvalid`
7070        // diagnostic first, before the cross-entry duplicate gate fires
7071        // — the canonical "per-entry shape before cross-entry uniqueness"
7072        // precedence every peer set-not-multiset gate establishes
7073        // (`*_invalid_fires_before_duplicate_check` pins on
7074        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
7075        // `validate_upgrade_from`).
7076        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7077        c.deps = vec![
7078            Dep::simple("caixa-teia", "^0.1"),
7079            Dep::simple("caixa-teia", "^bad-version"),
7080        ];
7081        let err = c.validate_deps().unwrap_err();
7082        assert!(
7083            matches!(
7084                err,
7085                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
7086                    if nome == "caixa-teia" && versao == "^bad-version"
7087            ),
7088            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
7089        );
7090    }
7091
7092    #[test]
7093    fn validate_deps_duplicate_diagnostic_names_first_collision() {
7094        // First-collision determinism pin: with three entries naming the
7095        // same caixa, the first colliding pair surfaces — not the last.
7096        // Mirrors the peer first-collision posture on every
7097        // duplicate-target gate
7098        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
7099        // — the second entry is the first collision; this gate uses the
7100        // same shape: the second entry's `:nome` lands in the diagnostic
7101        // because `seen.insert(first.nome)` already populated the set).
7102        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7103        c.deps = vec![
7104            Dep::simple("caixa-teia", "^0.1"),
7105            Dep::simple("caixa-teia", "^0.2"),
7106            Dep::simple("caixa-teia", "^0.3"),
7107        ];
7108        let err = c.validate_deps().unwrap_err();
7109        // The diagnostic carries the offending caixa name; the
7110        // implementation surfaces on the *second* entry (the first
7111        // collision), so the test pins the `:nome` value.
7112        assert!(
7113            matches!(
7114                err,
7115                crate::dep::DepError::DuplicateNome { ref nome, list }
7116                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
7117            ),
7118            "got {err:?}"
7119        );
7120    }
7121
7122    #[test]
7123    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
7124        // Cross-list precedence pin: when both lists carry duplicates,
7125        // the `:deps` diagnostic surfaces first — same author-mental-
7126        // model ordering the `validate_deps_runs_deps_before_deps_dev`
7127        // pin establishes for malformed `:versao` (runtime axis before
7128        // dev axis).
7129        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7130        c.deps = vec![
7131            Dep::simple("runtime-dep", "^0.1"),
7132            Dep::simple("runtime-dep", "^0.2"),
7133        ];
7134        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
7135        let err = c.validate_deps().unwrap_err();
7136        assert!(
7137            matches!(
7138                err,
7139                crate::dep::DepError::DuplicateNome { ref nome, list }
7140                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
7141            ),
7142            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
7143        );
7144    }
7145
7146    #[test]
7147    fn validate_deps_empty_lists_pass_duplicate_gate() {
7148        // Empty-set identity pin: the bare template (zero deps, zero
7149        // deps_dev) passes the duplicate gate as the gate's identity
7150        // element. A future tighten that conflates "empty" with
7151        // "missing" would regress this baseline.
7152        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7153        c.validate_deps().unwrap();
7154    }
7155
7156    #[test]
7157    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
7158        // Diagnostic-shape pin: the `list:` field tags which list the
7159        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
7160        // `feira lint` run can route the author to the right block in
7161        // their caixa.lisp without re-deriving the list from context.
7162        // Same self-locating shape every peer per-axis diagnostic
7163        // already exposes.
7164        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7165        c.deps_dev = vec![
7166            Dep::simple("dev-thing", "*"),
7167            Dep::simple("dev-thing", "^0.1"),
7168        ];
7169        let err = c.validate_deps().unwrap_err();
7170        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
7171            panic!("expected DuplicateNome from :deps-dev walk");
7172        };
7173        assert_eq!(nome, "dev-thing");
7174        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
7175    }
7176
7177    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
7178
7179    #[test]
7180    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
7181        // Thread-through pin on `:deps`: the per-entry
7182        // `Dep::validate_caracteristicas` gate fires inside
7183        // `Caixa::validate_deps`'s linear walk, so a malformed feature
7184        // list on any `:deps` entry surfaces as a `DepError` from
7185        // `validate_deps` — the same reachability shape every per-entry
7186        // `Dep::validate` arm threads through. Without this pin a future
7187        // shortcut that skips the per-entry `Dep::validate` call on the
7188        // cross-entry-uniqueness path would mask the within-entry
7189        // `:caracteristicas` gates.
7190        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7191        c.deps = vec![Dep {
7192            nome: "caixa-teia".into(),
7193            versao: "^0.1".into(),
7194            fonte: None,
7195            opcional: false,
7196            caracteristicas: vec!["http".into(), "http".into()],
7197        }];
7198        let err = c.validate_deps().unwrap_err();
7199        let crate::dep::DepError::CaracteristicaDuplicate {
7200            nome,
7201            caracteristica,
7202        } = err
7203        else {
7204            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
7205        };
7206        assert_eq!(nome, "caixa-teia");
7207        assert_eq!(caracteristica, "http");
7208    }
7209
7210    #[test]
7211    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
7212        // Peer thread-through pin on `:deps-dev`: same reachability as
7213        // the `:deps` arm above, on the dev-only authoring axis. Pins
7214        // that the `validate_deps` walk visits both lists' per-entry
7215        // gates uniformly. The empty-feature arm carries here so both
7216        // new `:caracteristicas` arms are surfaced via at least one
7217        // `validate_deps` thread-through.
7218        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7219        c.deps_dev = vec![Dep {
7220            nome: "caixa-teia".into(),
7221            versao: "^0.1".into(),
7222            fonte: None,
7223            opcional: false,
7224            caracteristicas: vec![String::new()],
7225        }];
7226        let err = c.validate_deps().unwrap_err();
7227        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
7228            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
7229        };
7230        assert_eq!(nome, "caixa-teia");
7231    }
7232
7233    #[test]
7234    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
7235        // Thread-through pin on `:deps`: the per-entry
7236        // `Dep::validate_caracteristicas` value-shape gate (lifted via
7237        // `crate::render::is_cargo_feature_name`) fires inside
7238        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
7239        // a structurally invalid feature name on any `:deps` entry
7240        // surfaces as `DepError::CaracteristicaInvalid` from
7241        // `validate_deps` — the same reachability shape every per-entry
7242        // `Dep::validate` arm threads through. Without this pin a
7243        // future shortcut that skips the per-entry `Dep::validate` call
7244        // on the cross-entry-uniqueness path would mask the within-
7245        // entry `:caracteristicas` value-shape gate.
7246        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7247        c.deps = vec![Dep {
7248            nome: "caixa-teia".into(),
7249            versao: "^0.1".into(),
7250            fonte: None,
7251            opcional: false,
7252            caracteristicas: vec!["+http".into()],
7253        }];
7254        let err = c.validate_deps().unwrap_err();
7255        let crate::dep::DepError::CaracteristicaInvalid {
7256            nome,
7257            caracteristica,
7258            ..
7259        } = err
7260        else {
7261            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
7262        };
7263        assert_eq!(nome, "caixa-teia");
7264        assert_eq!(caracteristica, "+http");
7265    }
7266
7267    #[test]
7268    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
7269        // Peer thread-through pin on `:deps-dev`: same reachability as
7270        // the `:deps` arm above, on the dev-only authoring axis. The
7271        // `http/json` shape carries here so the segment-separator
7272        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
7273        // confusion footgun) is surfaced via the cross-entry walk too —
7274        // pinning that the `:deps-dev` list visits the same per-entry
7275        // value-shape gate as the `:deps` list.
7276        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7277        c.deps_dev = vec![Dep {
7278            nome: "caixa-teia".into(),
7279            versao: "^0.1".into(),
7280            fonte: None,
7281            opcional: false,
7282            caracteristicas: vec!["http/json".into()],
7283        }];
7284        let err = c.validate_deps().unwrap_err();
7285        let crate::dep::DepError::CaracteristicaInvalid {
7286            nome,
7287            caracteristica,
7288            ..
7289        } = err
7290        else {
7291            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
7292        };
7293        assert_eq!(nome, "caixa-teia");
7294        assert_eq!(caracteristica, "http/json");
7295    }
7296
7297    #[test]
7298    fn to_lisp_preserves_deps() {
7299        let src = r#"
7300(defcaixa
7301  :nome "x"
7302  :versao "0.1.0"
7303  :kind Biblioteca
7304  :deps ((:nome "a" :versao "^0.1")
7305         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
7306"#;
7307        let c1 = Caixa::from_lisp(src).unwrap();
7308        let emitted = c1.to_lisp();
7309        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
7310        assert_eq!(c1.deps, c2.deps);
7311    }
7312
7313    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
7314
7315    fn caixa_with_nome(nome: &str) -> Caixa {
7316        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
7317        c.nome = nome.to_string();
7318        c
7319    }
7320
7321    #[test]
7322    fn validate_nome_accepts_canonical_template() {
7323        // Positive control: the bare `feira init`-style template's
7324        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
7325        // not regress this baseline shape. A future tightening of the
7326        // accepted set surfaces here as a test failure first.
7327        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7328        c.validate_nome().unwrap();
7329    }
7330
7331    #[test]
7332    fn validate_nome_accepts_canonical_forms() {
7333        // Positive-set sweep: each realistic caixa-name shape the K8s
7334        // apiserver accepts as a `metadata.name` label must pass —
7335        // single-word, hyphen-joined, version-suffixed, single-char,
7336        // two-char, digit-start (DNS-1123 allows this; the stricter
7337        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
7338        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
7339        // the peer member-name axis.
7340        for nome in [
7341            "checkout",
7342            "cart-v2",
7343            "a",
7344            "db",
7345            "3rd-party-shim",
7346            "payment-retry",
7347            "0",
7348        ] {
7349            caixa_with_nome(nome)
7350                .validate_nome()
7351                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
7352        }
7353    }
7354
7355    #[test]
7356    fn validate_nome_rejects_empty() {
7357        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
7358        // an empty `:nome` (the derive macro stores the raw String);
7359        // the gate's empty arm names the offending axis with a narrower
7360        // diagnostic than the `NomeInvalid` parse arm would emit.
7361        let c = caixa_with_nome("");
7362        let err = c.validate_nome().unwrap_err();
7363        assert_eq!(err, ManifestError::NomeEmpty);
7364    }
7365
7366    #[test]
7367    fn validate_nome_rejects_uppercase() {
7368        // The canonical "I copied the TitleCase display name verbatim"
7369        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
7370        // admission on every derived artifact (Helm chart, ComputeUnit,
7371        // CNP, HTTPRoute, label values); the gate moves the diagnostic
7372        // to the source `caixa.lisp` and the reason suggests the
7373        // lowercased fix verbatim.
7374        let c = caixa_with_nome("MyApp");
7375        let err = c.validate_nome().unwrap_err();
7376        let ManifestError::NomeInvalid { nome, reason } = err else {
7377            panic!("expected NomeInvalid for uppercase :nome");
7378        };
7379        assert_eq!(nome, "MyApp");
7380        assert!(
7381            reason.contains("uppercase") && reason.contains("myapp"),
7382            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
7383        );
7384    }
7385
7386    #[test]
7387    fn validate_nome_rejects_underscore() {
7388        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
7389        // `_`; the apiserver rejects on admission across every derived
7390        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
7391        // and `:children :caixa` (31bfa43).
7392        let c = caixa_with_nome("my_app");
7393        let err = c.validate_nome().unwrap_err();
7394        assert!(
7395            matches!(
7396                err,
7397                ManifestError::NomeInvalid { ref nome, ref reason }
7398                    if nome == "my_app" && reason.contains('_')
7399            ),
7400            "got {err:?}"
7401        );
7402    }
7403
7404    #[test]
7405    fn validate_nome_rejects_dot() {
7406        // A `:nome` is a single DNS-1123 label, not a subdomain. The
7407        // "I want to namespace with `.`" footgun the gate redirects to
7408        // `-` via the shared predicate's reason wording.
7409        let c = caixa_with_nome("team.app");
7410        let err = c.validate_nome().unwrap_err();
7411        assert!(
7412            matches!(
7413                err,
7414                ManifestError::NomeInvalid { ref nome, ref reason }
7415                    if nome == "team.app" && reason.contains('.')
7416            ),
7417            "got {err:?}"
7418        );
7419    }
7420
7421    #[test]
7422    fn validate_nome_rejects_leading_hyphen() {
7423        // DNS-1123 boundary rule: the label must start with an ASCII
7424        // alphanumeric. Pin the leading-`-` arm explicitly.
7425        let c = caixa_with_nome("-app");
7426        let err = c.validate_nome().unwrap_err();
7427        assert!(
7428            matches!(
7429                err,
7430                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
7431            ),
7432            "got {err:?}"
7433        );
7434    }
7435
7436    #[test]
7437    fn validate_nome_rejects_trailing_hyphen() {
7438        // Symmetric arm of the boundary rule, pinned separately so a
7439        // future relaxation that only checks the leading position
7440        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
7441        // and `_with_trailing_hyphen` on the supervisor / aplicacao
7442        // axes.
7443        let c = caixa_with_nome("app-");
7444        let err = c.validate_nome().unwrap_err();
7445        assert!(
7446            matches!(
7447                err,
7448                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
7449            ),
7450            "got {err:?}"
7451        );
7452    }
7453
7454    #[test]
7455    fn validate_nome_rejects_unicode() {
7456        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
7457        // bytes are rejected by the K8s apiserver on every name axis.
7458        let c = caixa_with_nome("café");
7459        let err = c.validate_nome().unwrap_err();
7460        assert!(
7461            matches!(
7462                err,
7463                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
7464            ),
7465            "got {err:?}"
7466        );
7467    }
7468
7469    #[test]
7470    fn validate_nome_rejects_whitespace() {
7471        // The paste-from-sketch / paste-from-spec footgun. Internal
7472        // whitespace is rejected by every K8s name axis.
7473        let c = caixa_with_nome("my app");
7474        let err = c.validate_nome().unwrap_err();
7475        assert!(
7476            matches!(
7477                err,
7478                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
7479            ),
7480            "got {err:?}"
7481        );
7482    }
7483
7484    #[test]
7485    fn validate_nome_rejects_too_long() {
7486        // 64-byte boundary pin: the K8s apiserver rejects any
7487        // `metadata.name` over 63 bytes at admission; the diagnostic
7488        // names both the 63-byte cap and the actual length so the
7489        // author can shorten in one edit. Mirrors `_too_long` on the
7490        // peer member-/cluster-/child-name axes.
7491        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
7492        let c = caixa_with_nome(&over);
7493        let err = c.validate_nome().unwrap_err();
7494        let ManifestError::NomeInvalid { nome, reason } = err else {
7495            panic!("expected NomeInvalid for over-cap :nome");
7496        };
7497        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
7498        assert!(
7499            reason.contains("63") && reason.contains("64"),
7500            "diagnostic must name the cap + actual length, got {reason:?}"
7501        );
7502    }
7503
7504    #[test]
7505    fn nome_max_length_validates() {
7506        // The 63-byte cap exactly — the boundary-accepting case pinned
7507        // alongside `validate_nome_rejects_too_long` so a future cap
7508        // shift surfaces both arms simultaneously. Mirrors
7509        // `membro_caixa_max_length_validates`,
7510        // `placement_cluster_max_length_validates`,
7511        // `child_caixa_max_length_validates`.
7512        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
7513        caixa_with_nome(&at_cap).validate_nome().unwrap();
7514    }
7515
7516    #[test]
7517    fn nome_empty_takes_precedence_over_invalid() {
7518        // Order pin: the empty arm fires before the predicate is
7519        // consulted. Empty < invalid in self-locating-ness — the
7520        // narrower `NomeEmpty` diagnostic doesn't carry a useless
7521        // `nome: ""` reference into the parser-shaped reason. Mirrors
7522        // `membro_caixa_empty_takes_precedence_over_invalid` on the
7523        // peer axis (3f9d7a0).
7524        let c = caixa_with_nome("");
7525        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
7526    }
7527
7528    #[test]
7529    fn nome_invalid_diagnostic_carries_offending_nome() {
7530        // Diagnostic-shape pin: the error names the offending `:nome`
7531        // verbatim with a non-empty parser-shaped reason, so a `feira
7532        // lint` run can render the diagnostic without re-parsing.
7533        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
7534        let c = caixa_with_nome("MyApp");
7535        let err = c.validate_nome().unwrap_err();
7536        let ManifestError::NomeInvalid { nome, reason } = err else {
7537            panic!("expected NomeInvalid variant");
7538        };
7539        assert_eq!(nome, "MyApp");
7540        assert!(
7541            !reason.is_empty(),
7542            "NomeInvalid `reason` must carry the predicate's wording verbatim"
7543        );
7544    }
7545
7546    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
7547    //
7548    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
7549    // via DNS-1123; this second-axis gate caps the joint
7550    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
7551    // canonical [`crate::lareira_chart_name`] helper's doc comment
7552    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
7553    // "the M4 admission webhook will pin the joint-length invariant
7554    // when it lands". These tests pin it at the manifest-validate
7555    // layer instead, fail-before-pass-after on the 56-byte boundary.
7556
7557    #[test]
7558    fn validate_nome_chart_name_budget_accepts_canonical_template() {
7559        // Positive control: the bare `feira init`-style template's
7560        // `:nome` ("demo") sits far below the cap; the gate must not
7561        // regress this baseline. Same shape every peer
7562        // value-shape-gate baseline pin uses.
7563        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7564        c.validate_nome_chart_name_budget().unwrap();
7565    }
7566
7567    #[test]
7568    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
7569        // Positive-set sweep across the canonical author surface every
7570        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
7571        // `worker`, the `checkout-aplicacao` example members, the
7572        // `akeyless-attest` caixa-tatara fixture). Every value sits
7573        // far below the 55-byte per-`:nome` budget. Same shape every
7574        // peer per-axis baseline pin uses.
7575        for nome in [
7576            "hello-rio",
7577            "cart",
7578            "checkout",
7579            "worker",
7580            "akeyless-attest",
7581            "demo",
7582            "a",
7583        ] {
7584            caixa_with_nome(nome)
7585                .validate_nome_chart_name_budget()
7586                .unwrap_or_else(|e| {
7587                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
7588                });
7589        }
7590    }
7591
7592    #[test]
7593    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
7594        // Boundary-accepting case at the 55-byte per-`:nome` budget —
7595        // the joint chart name is exactly 63 bytes, the DNS-1123 label
7596        // cap. Pinned alongside the rejecting-arm test so a future cap
7597        // shift surfaces both arms simultaneously. Mirrors
7598        // `nome_max_length_validates` on the peer bare-`:nome` axis.
7599        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
7600        caixa_with_nome(&at_cap)
7601            .validate_nome_chart_name_budget()
7602            .unwrap();
7603    }
7604
7605    #[test]
7606    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
7607        // Fail-before-pass-after pin on the 56-byte boundary: the
7608        // smallest `:nome` length that overflows the joint chart-name
7609        // cap. The inner [`is_dns_1123_label`] gate
7610        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
7611        // this gate it silently passed the manifest-validate cascade
7612        // and surfaced as a `helm lint` / apiserver rejection on the
7613        // rendered chart name far from the source `caixa.lisp`, with
7614        // no field naming the overflow. With this gate the diagnostic
7615        // names the offending `:nome` verbatim alongside the rendered
7616        // chart name and the budget, so the author can shorten in one
7617        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
7618        // bare-`:nome` axis.
7619        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7620        let c = caixa_with_nome(&over);
7621        let err = c.validate_nome_chart_name_budget().unwrap_err();
7622        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7623            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
7624        };
7625        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7626        assert_eq!(nome, over);
7627        assert!(
7628            reason.contains("63") && reason.contains("64") && reason.contains("55"),
7629            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
7630             and the per-`:nome` budget (55), got {reason:?}"
7631        );
7632    }
7633
7634    #[test]
7635    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
7636        // The 63-byte `:nome` boundary — passes the bare-`:nome`
7637        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
7638        // joint chart name that overflows the DNS-1123 label cap
7639        // structurally. The most stringent fail-before-pass-after
7640        // surface: every `:nome` in the 56..=63-byte range passed the
7641        // prior cascade and broke at admission.
7642        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
7643        let c = caixa_with_nome(&bare_max);
7644        // The bare-`:nome` gate accepts the 63-byte length.
7645        c.validate_nome().unwrap();
7646        // The new joint-length gate rejects it.
7647        let err = c.validate_nome_chart_name_budget().unwrap_err();
7648        assert!(
7649            matches!(
7650                err,
7651                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
7652                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
7653            ),
7654            "got {err:?}"
7655        );
7656    }
7657
7658    #[test]
7659    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
7660        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
7661        // name appears verbatim in the diagnostic so the author sees
7662        // exactly the string the apiserver / `helm lint` would have
7663        // rejected — no re-derivation required to grep the source.
7664        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
7665        // on the bare-`:nome` axis.
7666        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
7667        let c = caixa_with_nome(&over);
7668        let err = c.validate_nome_chart_name_budget().unwrap_err();
7669        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7670            panic!("expected NomeChartNameBudgetExceeded variant");
7671        };
7672        assert_eq!(nome, over);
7673        let expected_chart = crate::lareira_chart_name(&over);
7674        assert!(
7675            reason.contains(&expected_chart),
7676            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
7677             got {reason:?}"
7678        );
7679        assert!(
7680            reason.contains("lareira-"),
7681            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
7682        );
7683    }
7684
7685    #[test]
7686    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
7687        // Order pin on the layout cascade: the narrower
7688        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
7689        // joint-length budget. A structurally-malformed `:nome` (here:
7690        // uppercase) surfaces its specific shape error rather than
7691        // the chart-name-budget error, even when the joint length
7692        // would also overflow — the narrower diagnostic is more
7693        // self-locating. Mirrors the cascade-precedence pins peer
7694        // gates already use (e.g. `EntradaParaEmpty` before
7695        // `EntradaParaInvalid`).
7696        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7697        let c = caixa_with_nome(&over);
7698        // The bare-shape gate fires first.
7699        let err = c.validate_nome().unwrap_err();
7700        assert!(
7701            matches!(err, ManifestError::NomeInvalid { .. }),
7702            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
7703        );
7704        // And the layout verify cascade surfaces that diagnostic, not
7705        // the budget arm. Inject a path-exists oracle so the cascade
7706        // gets past the manifest-presence check and into the
7707        // value-shape gates.
7708        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7709        let err = crate::LayoutInvariants::verify(
7710            &layout,
7711            &c,
7712            std::path::Path::new("/tmp/caixa-test-fake-root"),
7713        )
7714        .unwrap_err();
7715        let issue = err.to_string();
7716        assert!(
7717            issue.contains("DNS-1123") || issue.contains("uppercase"),
7718            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
7719             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
7720        );
7721    }
7722
7723    #[test]
7724    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
7725        // Cross-axis envelope pin: the layout cascade wraps both
7726        // bare-`:nome` and joint-length-`:nome` failures through the
7727        // same [`LayoutError::NomeViolation`] envelope, since both
7728        // arms are on the `:nome` axis. The user's diagnostic stays
7729        // self-locating ("which axis"), and a future consumer that
7730        // dispatches on the layout-error variant (e.g. a `feira lint`
7731        // exit-code mapping) sees a single per-axis envelope. The
7732        // wrapped `issue:` carries the full inner diagnostic.
7733        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7734        let c = caixa_with_nome(&over);
7735        // The bare-shape gate accepts.
7736        c.validate_nome().unwrap();
7737        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7738        let err = crate::LayoutInvariants::verify(
7739            &layout,
7740            &c,
7741            std::path::Path::new("/tmp/caixa-test-fake-root"),
7742        )
7743        .unwrap_err();
7744        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
7745            panic!("expected LayoutError::NomeViolation, got {err:?}");
7746        };
7747        assert_eq!(caixa, over);
7748        assert!(
7749            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
7750            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
7751        );
7752    }
7753
7754    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
7755
7756    fn caixa_with_versao(versao: &str) -> Caixa {
7757        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7758        c.versao = versao.to_string();
7759        c
7760    }
7761
7762    #[test]
7763    fn validate_versao_accepts_canonical_template() {
7764        // Positive control: the bare `feira init`-style template's
7765        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
7766        // must not regress this baseline shape. A future tightening of
7767        // the accepted set surfaces here as a test failure first.
7768        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7769        c.validate_versao().unwrap();
7770    }
7771
7772    #[test]
7773    fn validate_versao_accepts_canonical_forms() {
7774        // Positive-set sweep: each realistic SemVer-2 shape the
7775        // substrate's downstream consumers accept must pass — bare
7776        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
7777        // build metadata (`+build.42`), the combined form, and the
7778        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
7779        // the peer `:nome` axis (6c992f8).
7780        for versao in [
7781            "0.1.0",
7782            "0.0.0",
7783            "1.0.0",
7784            "0.2.0-rc.1",
7785            "1.0.0-alpha.0",
7786            "1.0.0+build.42",
7787            "1.0.0-rc.1+build.42",
7788            "10.20.30",
7789        ] {
7790            caixa_with_versao(versao)
7791                .validate_versao()
7792                .unwrap_or_else(|e| {
7793                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
7794                });
7795        }
7796    }
7797
7798    #[test]
7799    fn validate_versao_rejects_empty() {
7800        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
7801        // an empty `:versao` (the derive macro stores the raw String);
7802        // the gate's empty arm names the offending axis with a narrower
7803        // diagnostic than the `VersaoInvalid` parse arm would emit.
7804        // Mirrors `validate_nome_rejects_empty` (6c992f8).
7805        let c = caixa_with_versao("");
7806        let err = c.validate_versao().unwrap_err();
7807        assert_eq!(err, ManifestError::VersaoEmpty);
7808    }
7809
7810    #[test]
7811    fn validate_versao_rejects_git_tag_shape() {
7812        // The canonical "I copied the git tag verbatim" footgun —
7813        // `feira publish` *emits* `v<versao>` git tags, so a leaked
7814        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
7815        // shift every downstream consumer's version axis. `semver`
7816        // rejects the leading `v` at parse time; the gate moves the
7817        // diagnostic to the source `caixa.lisp`.
7818        let c = caixa_with_versao("v0.1.0");
7819        let err = c.validate_versao().unwrap_err();
7820        let ManifestError::VersaoInvalid { versao, reason } = err else {
7821            panic!("expected VersaoInvalid for git-tag-shape :versao");
7822        };
7823        assert_eq!(versao, "v0.1.0");
7824        assert!(
7825            !reason.is_empty(),
7826            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
7827        );
7828    }
7829
7830    #[test]
7831    fn validate_versao_rejects_missing_patch() {
7832        // The canonical "I shortened it" footgun — SemVer-2 requires
7833        // three parts. Cargo's `version =` field accepts the shortened
7834        // form as a requirement, conflating the two leaks across the
7835        // typed `:deps :versao` vs top-level `:versao` axes; the gate
7836        // pins the top-level axis to the strict three-part shape.
7837        let c = caixa_with_versao("0.1");
7838        let err = c.validate_versao().unwrap_err();
7839        assert!(
7840            matches!(
7841                err,
7842                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
7843            ),
7844            "got {err:?}"
7845        );
7846    }
7847
7848    #[test]
7849    fn validate_versao_rejects_requirement_shape() {
7850        // The canonical "I leaked a requirement into a version" footgun —
7851        // the typed `:deps :versao` / `:membros :versao` axes accept
7852        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
7853        // concrete `Version`. Without this gate the two typed surfaces
7854        // would silently overlap, and a top-level `^0.1` would surface
7855        // at `helm install` time as a Chart.yaml version rejection far
7856        // from the source `caixa.lisp`.
7857        let c = caixa_with_versao("^0.1");
7858        let err = c.validate_versao().unwrap_err();
7859        assert!(
7860            matches!(
7861                err,
7862                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
7863            ),
7864            "got {err:?}"
7865        );
7866    }
7867
7868    #[test]
7869    fn validate_versao_rejects_docker_tag_shape() {
7870        // The "I confused it with a docker tag" footgun — `latest`,
7871        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
7872        // SemVer rejects at parse time; the gate moves the diagnostic
7873        // to the source `caixa.lisp`.
7874        for bad in ["latest", "main", "stable"] {
7875            let c = caixa_with_versao(bad);
7876            let err = c.validate_versao().unwrap_err();
7877            assert!(
7878                matches!(
7879                    err,
7880                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
7881                ),
7882                "got {err:?} for {bad:?}"
7883            );
7884        }
7885    }
7886
7887    #[test]
7888    fn validate_versao_rejects_four_part_form() {
7889        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
7890        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
7891        // semver crate rejects the extra `.0` at parse time.
7892        let c = caixa_with_versao("0.1.0.0");
7893        let err = c.validate_versao().unwrap_err();
7894        assert!(
7895            matches!(
7896                err,
7897                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
7898            ),
7899            "got {err:?}"
7900        );
7901    }
7902
7903    #[test]
7904    fn versao_empty_takes_precedence_over_invalid() {
7905        // Order pin: the empty arm fires before the parser is consulted.
7906        // Empty < invalid in self-locating-ness — the narrower
7907        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
7908        // reference into the parser-shaped reason. Mirrors
7909        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
7910        // peer axis.
7911        let c = caixa_with_versao("");
7912        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
7913    }
7914
7915    #[test]
7916    fn versao_invalid_diagnostic_carries_offending_versao() {
7917        // Diagnostic-shape pin: the error names the offending `:versao`
7918        // verbatim with a non-empty parser-shaped reason, so a `feira
7919        // lint` run can render the diagnostic without re-parsing.
7920        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
7921        let c = caixa_with_versao("v0.1.0");
7922        let err = c.validate_versao().unwrap_err();
7923        let ManifestError::VersaoInvalid { versao, reason } = err else {
7924            panic!("expected VersaoInvalid variant");
7925        };
7926        assert_eq!(versao, "v0.1.0");
7927        assert!(
7928            !reason.is_empty(),
7929            "VersaoInvalid `reason` must carry the parser's wording verbatim"
7930        );
7931    }
7932
7933    #[test]
7934    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
7935        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
7936        // for `:upgrade-from :from` must also pass `validate_versao` —
7937        // the two `:versao`-typed surfaces (top-level `:versao`,
7938        // `:upgrade-from :from`) consume the *same* `semver::Version`
7939        // parser, so they must agree on the accepted set. Without this
7940        // pin, a future tightening of one axis could silently diverge
7941        // from the other. Mirrors the `:versao` requirement-axis
7942        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
7943        // commits established.
7944        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
7945            // From the canonical UpgradeFromEntry round-trip fixture
7946            // (`upgrade::tests::round_trip_load_module` peers).
7947            let entry = crate::UpgradeFromEntry {
7948                from: versao.to_string(),
7949                instructions: Vec::new(),
7950            };
7951            entry
7952                .validate()
7953                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
7954            caixa_with_versao(versao)
7955                .validate_versao()
7956                .unwrap_or_else(|e| {
7957                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
7958                });
7959        }
7960    }
7961
7962    // ── Caixa::validate_restart_window — supervisor restart-window
7963    //    folds through the shared `supervisor::duration_codec` ────────
7964
7965    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
7966        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
7967        c.kind = CaixaKind::Supervisor;
7968        c.restart_window = window.map(str::to_string);
7969        c
7970    }
7971
7972    #[test]
7973    fn validate_restart_window_accepts_none() {
7974        // The canonical "omit the slot to express no reset" shape — a
7975        // `None` raw string is the absence of the typed
7976        // `:restart-window` slot, which is exactly the SupervisorSpec
7977        // "never reset" semantics. The gate must be a no-op here; a
7978        // future tightening that rejected `None` would force every
7979        // supervisor caixa to authoring-time pin a window even when
7980        // the OTP semantics call for none.
7981        caixa_with_restart_window(None)
7982            .validate_restart_window()
7983            .unwrap();
7984    }
7985
7986    #[test]
7987    fn validate_restart_window_accepts_canonical_forms() {
7988        // Positive-set sweep across the canonical authoring units the
7989        // shared `supervisor::duration_codec::parse` accepts —
7990        // matches the codec-side `parse_accepts_integer_canonical_units`
7991        // pin in supervisor::tests so a future codec-side tightening
7992        // surfaces simultaneously on both axes.
7993        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
7994            caixa_with_restart_window(Some(window))
7995                .validate_restart_window()
7996                .unwrap_or_else(|e| {
7997                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
7998                });
7999        }
8000    }
8001
8002    #[test]
8003    fn validate_restart_window_rejects_fractional_seconds() {
8004        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
8005        // as f64 to 1.5 → renders back as `"1500ms"` on first
8006        // serialize). Prior to the fold + this gate, the inline
8007        // `parse_window_inline` accepted f64 magnitudes and silently
8008        // produced a `Duration::from_secs_f64(1.5)`, divergent from
8009        // the shared codec's integer-magnitude discipline on the
8010        // serde-routed siblings. The gate now surfaces a self-locating
8011        // diagnostic at the manifest layer.
8012        let err = caixa_with_restart_window(Some("1.5s"))
8013            .validate_restart_window()
8014            .unwrap_err();
8015        let ManifestError::RestartWindowMalformed {
8016            restart_window,
8017            reason,
8018        } = err
8019        else {
8020            panic!("expected RestartWindowMalformed for fractional seconds");
8021        };
8022        assert_eq!(restart_window, "1.5s");
8023        assert!(
8024            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
8025            "diagnostic must carry shared-codec wording, got {reason:?}"
8026        );
8027    }
8028
8029    #[test]
8030    fn validate_restart_window_rejects_decimal_shaped_integer() {
8031        // The `"1.0s"` class — numerically `1s` exactly, but the
8032        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
8033        // gets the same canonical-form diagnostic.
8034        let err = caixa_with_restart_window(Some("1.0s"))
8035            .validate_restart_window()
8036            .unwrap_err();
8037        assert!(
8038            matches!(
8039                err,
8040                ManifestError::RestartWindowMalformed { ref restart_window, .. }
8041                    if restart_window == "1.0s"
8042            ),
8043            "got {err:?}"
8044        );
8045    }
8046
8047    #[test]
8048    fn validate_restart_window_rejects_half_unit_minute() {
8049        // `"0.5m"` is the unit-fraction footgun — author writes a
8050        // human-readable half-minute, the prior inline parser silently
8051        // produced `Duration::from_secs_f64(30.0)` and serde
8052        // re-emitted as `"30s"`, rewriting author intent. The gate
8053        // closes the loop at the manifest layer.
8054        let err = caixa_with_restart_window(Some("0.5m"))
8055            .validate_restart_window()
8056            .unwrap_err();
8057        let ManifestError::RestartWindowMalformed {
8058            restart_window,
8059            reason,
8060        } = err
8061        else {
8062            panic!("expected RestartWindowMalformed");
8063        };
8064        assert_eq!(restart_window, "0.5m");
8065        assert!(
8066            reason.contains("\"30s\""),
8067            "diagnostic must point at the canonical-form remediation, got {reason:?}"
8068        );
8069    }
8070
8071    #[test]
8072    fn validate_restart_window_rejects_leading_sign() {
8073        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
8074        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
8075        // and was caught by the `num < 0.0` arm which silently
8076        // returned `None`, dropping the author-supplied window). The
8077        // shared codec's digit-only gate rejects both with a unified
8078        // canonical-form diagnostic; the manifest-layer wrapper names
8079        // the offending value.
8080        for bad in ["+30s", "-30s"] {
8081            let err = caixa_with_restart_window(Some(bad))
8082                .validate_restart_window()
8083                .unwrap_err();
8084            assert!(
8085                matches!(
8086                    err,
8087                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
8088                        if restart_window == bad
8089                ),
8090                "got {err:?} for {bad:?}"
8091            );
8092        }
8093    }
8094
8095    #[test]
8096    fn validate_restart_window_rejects_unknown_unit() {
8097        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
8098        // unit dispatch surfaces an `unknown duration unit` reason;
8099        // the manifest-layer wrapper names the offending value.
8100        let err = caixa_with_restart_window(Some("30x"))
8101            .validate_restart_window()
8102            .unwrap_err();
8103        let ManifestError::RestartWindowMalformed {
8104            restart_window,
8105            reason,
8106        } = err
8107        else {
8108            panic!("expected RestartWindowMalformed for unknown unit");
8109        };
8110        assert_eq!(restart_window, "30x");
8111        assert!(
8112            reason.contains("unknown duration unit"),
8113            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
8114        );
8115    }
8116
8117    #[test]
8118    fn validate_restart_window_rejects_garbage() {
8119        // Pure non-numeric magnitude (`"abc"`) falls through to the
8120        // shared codec's narrower `"bad duration magnitude"` arm. Same
8121        // diagnostic shape as the codec-side
8122        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
8123        let err = caixa_with_restart_window(Some("abc"))
8124            .validate_restart_window()
8125            .unwrap_err();
8126        let ManifestError::RestartWindowMalformed {
8127            restart_window,
8128            reason,
8129        } = err
8130        else {
8131            panic!("expected RestartWindowMalformed for garbage");
8132        };
8133        assert_eq!(restart_window, "abc");
8134        assert!(
8135            reason.contains("bad duration magnitude"),
8136            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
8137        );
8138    }
8139
8140    #[test]
8141    fn validate_restart_window_rejects_empty_string() {
8142        // The empty-after-trim edge case — distinct from the `None`
8143        // canonical "omit the slot" shape. The shared codec's
8144        // digit-only gate refuses an empty magnitude; the manifest
8145        // layer names the offending `""` so the author can grep for
8146        // the literal empty value in their `caixa.lisp` and either
8147        // remove the slot (the canonical "no reset" shape) or pin a
8148        // positive duration.
8149        let err = caixa_with_restart_window(Some(""))
8150            .validate_restart_window()
8151            .unwrap_err();
8152        assert!(
8153            matches!(
8154                err,
8155                ManifestError::RestartWindowMalformed { ref restart_window, .. }
8156                    if restart_window.is_empty()
8157            ),
8158            "got {err:?}"
8159        );
8160    }
8161
8162    #[test]
8163    fn validate_restart_window_diagnostic_carries_offending_value() {
8164        // Diagnostic-shape pin (peer with
8165        // `nome_invalid_diagnostic_carries_offending_nome` /
8166        // `versao_invalid_diagnostic_carries_offending_versao`): the
8167        // error names the offending raw `:restart-window` verbatim
8168        // with a non-empty shared-codec-shaped reason, so a `feira
8169        // lint` run can render the diagnostic without re-parsing.
8170        let err = caixa_with_restart_window(Some("1.5s"))
8171            .validate_restart_window()
8172            .unwrap_err();
8173        let ManifestError::RestartWindowMalformed {
8174            restart_window,
8175            reason,
8176        } = err
8177        else {
8178            panic!("expected RestartWindowMalformed variant");
8179        };
8180        assert_eq!(restart_window, "1.5s");
8181        assert!(
8182            !reason.is_empty(),
8183            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
8184        );
8185    }
8186
8187    #[test]
8188    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
8189        // Behavioral parity pin after the fold (`parse_window_inline`
8190        // deletion): the canonical `"60s"` still produces
8191        // `Duration::from_secs(60)` on the typed view — the fold is
8192        // semantically equivalent to the prior inline parser on the
8193        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
8194        // pin, narrowed to the parser-side contract.
8195        let c = caixa_with_restart_window(Some("60s"));
8196        let view = c.supervisor_view().expect("Supervisor kind has a view");
8197        assert_eq!(
8198            view.restart_window,
8199            Some(std::time::Duration::from_secs(60))
8200        );
8201    }
8202
8203    #[test]
8204    fn supervisor_view_soft_swallows_what_validate_rejects() {
8205        // Parity pin between the view-construction path and the
8206        // manifest-level validator: the same `"1.5s"` that surfaces
8207        // `RestartWindowMalformed` at `validate_restart_window` time
8208        // becomes `restart_window: None` on the typed view (the fold
8209        // preserves the existing best-effort shape of `supervisor_view`).
8210        // The contract is: a layout-verifier / `feira lint` flow that
8211        // cares about the malformed-window axis MUST consult
8212        // `validate_restart_window` — relying solely on the view's
8213        // `None` swallows the diagnostic silently. This pin makes the
8214        // expectation a typed invariant.
8215        let c = caixa_with_restart_window(Some("1.5s"));
8216        let view = c.supervisor_view().expect("Supervisor kind has a view");
8217        assert_eq!(
8218            view.restart_window, None,
8219            "view-construction path soft-swallows the parse error to None"
8220        );
8221        // And the manifest-level validator does NOT soft-swallow:
8222        assert!(
8223            matches!(
8224                c.validate_restart_window().unwrap_err(),
8225                ManifestError::RestartWindowMalformed { ref restart_window, .. }
8226                    if restart_window == "1.5s"
8227            ),
8228            "validator must surface the offending value",
8229        );
8230    }
8231
8232    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
8233
8234    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
8235        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8236        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
8237        c.exe = exe.into_iter().map(String::from).collect();
8238        c.servicos = servicos.into_iter().map(String::from).collect();
8239        c
8240    }
8241
8242    #[test]
8243    fn validate_code_paths_accepts_canonical_template() {
8244        // The bare `Caixa::template` shape is the gate's identity element
8245        // on the canonical authoring shape — `:bibliotecas
8246        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
8247        // that the gate is non-disruptive against every existing caixa.
8248        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8249        c.validate_code_paths().unwrap();
8250    }
8251
8252    #[test]
8253    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
8254        // Positive control sweep: a canonical-shaped path on every slot
8255        // passes. Mirrors the peer
8256        // `behavior::validate_every_slot_relative_is_ok` pin.
8257        let c = caixa_with_code_paths(
8258            vec!["lib/demo.lisp", "lib/helpers.lisp"],
8259            vec!["exe/demo", "exe/tool"],
8260            vec!["servicos/demo.computeunit.yaml"],
8261        );
8262        c.validate_code_paths().unwrap();
8263    }
8264
8265    #[test]
8266    fn validate_code_paths_accepts_all_empty_lists() {
8267        // The empty-list identity element: every Caixa with no declared
8268        // code paths trivially passes (Supervisor / Aplicacao kinds rely
8269        // on this — the OwnCode gate already rejected them before the
8270        // path-shape gate runs in the layout, but the validator itself
8271        // must accept the empty shape).
8272        let c = caixa_with_code_paths(vec![], vec![], vec![]);
8273        c.validate_code_paths().unwrap();
8274    }
8275
8276    #[test]
8277    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
8278        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
8279        let err = c.validate_code_paths().unwrap_err();
8280        assert!(
8281            matches!(
8282                err,
8283                ManifestError::CodePathEmpty {
8284                    slot: ":bibliotecas"
8285                }
8286            ),
8287            "got {err:?}",
8288        );
8289    }
8290
8291    #[test]
8292    fn validate_code_paths_rejects_empty_exe_entry() {
8293        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
8294        let err = c.validate_code_paths().unwrap_err();
8295        assert!(
8296            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
8297            "got {err:?}",
8298        );
8299    }
8300
8301    #[test]
8302    fn validate_code_paths_rejects_empty_servicos_entry() {
8303        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
8304        let err = c.validate_code_paths().unwrap_err();
8305        assert!(
8306            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
8307            "got {err:?}",
8308        );
8309    }
8310
8311    #[test]
8312    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
8313        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
8314        // so an absolute path that resolves on disk silently passes the
8315        // layout's existence check — the canonical sandbox-escape on
8316        // the biblioteca axis.
8317        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8318        let err = c.validate_code_paths().unwrap_err();
8319        let ManifestError::CodePathAbsolute { slot, path } = err else {
8320            panic!("expected CodePathAbsolute, got {err:?}");
8321        };
8322        assert_eq!(slot, ":bibliotecas");
8323        assert_eq!(path, PathBuf::from("/etc/passwd"));
8324    }
8325
8326    #[test]
8327    fn validate_code_paths_rejects_absolute_exe_entry() {
8328        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
8329        let err = c.validate_code_paths().unwrap_err();
8330        let ManifestError::CodePathAbsolute { slot, path } = err else {
8331            panic!("expected CodePathAbsolute, got {err:?}");
8332        };
8333        assert_eq!(slot, ":exe");
8334        assert_eq!(path, PathBuf::from("/usr/bin/env"));
8335    }
8336
8337    #[test]
8338    fn validate_code_paths_rejects_absolute_servicos_entry() {
8339        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
8340        let err = c.validate_code_paths().unwrap_err();
8341        let ManifestError::CodePathAbsolute { slot, path } = err else {
8342            panic!("expected CodePathAbsolute, got {err:?}");
8343        };
8344        assert_eq!(slot, ":servicos");
8345        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
8346    }
8347
8348    #[test]
8349    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
8350        // Canonical "I want a lib from a sibling caixa" footgun on the
8351        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
8352        // downstream, so a leading `..` traverses to the parent of the
8353        // caixa root with no diagnostic at layout time if the resolved
8354        // target exists.
8355        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
8356        let err = c.validate_code_paths().unwrap_err();
8357        let ManifestError::CodePathParentEscape { slot, path } = err else {
8358            panic!("expected CodePathParentEscape, got {err:?}");
8359        };
8360        assert_eq!(slot, ":bibliotecas");
8361        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
8362    }
8363
8364    #[test]
8365    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
8366        // Mid-path `..` defeats the layout's component-aware
8367        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
8368        // `starts_with(<root>/exe)` is true, but the canonical resolution
8369        // lives outside the caixa root. Caught regardless of where the
8370        // `..` sits — mirrors the peer
8371        // `behavior::validate_rejects_parent_escape_mid_path` pin.
8372        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
8373        let err = c.validate_code_paths().unwrap_err();
8374        let ManifestError::CodePathParentEscape { slot, path } = err else {
8375            panic!("expected CodePathParentEscape, got {err:?}");
8376        };
8377        assert_eq!(slot, ":exe");
8378        assert_eq!(path, PathBuf::from("exe/../../escape"));
8379    }
8380
8381    #[test]
8382    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
8383        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
8384        let err = c.validate_code_paths().unwrap_err();
8385        let ManifestError::CodePathParentEscape { slot, path } = err else {
8386            panic!("expected CodePathParentEscape, got {err:?}");
8387        };
8388        assert_eq!(slot, ":servicos");
8389        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
8390    }
8391
8392    #[test]
8393    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
8394        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
8395        // `:servicos`. A manifest with malformed entries on all three
8396        // surfaces surfaces the `:bibliotecas` defect first, mirroring
8397        // the canonical declaration order
8398        // `Caixa::declared_foreign_code_slots` already establishes for
8399        // the foreign-code-slot diagnostic.
8400        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
8401        let err = c.validate_code_paths().unwrap_err();
8402        assert!(
8403            matches!(
8404                err,
8405                ManifestError::CodePathEmpty {
8406                    slot: ":bibliotecas"
8407                }
8408            ),
8409            "got {err:?}",
8410        );
8411    }
8412
8413    #[test]
8414    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
8415        // Within-slot precedence pin: empty → absolute → parent-escape,
8416        // matching the [`PathShapeViolation`] arm-ordering every peer
8417        // `is_sandboxed_relative_path` caller follows (b0c8389
8418        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
8419        // `:bibliotecas` list whose first entry is empty *and* whose
8420        // later entries are absolute/parent-escape surfaces the empty
8421        // arm first, on the lexicographically-earliest offending entry.
8422        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
8423        let err = c.validate_code_paths().unwrap_err();
8424        assert!(
8425            matches!(
8426                err,
8427                ManifestError::CodePathEmpty {
8428                    slot: ":bibliotecas"
8429                }
8430            ),
8431            "got {err:?}",
8432        );
8433    }
8434
8435    #[test]
8436    fn validate_code_paths_first_offender_per_slot_wins() {
8437        // Within a single slot, the first declaration-order offender
8438        // surfaces — pins that the gate is left-to-right deterministic
8439        // (peer of every `*_first_collision_*` pin on duplicate gates).
8440        let c = caixa_with_code_paths(
8441            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
8442            vec![],
8443            vec![],
8444        );
8445        let err = c.validate_code_paths().unwrap_err();
8446        let ManifestError::CodePathAbsolute { slot, path } = err else {
8447            panic!("expected CodePathAbsolute, got {err:?}");
8448        };
8449        assert_eq!(slot, ":bibliotecas");
8450        assert_eq!(path, PathBuf::from("/etc/escape"));
8451    }
8452
8453    #[test]
8454    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
8455        // Diagnostic-shape pin (peer with
8456        // `nome_invalid_diagnostic_carries_offending_nome` /
8457        // `versao_invalid_diagnostic_carries_offending_versao`): the
8458        // error's Display surfaces both the offending `:slot` tag and
8459        // the offending path verbatim, so a `feira lint` run can render
8460        // the diagnostic without re-parsing.
8461        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8462        let rendered = c.validate_code_paths().unwrap_err().to_string();
8463        assert!(
8464            rendered.contains(":bibliotecas"),
8465            "diagnostic must name the offending slot: {rendered}",
8466        );
8467        assert!(
8468            rendered.contains("/etc/passwd"),
8469            "diagnostic must quote the offending path: {rendered}",
8470        );
8471    }
8472
8473    #[test]
8474    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
8475        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
8476        // axis. Without the gate `feira build` re-parses the same lib
8477        // twice, wasting work and silently masking the author's intent
8478        // to declare a *second* biblioteca.
8479        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
8480        let err = c.validate_code_paths().unwrap_err();
8481        let ManifestError::CodePathDuplicate { slot, path } = err else {
8482            panic!("expected CodePathDuplicate, got {err:?}");
8483        };
8484        assert_eq!(slot, ":bibliotecas");
8485        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
8486    }
8487
8488    #[test]
8489    fn validate_code_paths_rejects_duplicate_exe_entry() {
8490        // Same footgun on the Binario surface. The future `caixa-flake`
8491        // emitter that materializes each `:exe` entry as a flake
8492        // `packages.<name>` derivation would collide on the duplicate
8493        // package key — surfaced here at the typed-validate layer with a
8494        // self-locating diagnostic instead.
8495        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
8496        let err = c.validate_code_paths().unwrap_err();
8497        let ManifestError::CodePathDuplicate { slot, path } = err else {
8498            panic!("expected CodePathDuplicate, got {err:?}");
8499        };
8500        assert_eq!(slot, ":exe");
8501        assert_eq!(path, PathBuf::from("exe/cli"));
8502    }
8503
8504    #[test]
8505    fn validate_code_paths_rejects_duplicate_servicos_entry() {
8506        // Same footgun on the Servico surface. The peer caixa-helm /
8507        // caixa-flux renderers refuse `:servicos.len() != 1` with the
8508        // narrower `UnsupportedServicoCount` diagnostic, but that
8509        // diagnostic surfaces "too many servicos" without naming
8510        // "duplicate entry" — the typed self-locating framing only lands
8511        // at this gate.
8512        let c = caixa_with_code_paths(
8513            vec![],
8514            vec![],
8515            vec![
8516                "servicos/demo.computeunit.yaml",
8517                "servicos/demo.computeunit.yaml",
8518            ],
8519        );
8520        let err = c.validate_code_paths().unwrap_err();
8521        let ManifestError::CodePathDuplicate { slot, path } = err else {
8522            panic!("expected CodePathDuplicate, got {err:?}");
8523        };
8524        assert_eq!(slot, ":servicos");
8525        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
8526    }
8527
8528    #[test]
8529    fn validate_code_paths_accepts_same_path_across_slots() {
8530        // Per-list scope pin: a `:bibliotecas` entry that happens to
8531        // collide with an `:exe` or `:servicos` entry as a *string* is
8532        // not a duplicate by this gate (each list gets its own HashSet),
8533        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
8534        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
8535        // shape on the dep axis). The structural `starts_with(<exe |
8536        // servicos>_dir)` fence at layout time prevents the realistic
8537        // cross-slot collision case from existing on disk, but the gate's
8538        // per-list scope is correct independent of that downstream fence.
8539        let c = caixa_with_code_paths(
8540            vec!["lib/x.lisp"],
8541            vec!["exe/x"],
8542            vec!["servicos/x.computeunit.yaml"],
8543        );
8544        c.validate_code_paths().unwrap();
8545    }
8546
8547    #[test]
8548    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
8549        // Within-slot ordering pin: structural defects (empty / absolute
8550        // / parent-escape) fire before the duplicate gate on the same
8551        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
8552        // surfaces the narrower `CodePathEmpty` for the empty entry
8553        // first, not the duplicate on the later pair — same arm-ordering
8554        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
8555        // `:autores` 86c769b, `:deps` 359fba5).
8556        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
8557        let err = c.validate_code_paths().unwrap_err();
8558        assert!(
8559            matches!(
8560                err,
8561                ManifestError::CodePathEmpty {
8562                    slot: ":bibliotecas"
8563                }
8564            ),
8565            "got {err:?}",
8566        );
8567    }
8568
8569    #[test]
8570    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
8571        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
8572        // duplicates surface before `:exe` duplicates, matching the
8573        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
8574        // order every peer per-slot diagnostic on this surface follows.
8575        let c = caixa_with_code_paths(
8576            vec!["lib/x.lisp", "lib/x.lisp"],
8577            vec!["exe/y", "exe/y"],
8578            vec![],
8579        );
8580        let err = c.validate_code_paths().unwrap_err();
8581        let ManifestError::CodePathDuplicate { slot, path } = err else {
8582            panic!("expected CodePathDuplicate, got {err:?}");
8583        };
8584        assert_eq!(slot, ":bibliotecas");
8585        assert_eq!(path, PathBuf::from("lib/x.lisp"));
8586    }
8587
8588    #[test]
8589    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
8590        // Diagnostic-shape pin (peer with
8591        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8592        // on the structural arm): the duplicate-arm Display surfaces both
8593        // the offending `:slot` tag and the offending path verbatim, so a
8594        // `feira lint` run can render the diagnostic without re-parsing.
8595        let c = caixa_with_code_paths(
8596            vec![],
8597            vec![],
8598            vec![
8599                "servicos/demo.computeunit.yaml",
8600                "servicos/demo.computeunit.yaml",
8601            ],
8602        );
8603        let rendered = c.validate_code_paths().unwrap_err().to_string();
8604        assert!(
8605            rendered.contains(":servicos"),
8606            "diagnostic must name the offending slot: {rendered}",
8607        );
8608        assert!(
8609            rendered.contains("servicos/demo.computeunit.yaml"),
8610            "diagnostic must quote the offending path: {rendered}",
8611        );
8612    }
8613
8614    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
8615    //
8616    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
8617    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
8618    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
8619    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
8620    // at parse time — the same downstream consumer the peer `:behavior
8621    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
8622    // `:upgrade-from :state-change :script` (33cc830,
8623    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
8624    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
8625    // nix-built executable surface (`"exe/<name>"` shape per the canonical
8626    // [`crate::LayoutError::ExeOutsideDir`] error message and every
8627    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
8628    // is the `.computeunit.yaml` ComputeUnit-CR axis.
8629
8630    #[test]
8631    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
8632        // Canonical "I dragged the wrong file from the workspace tree"
8633        // footgun on the biblioteca axis. Without the gate `feira build`
8634        // hands the extensionless path to `tatara_lisp::read` and fails
8635        // with a parser-shaped diagnostic far from the source caixa.lisp,
8636        // with no field naming the offending `:bibliotecas` entry.
8637        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
8638            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8639            let err = c.validate_code_paths().unwrap_err();
8640            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8641                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8642            };
8643            assert_eq!(slot, ":bibliotecas");
8644            assert_eq!(path, PathBuf::from(relpath));
8645        }
8646    }
8647
8648    #[test]
8649    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
8650        // Wrong-extension sweep across common authoring footguns. Same
8651        // sweep posture as the peer
8652        // `behavior::validate_rejects_wrong_extension` (c97815a) and
8653        // `upgrade::tests::state_change_rejects_wrong_extension_script`
8654        // (33cc830) cases.
8655        for relpath in [
8656            "lib/demo.rs",
8657            "lib/demo.txt",
8658            "lib/demo.md",
8659            "lib/demo.json",
8660            "lib/demo.yaml",
8661            "lib/demo.toml",
8662            "lib/demo.lisp.bak",
8663            "lib/demo.lispx",
8664            "lib/demo.lis",
8665        ] {
8666            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8667            let err = c.validate_code_paths().unwrap_err();
8668            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8669                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8670            };
8671            assert_eq!(slot, ":bibliotecas");
8672            assert_eq!(path, PathBuf::from(relpath));
8673        }
8674    }
8675
8676    #[test]
8677    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
8678        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
8679        // contract. An uppercase `.LISP` shape that the layout's existence
8680        // check would (case-insensitively, on case-insensitive volumes)
8681        // match the on-disk file still mismatches the canonical form the
8682        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
8683        // contract. Mirrors the peer
8684        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
8685        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
8686        // (33cc830) sweeps.
8687        for relpath in [
8688            "lib/demo.LISP",
8689            "lib/demo.Lisp",
8690            "lib/demo.LiSp",
8691            "lib/demo.lISP",
8692        ] {
8693            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8694            let err = c.validate_code_paths().unwrap_err();
8695            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8696                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8697            };
8698            assert_eq!(slot, ":bibliotecas");
8699            assert_eq!(path, PathBuf::from(relpath));
8700        }
8701    }
8702
8703    #[test]
8704    fn validate_code_paths_accepts_canonical_lisp_shapes() {
8705        // Positive-control sweep through every canonical authoring shape
8706        // every in-tree fixture and the `Caixa::template` scaffold use.
8707        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
8708        // (c97815a) and the lifted predicate's own
8709        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
8710        // (33cc830).
8711        for relpath in [
8712            "lib/demo.lisp",
8713            "lib/handlers.lisp",
8714            "lib/migrations/v01-to-v02.lisp",
8715            "demo.lisp",
8716            "a.lisp",
8717            "./lib/demo.lisp",
8718            "lib/./handlers.lisp",
8719            "lib/migrations/v.0.1.lisp",
8720        ] {
8721            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8722            c.validate_code_paths()
8723                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8724        }
8725    }
8726
8727    #[test]
8728    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
8729        // The file-type gate is per-slot — only `:bibliotecas` carries the
8730        // tatara-lisp-source contract. An extensionless `:exe` entry
8731        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
8732        // canonical shapes every in-tree fixture uses, and must continue
8733        // to pass validate. Pins that a future tightening that broadens
8734        // the `.lisp` gate to either axis surfaces as a test failure
8735        // rather than as a silent breaking change to existing valid
8736        // manifests.
8737        let c = caixa_with_code_paths(
8738            vec![],
8739            vec!["exe/demo", "exe/tool"],
8740            vec!["servicos/demo.computeunit.yaml"],
8741        );
8742        c.validate_code_paths().unwrap();
8743    }
8744
8745    #[test]
8746    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
8747        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
8748        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
8749        // sandbox-shape diagnostic first (the `.lisp` remediation would
8750        // be misleading when the offending path can never resolve under
8751        // the caixa root anyway). Mirrors the peer
8752        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
8753        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
8754        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
8755        // on `:upgrade-from :state-change :script` (33cc830).
8756        //
8757        // Empty wins (the strictly-smaller-scope structural arm).
8758        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
8759        assert!(
8760            matches!(
8761                c.validate_code_paths().unwrap_err(),
8762                ManifestError::CodePathEmpty {
8763                    slot: ":bibliotecas"
8764                }
8765            ),
8766            "empty must win over non-lisp-extension",
8767        );
8768        // Absolute wins (the path can't resolve under the caixa root).
8769        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8770        let err = c.validate_code_paths().unwrap_err();
8771        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8772            panic!("absolute must win over non-lisp-extension, got {err:?}");
8773        };
8774        assert_eq!(slot, ":bibliotecas");
8775        // ParentEscape wins (the path escapes the caixa root).
8776        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
8777        let err = c.validate_code_paths().unwrap_err();
8778        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8779            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
8780        };
8781        assert_eq!(slot, ":bibliotecas");
8782    }
8783
8784    #[test]
8785    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
8786        // Within-slot precedence pin: the per-entry file-type shape gate
8787        // fires before the cross-entry duplicate gate, so the narrower
8788        // structural defect dominates the uniqueness diagnostic. A
8789        // `("lib/x.txt" "lib/x.txt")` shape surfaces
8790        // `CodePathNonLispExtension` on the first entry rather than
8791        // `CodePathDuplicate` on the pair — same posture every per-entry
8792        // shape-gate-precedes-duplicate cascade follows on this surface
8793        // (the empty / absolute / parent-escape arms already precede the
8794        // duplicate arm; the lifted file-type arm joins that set).
8795        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
8796        let err = c.validate_code_paths().unwrap_err();
8797        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8798            panic!("expected CodePathNonLispExtension, got {err:?}");
8799        };
8800        assert_eq!(slot, ":bibliotecas");
8801        assert_eq!(path, PathBuf::from("lib/x.txt"));
8802    }
8803
8804    #[test]
8805    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
8806        // Diagnostic-shape pin (peer with
8807        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8808        // on the sandbox-shape arms and
8809        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
8810        // on the duplicate arm): the file-type-arm Display surfaces both
8811        // the offending `:slot` tag, the offending path verbatim, and the
8812        // expected `.lisp` extension named in the remediation text, so a
8813        // `feira lint` run can render the diagnostic without re-parsing.
8814        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
8815        let rendered = c.validate_code_paths().unwrap_err().to_string();
8816        assert!(
8817            rendered.contains(":bibliotecas"),
8818            "diagnostic must name the offending slot: {rendered}",
8819        );
8820        assert!(
8821            rendered.contains("lib/demo.rs"),
8822            "diagnostic must quote the offending path: {rendered}",
8823        );
8824        assert!(
8825            rendered.contains(".lisp"),
8826            "diagnostic must name the expected extension: {rendered}",
8827        );
8828    }
8829
8830    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
8831    //
8832    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
8833    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
8834    // contract. The peer caixa-helm / caixa-flux renderers consume each
8835    // `:servicos` entry through `serde_yaml::from_str` as a typed
8836    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
8837    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
8838    // axis `Path::extension` can't express on its own.
8839
8840    #[test]
8841    fn validate_code_paths_rejects_no_extension_servicos_entry() {
8842        // Canonical "I dragged the wrong file from the workspace tree"
8843        // footgun on the Servico axis. Without the gate the peer
8844        // caixa-helm / caixa-flux renderers hand the extensionless path
8845        // to `serde_yaml::from_str` and fail with a parser-shaped
8846        // diagnostic far from the source caixa.lisp, with no field
8847        // naming the offending `:servicos` entry.
8848        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
8849            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8850            let err = c.validate_code_paths().unwrap_err();
8851            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8852                panic!(
8853                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8854                     got {err:?}"
8855                );
8856            };
8857            assert_eq!(slot, ":servicos");
8858            assert_eq!(path, PathBuf::from(relpath));
8859        }
8860    }
8861
8862    #[test]
8863    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
8864        // Wrong-extension sweep across common authoring footguns on the
8865        // Servico axis. Bare `.yaml` is the canonical "I forgot the
8866        // `.computeunit` segment" typo; the off-by-one-segment shapes
8867        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
8868        // bare `Path::extension` view but mismatch the typed compound
8869        // suffix the renderers' `serde_yaml::from_str` consumer demands.
8870        // Same sweep-posture as the peer
8871        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
8872        // (64772a9) on the sibling tatara-lisp-source axis.
8873        for relpath in [
8874            "servicos/demo.yaml",
8875            "servicos/demo.yml",
8876            "servicos/demo.json",
8877            "servicos/demo.toml",
8878            "servicos/demo.txt",
8879            "servicos/demo.computeunit.yaml.bak",
8880            "servicos/demo.computeunit.yam",
8881            "servicos/demo.computeunit",
8882            "servicos/demo-computeunit.yaml",
8883            "servicos/demo_computeunit.yaml",
8884        ] {
8885            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8886            let err = c.validate_code_paths().unwrap_err();
8887            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8888                panic!(
8889                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8890                     got {err:?}"
8891                );
8892            };
8893            assert_eq!(slot, ":servicos");
8894            assert_eq!(path, PathBuf::from(relpath));
8895        }
8896    }
8897
8898    #[test]
8899    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
8900        // Case-sensitivity sweep — pins the strict lowercase
8901        // `.computeunit.yaml` contract. A case-folded shape that the
8902        // layout's existence check would (case-insensitively, on
8903        // case-insensitive volumes) match the on-disk file still
8904        // mismatches the canonical form the codec emits, breaking the
8905        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
8906        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
8907        // (64772a9) sweep on the sibling tatara-lisp-source axis.
8908        for relpath in [
8909            "servicos/demo.ComputeUnit.yaml",
8910            "servicos/demo.COMPUTEUNIT.yaml",
8911            "servicos/demo.computeunit.YAML",
8912            "servicos/demo.computeunit.Yaml",
8913            "servicos/demo.COMPUTEUNIT.YAML",
8914        ] {
8915            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8916            let err = c.validate_code_paths().unwrap_err();
8917            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8918                panic!(
8919                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8920                     got {err:?}"
8921                );
8922            };
8923            assert_eq!(slot, ":servicos");
8924            assert_eq!(path, PathBuf::from(relpath));
8925        }
8926    }
8927
8928    #[test]
8929    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
8930        // Degenerate hidden-file shape: a file name exactly equal to the
8931        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
8932        // the structural "Servico declared with no identity" footgun.
8933        // The substrate identifies each ComputeUnit by the file-stem
8934        // segment that precedes `.computeunit.yaml` (the rendered
8935        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
8936        // the M3 `:contratos` membership lookup), so an empty stem
8937        // leaves the Servico unidentifiable. Pinned at the typed-axis
8938        // level so a future regression that drops the `name.len() >
8939        // SUFFIX.len()` bound at the predicate surfaces here, not
8940        // piecemeal as a `lareira-` chart-name collision at render time.
8941        for relpath in ["servicos/.computeunit.yaml"] {
8942            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8943            let err = c.validate_code_paths().unwrap_err();
8944            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8945                panic!(
8946                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8947                     got {err:?}"
8948                );
8949            };
8950            assert_eq!(slot, ":servicos");
8951            assert_eq!(path, PathBuf::from(relpath));
8952        }
8953    }
8954
8955    #[test]
8956    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
8957        // Positive-control sweep through every canonical authoring shape
8958        // every in-tree fixture and the `Caixa::template` scaffold use.
8959        // Mirrors the peer
8960        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
8961        // and the lifted predicate's own
8962        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
8963        // render.rs.
8964        for relpath in [
8965            "servicos/demo.computeunit.yaml",
8966            "servicos/hello-rio.computeunit.yaml",
8967            "servicos/my-service.computeunit.yaml",
8968            "servicos/a.computeunit.yaml",
8969            "./servicos/demo.computeunit.yaml",
8970            "servicos/./demo.computeunit.yaml",
8971            "servicos/sub/nested.computeunit.yaml",
8972            "servicos/v0.1.computeunit.yaml",
8973        ] {
8974            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8975            c.validate_code_paths()
8976                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8977        }
8978    }
8979
8980    #[test]
8981    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
8982        // The file-type gate is per-slot — only `:servicos` carries the
8983        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
8984        // entry and an extensionless `:exe` entry are the canonical
8985        // shapes every in-tree fixture uses, and must continue to pass
8986        // validate. Peer of
8987        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
8988        // (64772a9) — together pin that the typed
8989        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
8990        // cross-axis leakage in either direction.
8991        let c = caixa_with_code_paths(
8992            vec!["lib/demo.lisp"],
8993            vec!["exe/demo", "exe/tool"],
8994            vec!["servicos/demo.computeunit.yaml"],
8995        );
8996        c.validate_code_paths().unwrap();
8997    }
8998
8999    #[test]
9000    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
9001        // Cross-arm precedence pin: a `:servicos` entry that is *both*
9002        // sandbox-escaping and wrong-extension surfaces the more
9003        // fundamental sandbox-shape diagnostic first (the
9004        // `.computeunit.yaml` remediation would be misleading when the
9005        // offending path can never resolve under the caixa root
9006        // anyway). Mirrors the peer
9007        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
9008        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
9009        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
9010        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
9011        // table establishes.
9012        //
9013        // Empty wins (the strictly-smaller-scope structural arm).
9014        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
9015        assert!(
9016            matches!(
9017                c.validate_code_paths().unwrap_err(),
9018                ManifestError::CodePathEmpty { slot: ":servicos" }
9019            ),
9020            "empty must win over non-computeunit-yaml-extension",
9021        );
9022        // Absolute wins (the path can't resolve under the caixa root).
9023        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
9024        let err = c.validate_code_paths().unwrap_err();
9025        let ManifestError::CodePathAbsolute { slot, .. } = err else {
9026            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
9027        };
9028        assert_eq!(slot, ":servicos");
9029        // ParentEscape wins (the path escapes the caixa root).
9030        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
9031        let err = c.validate_code_paths().unwrap_err();
9032        let ManifestError::CodePathParentEscape { slot, .. } = err else {
9033            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
9034        };
9035        assert_eq!(slot, ":servicos");
9036    }
9037
9038    #[test]
9039    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
9040        // Within-slot precedence pin: the per-entry file-type shape gate
9041        // fires before the cross-entry duplicate gate, so the narrower
9042        // structural defect dominates the uniqueness diagnostic. A
9043        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
9044        // `CodePathNonComputeUnitYamlExtension` on the first entry
9045        // rather than `CodePathDuplicate` on the pair — same posture
9046        // every per-entry shape-gate-precedes-duplicate cascade follows
9047        // on this surface, peer of the 64772a9 `:bibliotecas`
9048        // `("lib/x.txt" "lib/x.txt")` ordering.
9049        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
9050        let err = c.validate_code_paths().unwrap_err();
9051        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9052            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
9053        };
9054        assert_eq!(slot, ":servicos");
9055        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
9056    }
9057
9058    #[test]
9059    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
9060     {
9061        // Diagnostic-shape pin (peer with
9062        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
9063        // on the sibling tatara-lisp-source axis): the file-type-arm
9064        // Display surfaces both the offending `:slot` tag, the
9065        // offending path verbatim, and the expected
9066        // `.computeunit.yaml` compound suffix named in the remediation
9067        // text, so a `feira lint` run can render the diagnostic without
9068        // re-parsing.
9069        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
9070        let rendered = c.validate_code_paths().unwrap_err().to_string();
9071        assert!(
9072            rendered.contains(":servicos"),
9073            "diagnostic must name the offending slot: {rendered}",
9074        );
9075        assert!(
9076            rendered.contains("servicos/demo.yaml"),
9077            "diagnostic must quote the offending path: {rendered}",
9078        );
9079        assert!(
9080            rendered.contains(".computeunit.yaml"),
9081            "diagnostic must name the expected compound suffix: {rendered}",
9082        );
9083    }
9084
9085    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
9086
9087    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
9088        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9089        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
9090        c
9091    }
9092
9093    #[test]
9094    fn validate_etiquetas_accepts_empty_list() {
9095        // The empty-list identity: every caixa with no declared tags
9096        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
9097        // so the gate is non-disruptive against every existing manifest.
9098        let c = caixa_with_etiquetas(vec![]);
9099        c.validate_etiquetas().unwrap();
9100    }
9101
9102    #[test]
9103    fn validate_etiquetas_accepts_canonical_forms() {
9104        // Positive control sweep: a canonical-shaped non-empty distinct
9105        // tag list passes, mirroring the example checkout-aplicacao
9106        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
9107        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
9108        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
9109        c.validate_etiquetas().unwrap();
9110    }
9111
9112    #[test]
9113    fn validate_etiquetas_rejects_empty_entry() {
9114        // Canonical paste-from-blank-doc footgun. Without the gate the
9115        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
9116        // no-op tag indexing nothing in the future caixa-registry.
9117        let c = caixa_with_etiquetas(vec![""]);
9118        let err = c.validate_etiquetas().unwrap_err();
9119        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
9120    }
9121
9122    #[test]
9123    fn validate_etiquetas_rejects_duplicate_entry() {
9124        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
9125        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
9126        // collect at chart render — a "second wins / one silently
9127        // disappears" shape divergent from every peer typed-graph set
9128        // gate. The duplicate-arm names the offending tag verbatim.
9129        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
9130        let err = c.validate_etiquetas().unwrap_err();
9131        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
9132            panic!("expected EtiquetaDuplicate, got {err:?}");
9133        };
9134        assert_eq!(etiqueta, "demo");
9135    }
9136
9137    #[test]
9138    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
9139        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
9140        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
9141        // structural "this entry has no value" defect dominates the
9142        // cross-entry uniqueness diagnostic. Mirrors the peer
9143        // empty-before-duplicate cascades on `:caracteristicas`
9144        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
9145        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
9146        // `MembroDuplicate`).
9147        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
9148        let err = c.validate_etiquetas().unwrap_err();
9149        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
9150    }
9151
9152    #[test]
9153    fn validate_etiquetas_duplicate_reports_first_collision() {
9154        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
9155        // duplicate (the lexicographically-earliest offending position
9156        // — the second `"a"` at index 2 collides with the first `"a"`
9157        // at index 0), not the later `"b"` collision at index 3,
9158        // peer with every other first-collision diagnostic posture on
9159        // this surface (`validate_load_singularity_reports_first_collision`,
9160        // `validate_cleanup_singularity_reports_first_collision`).
9161        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
9162        let err = c.validate_etiquetas().unwrap_err();
9163        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
9164            panic!("expected EtiquetaDuplicate, got {err:?}");
9165        };
9166        assert_eq!(etiqueta, "a");
9167    }
9168
9169    #[test]
9170    fn validate_etiquetas_case_sensitive() {
9171        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
9172        // mirroring the peer `:membros :caixa` / `:children :caixa`
9173        // exact-string-match discipline. The shape gate this routine
9174        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
9175        // grammar) accepts mixed case — crates.io's keyword rule is
9176        // "case-insensitive" at the index layer but admits mixed case
9177        // at the entry layer (the canonical Helm chart `keywords:`
9178        // shape is lowercase by convention, but the grammar admits
9179        // uppercase). Case-sensitivity at the duplicate-set layer
9180        // remains structural — two distinct strings are two distinct
9181        // entries.
9182        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
9183        c.validate_etiquetas().unwrap();
9184    }
9185
9186    #[test]
9187    fn validate_etiquetas_diagnostic_carries_offending_tag() {
9188        // Diagnostic-shape pin (peer with
9189        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
9190        // the error's Display surfaces the offending tag verbatim, so a
9191        // `feira lint` run can render the diagnostic without re-parsing
9192        // and the author can grep their caixa.lisp for the offending
9193        // value.
9194        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
9195        let rendered = c.validate_etiquetas().unwrap_err().to_string();
9196        assert!(
9197            rendered.contains(":etiquetas"),
9198            "diagnostic must name the offending slot: {rendered}",
9199        );
9200        assert!(
9201            rendered.contains("demo"),
9202            "diagnostic must quote the offending tag: {rendered}",
9203        );
9204    }
9205
9206    #[test]
9207    fn validate_etiquetas_rejects_leading_whitespace_entry() {
9208        // Canonical paste-from-aligned-doc footgun. Without the shape
9209        // gate `" mesh"` silently passed validate and landed as a
9210        // YAML plain-style scalar with leading whitespace in the
9211        // rendered Chart.yaml `keywords:` array — every YAML 1.2
9212        // dumper trims leading whitespace from plain-style scalars,
9213        // so the authored space round-tripped inconsistently back
9214        // through `caixa.lisp`. Mirrors the peer
9215        // `validate_autores_rejects_leading_whitespace_entry`.
9216        let c = caixa_with_etiquetas(vec![" mesh"]);
9217        let err = c.validate_etiquetas().unwrap_err();
9218        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9219            panic!("expected EtiquetaInvalid, got {err:?}");
9220        };
9221        assert_eq!(etiqueta, " mesh");
9222        assert!(reason.contains("whitespace"), "got: {reason}");
9223    }
9224
9225    #[test]
9226    fn validate_etiquetas_rejects_embedded_newline_entry() {
9227        // Canonical paste-from-multiline-doc footgun — the author
9228        // pasted a multi-tag block into one `:etiquetas` entry
9229        // instead of splitting into one entry per tag. Without the
9230        // shape gate `"mesh\nhttp"` silently passed validate and
9231        // landed as a YAML-illegal multi-line scalar in the rendered
9232        // Chart.yaml `keywords:` array.
9233        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
9234        let err = c.validate_etiquetas().unwrap_err();
9235        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9236            panic!("expected EtiquetaInvalid, got {err:?}");
9237        };
9238        assert_eq!(etiqueta, "mesh\nhttp");
9239        assert!(reason.contains("newline"), "got: {reason}");
9240    }
9241
9242    #[test]
9243    fn validate_etiquetas_rejects_embedded_comma_entry() {
9244        // Canonical CSV-list-separator-confusion footgun: the author
9245        // confused the CSV-style separator convention with the
9246        // `:etiquetas` list grammar. Without the shape gate
9247        // `"mesh,http,grpc"` silently passed validate and landed as a
9248        // single malformed search tag in the rendered Chart.yaml
9249        // `keywords:` array — Artifact Hub's keyword index would
9250        // either silently drop the tag or index it as
9251        // `mesh,http,grpc` instead of three separate tags.
9252        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
9253        let err = c.validate_etiquetas().unwrap_err();
9254        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9255            panic!("expected EtiquetaInvalid, got {err:?}");
9256        };
9257        assert_eq!(etiqueta, "mesh,http,grpc");
9258        assert!(reason.contains('`'), "got: {reason}");
9259        assert!(reason.contains(','), "got: {reason}");
9260    }
9261
9262    #[test]
9263    fn validate_etiquetas_rejects_embedded_slash_entry() {
9264        // Canonical path-separator-confusion footgun: the author
9265        // confused namespace-path notation with the keyword grammar.
9266        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
9267        let err = c.validate_etiquetas().unwrap_err();
9268        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9269            panic!("expected EtiquetaInvalid, got {err:?}");
9270        };
9271        assert_eq!(etiqueta, "caixa/servico");
9272        assert!(reason.contains('/'), "got: {reason}");
9273    }
9274
9275    #[test]
9276    fn validate_etiquetas_rejects_leading_digit_entry() {
9277        // Canonical paste-from-numbered-list footgun: the author
9278        // copied `1. mesh` from a numbered doc and the `1` leaked
9279        // into the tag.
9280        let c = caixa_with_etiquetas(vec!["1mesh"]);
9281        let err = c.validate_etiquetas().unwrap_err();
9282        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9283            panic!("expected EtiquetaInvalid, got {err:?}");
9284        };
9285        assert_eq!(etiqueta, "1mesh");
9286        assert!(reason.contains("digit"), "got: {reason}");
9287    }
9288
9289    #[test]
9290    fn validate_etiquetas_rejects_leading_hyphen_entry() {
9291        // Canonical kebab-leak footgun.
9292        let c = caixa_with_etiquetas(vec!["-foo"]);
9293        let err = c.validate_etiquetas().unwrap_err();
9294        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9295            panic!("expected EtiquetaInvalid, got {err:?}");
9296        };
9297        assert_eq!(etiqueta, "-foo");
9298        assert!(reason.contains('-'), "got: {reason}");
9299    }
9300
9301    #[test]
9302    fn validate_etiquetas_rejects_non_ascii_entry() {
9303        // Canonical paste-from-Unicode-doc footgun. Every legitimate
9304        // search tag is strict ASCII; raw non-ASCII silently
9305        // round-trips inconsistently across NFC/NFD normalization on
9306        // APFS / case-folding filesystems and breaks the Artifact Hub
9307        // keyword search index lookup.
9308        let c = caixa_with_etiquetas(vec!["café"]);
9309        let err = c.validate_etiquetas().unwrap_err();
9310        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9311            panic!("expected EtiquetaInvalid, got {err:?}");
9312        };
9313        assert_eq!(etiqueta, "café");
9314        assert!(reason.contains("non-ASCII"), "got: {reason}");
9315    }
9316
9317    #[test]
9318    fn validate_etiquetas_rejects_period_entry() {
9319        // Canonical namespace-confusion / version-suffix footgun
9320        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
9321        // excludes `.` from the continuation set even though the
9322        // sibling `:caracteristicas` axis (Cargo's feature-name
9323        // grammar) admits it. Tighter than the sibling axis, peer
9324        // with Cargo's own crates.io keyword shape.
9325        let c = caixa_with_etiquetas(vec!["http.1"]);
9326        let err = c.validate_etiquetas().unwrap_err();
9327        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9328            panic!("expected EtiquetaInvalid, got {err:?}");
9329        };
9330        assert_eq!(etiqueta, "http.1");
9331        assert!(reason.contains('.'), "got: {reason}");
9332    }
9333
9334    #[test]
9335    fn validate_etiquetas_empty_takes_precedence_over_shape() {
9336        // Per-entry empty-first cascade pin: an entry that is both
9337        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
9338        // narrower "this entry has no value" structural defect
9339        // dominates the broader shape-predicate diagnostic). The
9340        // empty arm fires before the shape predicate is consulted,
9341        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
9342        // cascade established on the sibling universal-axis Vec<String>
9343        // surface.
9344        let c = caixa_with_etiquetas(vec![""]);
9345        let err = c.validate_etiquetas().unwrap_err();
9346        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
9347    }
9348
9349    #[test]
9350    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
9351        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
9352        // entry that is malformed surfaces `EtiquetaInvalid` even when
9353        // a later entry would have collided on duplicate. The
9354        // per-entry shape arm fires inside the same loop iteration as
9355        // the empty arm, before the seen-set insert at end-of-iteration
9356        // — structural per-entry defects dominate the cross-entry
9357        // uniqueness diagnostic. Mirrors the peer
9358        // `validate_autores_shape_takes_precedence_over_duplicate`.
9359        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
9360        let err = c.validate_etiquetas().unwrap_err();
9361        assert!(
9362            matches!(err, ManifestError::EtiquetaInvalid { .. }),
9363            "got {err:?}",
9364        );
9365    }
9366
9367    #[test]
9368    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
9369        // Diagnostic-shape pin on the new shape arm (peer with
9370        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
9371        // the rendered Display surfaces both the offending slot name
9372        // and the offending value verbatim, so a `feira lint` run
9373        // points the author at the exact `:etiquetas` entry to fix.
9374        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
9375        let rendered = c.validate_etiquetas().unwrap_err().to_string();
9376        assert!(
9377            rendered.contains(":etiquetas"),
9378            "diagnostic must name the offending slot: {rendered}",
9379        );
9380        assert!(
9381            rendered.contains("mesh\\nhttp"),
9382            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9383        );
9384    }
9385
9386    #[test]
9387    fn validate_etiquetas_rejects_at_21_byte_boundary() {
9388        // The 20-byte cap pin — boundary-exceeding case rejected,
9389        // boundary-accepting case passes. Mirrors the peer
9390        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
9391        // side pin, surfaced at the per-axis caller so the cap
9392        // propagates through validate end-to-end. Constructed as a
9393        // single all-`a` token so only the cap arm fires.
9394        let max_ok = "a".repeat(20);
9395        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
9396        c.validate_etiquetas().unwrap();
9397        let too_long = "a".repeat(21);
9398        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
9399        let err = c.validate_etiquetas().unwrap_err();
9400        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
9401            panic!("expected EtiquetaInvalid, got {err:?}");
9402        };
9403        assert!(reason.contains("20"), "got: {reason}");
9404        assert!(reason.contains("21"), "got: {reason}");
9405    }
9406
9407    #[test]
9408    fn validate_etiquetas_accepts_canonical_shaped_forms() {
9409        // Positive control sweep: every canonical-shaped tag from the
9410        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
9411        // example fixtures plus the substrate-fixed tags caixa-helm
9412        // unions in at chart render. Drift between this list and the
9413        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
9414        // sweep surfaces here — one source of truth for the rule.
9415        let c = caixa_with_etiquetas(vec![
9416            "example",
9417            "aplicacao",
9418            "mesh",
9419            "ecommerce",
9420            "demo",
9421            "infrastructure",
9422            "aws",
9423            "akeyless",
9424            "pangea-native",
9425            "hello-world",
9426            "wasm",
9427            "rust",
9428            "tatara-lisp",
9429            "caixa-servico",
9430            "lareira",
9431        ]);
9432        c.validate_etiquetas().unwrap();
9433    }
9434
9435    // ── validate_autores — universal-axis maintainer shape ────────────
9436
9437    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
9438        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9439        c.autores = autores.into_iter().map(String::from).collect();
9440        c
9441    }
9442
9443    #[test]
9444    fn validate_autores_accepts_empty_list() {
9445        // The empty-list identity: `Caixa::template` emits `:autores ()`,
9446        // so the gate is non-disruptive against every existing manifest.
9447        let c = caixa_with_autores(vec![]);
9448        c.validate_autores().unwrap();
9449    }
9450
9451    #[test]
9452    fn validate_autores_accepts_canonical_forms() {
9453        // Positive control sweep: every canonical-shaped non-empty
9454        // distinct maintainer list passes — the hello-rio / checkout-
9455        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
9456        // multi-author shape downstream packaging surfaces emit.
9457        let c = caixa_with_autores(vec!["pleme-io"]);
9458        c.validate_autores().unwrap();
9459        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
9460        c.validate_autores().unwrap();
9461    }
9462
9463    #[test]
9464    fn validate_autores_rejects_empty_entry() {
9465        // Canonical paste-from-blank-doc footgun. Without the gate the
9466        // empty entry rendered as `maintainers: [{name: "", email: null}]`
9467        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
9468        // to.
9469        let c = caixa_with_autores(vec![""]);
9470        let err = c.validate_autores().unwrap_err();
9471        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9472    }
9473
9474    #[test]
9475    fn validate_autores_rejects_duplicate_entry() {
9476        // Canonical copy-paste-the-wrong-author footgun. Unlike the
9477        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
9478        // dedups the rendered `keywords:` array), the `maintainers:`
9479        // rendering has *no* dedup — duplicates stack verbatim. The
9480        // duplicate-arm names the offending author verbatim.
9481        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
9482        let err = c.validate_autores().unwrap_err();
9483        let ManifestError::AutorDuplicate { autor } = err else {
9484            panic!("expected AutorDuplicate, got {err:?}");
9485        };
9486        assert_eq!(autor, "pleme-io");
9487    }
9488
9489    #[test]
9490    fn validate_autores_empty_takes_precedence_over_duplicate() {
9491        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
9492        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
9493        // "this entry has no value" defect dominates the cross-entry
9494        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
9495        // cascades on `:etiquetas` (`EtiquetaEmpty` before
9496        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
9497        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
9498        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
9499        // `MembroDuplicate`).
9500        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
9501        let err = c.validate_autores().unwrap_err();
9502        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9503    }
9504
9505    #[test]
9506    fn validate_autores_duplicate_reports_first_collision() {
9507        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
9508        // duplicate (the lexicographically-earliest offending position
9509        // — the second `"a"` at index 2 collides with the first `"a"`
9510        // at index 0), not the later `"b"` collision at index 3,
9511        // peer with every other first-collision diagnostic posture on
9512        // this surface.
9513        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
9514        let err = c.validate_autores().unwrap_err();
9515        let ManifestError::AutorDuplicate { autor } = err else {
9516            panic!("expected AutorDuplicate, got {err:?}");
9517        };
9518        assert_eq!(autor, "a");
9519    }
9520
9521    #[test]
9522    fn validate_autores_case_sensitive() {
9523        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
9524        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
9525        // / `:children :caixa` exact-string-match discipline.
9526        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
9527        c.validate_autores().unwrap();
9528    }
9529
9530    #[test]
9531    fn validate_autores_diagnostic_carries_offending_author() {
9532        // Diagnostic-shape pin (peer with
9533        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
9534        // error's Display surfaces the offending author verbatim, so a
9535        // `feira lint` run can render the diagnostic without re-parsing
9536        // and the author can grep their caixa.lisp for the offending
9537        // value.
9538        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
9539        let rendered = c.validate_autores().unwrap_err().to_string();
9540        assert!(
9541            rendered.contains(":autores"),
9542            "diagnostic must name the offending slot: {rendered}",
9543        );
9544        assert!(
9545            rendered.contains("pleme-io"),
9546            "diagnostic must quote the offending author: {rendered}",
9547        );
9548    }
9549
9550    #[test]
9551    fn validate_autores_rejects_leading_whitespace_entry() {
9552        // Canonical paste-from-aligned-doc footgun. Without the shape
9553        // gate `" pleme-io"` silently passed validate and landed as a
9554        // YAML plain-style scalar with leading whitespace in the
9555        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
9556        // dumper trims leading whitespace from plain-style scalars, so
9557        // the authored space round-tripped inconsistently back through
9558        // `caixa.lisp`. Mirrors the peer
9559        // `validate_descricao_rejects_leading_whitespace`.
9560        let c = caixa_with_autores(vec![" pleme-io"]);
9561        let err = c.validate_autores().unwrap_err();
9562        let ManifestError::AutorInvalid { autor, reason } = err else {
9563            panic!("expected AutorInvalid, got {err:?}");
9564        };
9565        assert_eq!(autor, " pleme-io");
9566        assert!(reason.contains("whitespace"), "got: {reason}");
9567    }
9568
9569    #[test]
9570    fn validate_autores_rejects_trailing_whitespace_entry() {
9571        // Canonical paste-from-doc footgun.
9572        let c = caixa_with_autores(vec!["pleme-io "]);
9573        let err = c.validate_autores().unwrap_err();
9574        let ManifestError::AutorInvalid { autor, reason } = err else {
9575            panic!("expected AutorInvalid, got {err:?}");
9576        };
9577        assert_eq!(autor, "pleme-io ");
9578        assert!(reason.contains("whitespace"), "got: {reason}");
9579    }
9580
9581    #[test]
9582    fn validate_autores_rejects_embedded_newline_entry() {
9583        // Canonical paste-from-multiline-doc footgun — the author
9584        // pasted a multi-line block of author records into one
9585        // `:autores` entry instead of splitting into one entry per
9586        // author. Without the shape gate `"alice\nbob"` silently
9587        // passed validate and landed as a YAML-illegal multi-line
9588        // scalar in the rendered Chart.yaml `maintainers:` array.
9589        let c = caixa_with_autores(vec!["alice\nbob"]);
9590        let err = c.validate_autores().unwrap_err();
9591        let ManifestError::AutorInvalid { autor, reason } = err else {
9592            panic!("expected AutorInvalid, got {err:?}");
9593        };
9594        assert_eq!(autor, "alice\nbob");
9595        assert!(reason.contains("newline"), "got: {reason}");
9596    }
9597
9598    #[test]
9599    fn validate_autores_rejects_embedded_carriage_return_entry() {
9600        // Canonical paste-from-Windows-CRLF-doc footgun.
9601        let c = caixa_with_autores(vec!["alice\rbob"]);
9602        let err = c.validate_autores().unwrap_err();
9603        let ManifestError::AutorInvalid { autor, reason } = err else {
9604            panic!("expected AutorInvalid, got {err:?}");
9605        };
9606        assert_eq!(autor, "alice\rbob");
9607        assert!(reason.contains("carriage return"), "got: {reason}");
9608    }
9609
9610    #[test]
9611    fn validate_autores_rejects_embedded_tab_entry() {
9612        // Canonical tab-from-aligned-doc footgun.
9613        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
9614        let err = c.validate_autores().unwrap_err();
9615        let ManifestError::AutorInvalid { autor, reason } = err else {
9616            panic!("expected AutorInvalid, got {err:?}");
9617        };
9618        assert_eq!(autor, "Pleme\tContributors");
9619        assert!(reason.contains("tab"), "got: {reason}");
9620    }
9621
9622    #[test]
9623    fn validate_autores_rejects_embedded_control_bytes_entry() {
9624        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
9625        // surface the same control-byte arm.
9626        for entry in [
9627            "alice\x00bob",
9628            "alice\x07bob",
9629            "alice\x1bbob",
9630            "alice\x7fbob",
9631        ] {
9632            let c = caixa_with_autores(vec![entry]);
9633            let err = c.validate_autores().unwrap_err();
9634            let ManifestError::AutorInvalid { autor, reason } = err else {
9635                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
9636            };
9637            assert_eq!(autor, entry);
9638            assert!(
9639                reason.contains("control character"),
9640                "{entry:?} reason: {reason}",
9641            );
9642        }
9643    }
9644
9645    #[test]
9646    fn validate_autores_accepts_unicode_entry() {
9647        // Unicode positive control: realistic maintainer names carry
9648        // Unicode (`François`, `日本語`, `naïve`). The predicate must
9649        // round-trip Unicode losslessly, peer with the
9650        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
9651        // sweep.
9652        let c = caixa_with_autores(vec![
9653            "François Dupont",
9654            "日本語の名前",
9655            "naïve <naive@example.com>",
9656        ]);
9657        c.validate_autores().unwrap();
9658    }
9659
9660    #[test]
9661    fn validate_autores_empty_takes_precedence_over_shape() {
9662        // Per-entry empty-first cascade pin: an entry that is both
9663        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
9664        // "this entry has no value" structural defect dominates the
9665        // broader shape-predicate diagnostic). The empty arm fires
9666        // before the shape predicate is consulted, mirroring the peer
9667        // `validate_repositorio_empty_takes_precedence_over_shape`
9668        // cascade on the universal `Option<String>` siblings — and now
9669        // established on the Vec<String> per-entry surface.
9670        let c = caixa_with_autores(vec![""]);
9671        let err = c.validate_autores().unwrap_err();
9672        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9673    }
9674
9675    #[test]
9676    fn validate_autores_shape_takes_precedence_over_duplicate() {
9677        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
9678        // entry that is malformed surfaces `AutorInvalid` even when a
9679        // later entry would have collided on duplicate. The per-entry
9680        // shape arm fires inside the same loop iteration as the empty
9681        // arm, before the seen-set insert at end-of-iteration —
9682        // structural per-entry defects dominate the cross-entry
9683        // uniqueness diagnostic.
9684        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
9685        let err = c.validate_autores().unwrap_err();
9686        assert!(
9687            matches!(err, ManifestError::AutorInvalid { .. }),
9688            "got {err:?}",
9689        );
9690    }
9691
9692    #[test]
9693    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
9694        // Diagnostic-shape pin on the new shape arm (peer with
9695        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
9696        // the rendered Display surfaces both the offending slot name
9697        // and the offending value verbatim, so a `feira lint` run
9698        // points the author at the exact `:autores` entry to fix.
9699        let c = caixa_with_autores(vec!["alice\nbob"]);
9700        let rendered = c.validate_autores().unwrap_err().to_string();
9701        assert!(
9702            rendered.contains(":autores"),
9703            "diagnostic must name the offending slot: {rendered}",
9704        );
9705        assert!(
9706            rendered.contains("alice\\nbob"),
9707            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9708        );
9709    }
9710
9711    #[test]
9712    fn validate_autores_rejects_at_129_byte_boundary() {
9713        // The 128-byte cap pin — boundary-exceeding case rejected,
9714        // boundary-accepting case passes. Mirrors the peer
9715        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
9716        // substrate-side pin, surfaced at the per-axis caller so the
9717        // cap propagates through validate end-to-end. Constructed as
9718        // a single all-`a` token so only the cap arm fires.
9719        let max_ok = "a".repeat(128);
9720        let c = caixa_with_autores(vec![max_ok.as_str()]);
9721        c.validate_autores().unwrap();
9722        let too_long = "a".repeat(129);
9723        let c = caixa_with_autores(vec![too_long.as_str()]);
9724        let err = c.validate_autores().unwrap_err();
9725        let ManifestError::AutorInvalid { reason, .. } = err else {
9726            panic!("expected AutorInvalid, got {err:?}");
9727        };
9728        assert!(reason.contains("128"), "got: {reason}");
9729        assert!(reason.contains("129"), "got: {reason}");
9730    }
9731
9732    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
9733
9734    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
9735        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9736        c.repositorio = repositorio.map(String::from);
9737        c
9738    }
9739
9740    #[test]
9741    fn validate_repositorio_accepts_none() {
9742        // The omit-the-slot identity: `:repositorio` is optional. The
9743        // gate is a no-op when the author didn't declare a value —
9744        // every caixa without a `:repositorio` line trivially passes,
9745        // and the substrate-side renderers fall back to their
9746        // documented placeholder (`caixa-helm`'s `home: None`,
9747        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
9748        // URL). Mirrors the peer `validate_restart_window_accepts_none`
9749        // posture on the other `Option<String>` Caixa slot.
9750        let c = caixa_with_repositorio(None);
9751        c.validate_repositorio().unwrap();
9752    }
9753
9754    #[test]
9755    fn validate_repositorio_accepts_canonical_forms() {
9756        // Positive control sweep across every documented `:repositorio`
9757        // authoring shape — the same union the shared
9758        // `crate::render::is_git_repo_url` predicate accepts and the
9759        // peer `:deps :fonte :repo` axis already routes through.
9760        // Covers the `github:` shorthand (the canonical pleme-io
9761        // convention used in the `:repositorio` field of every
9762        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
9763        // `examples/`), the `https://…` URL the README quickstart uses,
9764        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
9765        // `file://` URL schemes the shared predicate documents.
9766        for repo in [
9767            "github:pleme-io/hello-rio",
9768            "github:pleme-io/checkout",
9769            "https://github.com/pleme-io/hello-rio",
9770            "ssh://git@github.com/pleme-io/hello-rio.git",
9771            "git://github.com/pleme-io/hello-rio.git",
9772            "git@github.com:pleme-io/hello-rio.git",
9773            "file:///srv/pleme/hello-rio",
9774        ] {
9775            let c = caixa_with_repositorio(Some(repo));
9776            c.validate_repositorio()
9777                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
9778        }
9779    }
9780
9781    #[test]
9782    fn validate_repositorio_rejects_empty_some() {
9783        // Canonical paste-from-blank-doc footgun. The narrower
9784        // [`ManifestError::RepositorioEmpty`] arm fires before the
9785        // shape predicate is consulted, mirroring the empty-first
9786        // cascade every peer per-axis identity gate uses
9787        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9788        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
9789        // the empty `Some("")` silently passed the renderer's
9790        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9791        // on `None`) and landed as `home: ""` in `Chart.yaml` /
9792        // `url: ""` in the FluxCD `GitRepository`.
9793        let c = caixa_with_repositorio(Some(""));
9794        let err = c.validate_repositorio().unwrap_err();
9795        assert!(
9796            matches!(err, ManifestError::RepositorioEmpty),
9797            "got {err:?}",
9798        );
9799    }
9800
9801    #[test]
9802    fn validate_repositorio_rejects_whitespace() {
9803        // Paste-from-doc whitespace footgun. The shared
9804        // `is_git_repo_url` predicate refuses any whitespace byte; a
9805        // trailing space in a `:repositorio` value silently broke
9806        // `git clone '<value> '` at clone time. The diagnostic names
9807        // the offending value verbatim.
9808        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
9809        let err = c.validate_repositorio().unwrap_err();
9810        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
9811            panic!("expected RepositorioInvalid, got {err:?}");
9812        };
9813        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
9814    }
9815
9816    #[test]
9817    fn validate_repositorio_rejects_control_char() {
9818        // Paste-from-multiline-doc CRLF footgun — control characters
9819        // at the URL boundary are a class of subprocess-arg injection
9820        // and break git's URL parser at every porcelain entry point.
9821        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
9822        let err = c.validate_repositorio().unwrap_err();
9823        assert!(
9824            matches!(err, ManifestError::RepositorioInvalid { .. }),
9825            "got {err:?}",
9826        );
9827    }
9828
9829    #[test]
9830    fn validate_repositorio_rejects_leading_dash() {
9831        // Canonical CLI-argument-injection footgun: `git clone <repo>`
9832        // interprets a leading `-` as a CLI flag, so a
9833        // `-upload-pack=…` value escapes the subprocess argument
9834        // boundary. The shared predicate refuses every leading-`-`
9835        // shape at validate time.
9836        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
9837        let err = c.validate_repositorio().unwrap_err();
9838        assert!(
9839            matches!(err, ManifestError::RepositorioInvalid { .. }),
9840            "got {err:?}",
9841        );
9842    }
9843
9844    #[test]
9845    fn validate_repositorio_rejects_missing_colon_separator() {
9846        // The bare `org/repo` ambiguity footgun — `git clone` reads
9847        // a no-`:` form as a relative filesystem path rather than the
9848        // GitHub-shorthand expansion the author probably intended.
9849        // The shared predicate refuses every shape without a `:`
9850        // separator.
9851        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9852        let err = c.validate_repositorio().unwrap_err();
9853        assert!(
9854            matches!(err, ManifestError::RepositorioInvalid { .. }),
9855            "got {err:?}",
9856        );
9857    }
9858
9859    #[test]
9860    fn validate_repositorio_rejects_fragment_anchor() {
9861        // Paste-from-browser-address-bar footgun on the
9862        // `:repositorio` axis — an author copies a GitHub permalink
9863        // to a README section / line-permalink and forgets to trim
9864        // the `#fragment` tail. The shared `is_git_repo_url`
9865        // predicate refuses the byte at the URL-grammar layer
9866        // (libcurl strips the fragment before opening the
9867        // transport, so the byte rides verbatim into the rendered
9868        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
9869        // fields but is silently dropped on the wire — two
9870        // manifest variants whose values differ only in their
9871        // fragment anchor lock to two distinct rendered artifacts
9872        // for the byte-identical clone, defeating the THEORY.md
9873        // §V.2 render-determinism contract on the `:repositorio`
9874        // axis the peer `:fonte :repo` axis already closes).
9875        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
9876        let err = c.validate_repositorio().unwrap_err();
9877        let ManifestError::RepositorioInvalid {
9878            repositorio,
9879            reason,
9880        } = err
9881        else {
9882            panic!("expected RepositorioInvalid, got {err:?}");
9883        };
9884        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
9885        assert!(
9886            reason.contains("must not contain `#`"),
9887            "reason must surface the fragment-`#` arm, got {reason:?}"
9888        );
9889    }
9890
9891    #[test]
9892    fn validate_repositorio_rejects_query_string() {
9893        // Paste-from-browser-address-bar footgun on the
9894        // `:repositorio` axis (peer with the a68f818 fragment-`#`
9895        // arm on the same axis). An author copies a GitHub tab
9896        // deep-link out of the address bar and forgets to trim
9897        // the `?tab=…` query tail. The shared `is_git_repo_url`
9898        // predicate refuses the byte at the URL-grammar layer
9899        // (GitHub / GitLab / Bitbucket silently ignore the
9900        // `?query` tail and serve the same repo regardless, so
9901        // the byte rides verbatim into the rendered `Chart.yaml`
9902        // `home:` and FluxCD `GitRepository` `url:` fields but
9903        // is silently masked at the wire — two manifest variants
9904        // whose values differ only in their query tail lock to
9905        // two distinct rendered artifacts for the byte-identical
9906        // clone, defeating the THEORY.md §V.2 render-determinism
9907        // contract on the `:repositorio` axis the peer `:fonte
9908        // :repo` axis already closes).
9909        let c = caixa_with_repositorio(Some(
9910            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
9911        ));
9912        let err = c.validate_repositorio().unwrap_err();
9913        let ManifestError::RepositorioInvalid {
9914            repositorio,
9915            reason,
9916        } = err
9917        else {
9918            panic!("expected RepositorioInvalid, got {err:?}");
9919        };
9920        assert_eq!(
9921            repositorio,
9922            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
9923        );
9924        assert!(
9925            reason.contains("must not contain `?`"),
9926            "reason must surface the query-`?` arm, got {reason:?}"
9927        );
9928    }
9929
9930    #[test]
9931    fn validate_repositorio_rejects_embedded_backslash() {
9932        // Windows-file-path-confusion footgun on the `:repositorio`
9933        // axis (peer with the prior fragment-`#` / query-`?` arms on
9934        // the same axis, and peer with the new dep-level `:fonte :repo`
9935        // backslash arm on the URL-grammar trajectory). An author
9936        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
9937        // hello-rio` into the `:repositorio` slot, expecting the
9938        // `lareira-<nome>` chart's `home:` field and the FluxCD
9939        // `GitRepository` `url:` field to render the canonical local
9940        // file-URI. The shared `is_git_repo_url` predicate refuses
9941        // the byte at the URL-grammar layer (libcurl silently
9942        // translates `\` → `/` on some platforms and refuses it on
9943        // others, so the byte rides verbatim into the rendered
9944        // artifacts but is silently rewritten or rejected at the wire
9945        // — two manifest variants whose values differ only in
9946        // backslash-vs-forward-slash lock to two distinct rendered
9947        // artifacts for the byte-identical clone, defeating the
9948        // THEORY.md §V.2 render-determinism contract on the
9949        // `:repositorio` axis the peer `:fonte :repo` axis already
9950        // closes).
9951        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
9952        let err = c.validate_repositorio().unwrap_err();
9953        let ManifestError::RepositorioInvalid {
9954            repositorio,
9955            reason,
9956        } = err
9957        else {
9958            panic!("expected RepositorioInvalid, got {err:?}");
9959        };
9960        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
9961        assert!(
9962            reason.contains("must not contain `\\`"),
9963            "reason must surface the backslash-`\\` arm, got {reason:?}"
9964        );
9965    }
9966
9967    #[test]
9968    fn validate_repositorio_rejects_uri_template_placeholder() {
9969        // URI Template (RFC 6570) placeholder footgun on the
9970        // `:repositorio` axis (peer with the prior fragment-`#` /
9971        // query-`?` / backslash-`\` arms on the same axis, and peer
9972        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
9973        // URL-grammar trajectory). An author pastes a quick-start
9974        // README snippet / OpenAPI `servers:` URL / Helm chart
9975        // `home:` template carrying unresolved `{org}` / `{repo}`
9976        // placeholders into the `:repositorio` slot, expecting the
9977        // substrate to resolve the placeholder downstream. The
9978        // shared `is_git_repo_url` predicate refuses the byte at the
9979        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
9980        // `%7B` / `%7D` on the wire, so the byte round-trips
9981        // inconsistently between the rendered `Chart.yaml home:` /
9982        // FluxCD `GitRepository url:` and the resolver's `git clone`
9983        // invocation, defeating the THEORY.md §V.2 render-
9984        // determinism contract on the `:repositorio` axis the peer
9985        // `:fonte :repo` axis already closes; every git porcelain
9986        // entry-point additionally fetches a nonexistent literal-
9987        // `{placeholder}`-named path far from the source caixa.lisp).
9988        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
9989        let err = c.validate_repositorio().unwrap_err();
9990        let ManifestError::RepositorioInvalid {
9991            repositorio,
9992            reason,
9993        } = err
9994        else {
9995            panic!("expected RepositorioInvalid, got {err:?}");
9996        };
9997        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
9998        assert!(
9999            reason.contains("must not contain `{`"),
10000            "reason must surface the open-brace `{{` arm, got {reason:?}"
10001        );
10002        assert!(
10003            reason.contains("URI Template") || reason.contains("RFC 6570"),
10004            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
10005        );
10006    }
10007
10008    #[test]
10009    fn validate_repositorio_empty_takes_precedence_over_shape() {
10010        // Empty-first cascade pin: the empty `Some("")` surfaces the
10011        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
10012        // `RepositorioInvalid`, mirroring the peer
10013        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
10014        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
10015        // `is_git_repo_url` predicate also rejects the empty input
10016        // (defensively, with its own `"must not be empty"` reason),
10017        // but the manifest-layer empty arm runs first to surface the
10018        // narrower diagnostic verbatim.
10019        let c = caixa_with_repositorio(Some(""));
10020        let err = c.validate_repositorio().unwrap_err();
10021        assert!(
10022            matches!(err, ManifestError::RepositorioEmpty),
10023            "got {err:?}",
10024        );
10025    }
10026
10027    #[test]
10028    fn validate_repositorio_diagnostic_carries_offending_value() {
10029        // Diagnostic-shape pin (peer with
10030        // `validate_autores_diagnostic_carries_offending_author`): the
10031        // error's Display surfaces the offending value + slot name
10032        // verbatim, so a `feira lint` run can render the diagnostic
10033        // without re-parsing and the author can grep their caixa.lisp
10034        // for the offending `:repositorio` value.
10035        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
10036        let rendered = c.validate_repositorio().unwrap_err().to_string();
10037        assert!(
10038            rendered.contains(":repositorio"),
10039            "diagnostic must name the offending slot: {rendered}",
10040        );
10041        assert!(
10042            rendered.contains("pleme-io/hello-rio"),
10043            "diagnostic must quote the offending value: {rendered}",
10044        );
10045    }
10046
10047    // ── validate_descricao — universal-axis Chart.yaml description shape ──
10048
10049    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
10050        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10051        c.descricao = descricao.map(String::from);
10052        c
10053    }
10054
10055    #[test]
10056    fn validate_descricao_accepts_none() {
10057        // The omit-the-slot identity: `:descricao` is optional. The
10058        // gate is a no-op when the author didn't declare a value —
10059        // every caixa without a `:descricao` line trivially passes,
10060        // and the substrate-side renderers fall back to their
10061        // documented `caixa.nome`-derived placeholder. Mirrors the
10062        // peer `validate_repositorio_accepts_none` posture on the
10063        // sibling `Option<String>` Caixa slot.
10064        let c = caixa_with_descricao(None);
10065        c.validate_descricao().unwrap();
10066    }
10067
10068    #[test]
10069    fn validate_descricao_accepts_canonical_summary() {
10070        // Positive control: the canonical pleme-io descricao shape —
10071        // a short free-form prose summary — passes the gate. Covers
10072        // the fixture shapes the `caixa-helm` / `caixa-flux` /
10073        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
10074        // wasip2 caixa Servico."`, `"Checkout flow."`).
10075        for desc in [
10076            "Canonical Rust→wasm32-wasip2 caixa Servico.",
10077            "Checkout flow.",
10078            "AWS provider caixa for tatara-lisp",
10079            "FIXME — describe this caixa",
10080            "x",
10081        ] {
10082            let c = caixa_with_descricao(Some(desc));
10083            c.validate_descricao()
10084                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
10085        }
10086    }
10087
10088    #[test]
10089    fn validate_descricao_rejects_empty_some() {
10090        // Canonical paste-from-blank-doc footgun. Without this gate
10091        // the empty `Some("")` silently passed the renderer's
10092        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
10093        // on `None`) and landed as `description: ""` in `Chart.yaml`
10094        // and a blank `README.md` header. Mirrors the peer
10095        // [`ManifestError::RepositorioEmpty`] empty-arm on the
10096        // sibling `Option<String>` Caixa slot.
10097        let c = caixa_with_descricao(Some(""));
10098        let err = c.validate_descricao().unwrap_err();
10099        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
10100    }
10101
10102    #[test]
10103    fn validate_descricao_rejects_leading_whitespace() {
10104        // Paste-from-aligned-doc footgun: a leading ASCII space the
10105        // bare empty-arm gate accepted, the shape predicate now
10106        // refuses. The diagnostic carries the offending value
10107        // verbatim (with the leading space preserved) so the author
10108        // can grep their caixa.lisp for the exact `:descricao` line
10109        // and fix the round-trip-inconsistent leading whitespace.
10110        // Mirrors the peer
10111        // `validate_licenca_rejects_leading_whitespace` arm on the
10112        // sibling `:licenca` axis.
10113        let c = caixa_with_descricao(Some(" Checkout flow."));
10114        let err = c.validate_descricao().unwrap_err();
10115        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
10116            panic!("expected DescricaoInvalid, got {err:?}");
10117        };
10118        assert_eq!(descricao, " Checkout flow.");
10119        assert!(reason.contains("whitespace"), "got: {reason:?}");
10120    }
10121
10122    #[test]
10123    fn validate_descricao_rejects_trailing_whitespace() {
10124        // Paste-from-doc footgun: a trailing ASCII space the bare
10125        // empty-arm gate accepted, the shape predicate now refuses.
10126        let c = caixa_with_descricao(Some("Checkout flow. "));
10127        let err = c.validate_descricao().unwrap_err();
10128        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
10129            panic!("expected DescricaoInvalid, got {err:?}");
10130        };
10131        assert_eq!(descricao, "Checkout flow. ");
10132        assert!(reason.contains("whitespace"), "got: {reason:?}");
10133    }
10134
10135    #[test]
10136    fn validate_descricao_rejects_embedded_newline() {
10137        // Paste-from-multiline-doc footgun: an embedded LF the bare
10138        // empty-arm gate accepted, the shape predicate now refuses.
10139        // Without this gate the embedded newline silently landed in
10140        // the rendered Chart.yaml as a multi-line YAML block scalar,
10141        // and every chart-aware UI (`helm list`, `helm search`,
10142        // Artifact Hub) renders the description in a single-line
10143        // column so the embedded newline is silently dropped at
10144        // every downstream consumer.
10145        let c = caixa_with_descricao(Some("Checkout\nflow."));
10146        let err = c.validate_descricao().unwrap_err();
10147        assert!(
10148            matches!(err, ManifestError::DescricaoInvalid { .. }),
10149            "got {err:?}",
10150        );
10151        assert!(err.to_string().contains("newline"), "got {err}");
10152    }
10153
10154    #[test]
10155    fn validate_descricao_rejects_embedded_carriage_return() {
10156        // Paste-from-Windows-CRLF-doc footgun.
10157        let c = caixa_with_descricao(Some("Checkout\rflow."));
10158        let err = c.validate_descricao().unwrap_err();
10159        assert!(
10160            matches!(err, ManifestError::DescricaoInvalid { .. }),
10161            "got {err:?}",
10162        );
10163        assert!(err.to_string().contains("carriage return"), "got {err}");
10164    }
10165
10166    #[test]
10167    fn validate_descricao_rejects_embedded_tab() {
10168        // Tab-from-aligned-doc footgun.
10169        let c = caixa_with_descricao(Some("Checkout\tflow."));
10170        let err = c.validate_descricao().unwrap_err();
10171        assert!(
10172            matches!(err, ManifestError::DescricaoInvalid { .. }),
10173            "got {err:?}",
10174        );
10175        assert!(err.to_string().contains("tab"), "got {err}");
10176    }
10177
10178    #[test]
10179    fn validate_descricao_rejects_embedded_control_bytes() {
10180        // Paste-from-binary-blob footgun: every other control byte
10181        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
10182        // the peer SPDX-expression control-byte arm.
10183        for s in [
10184            "Checkout\x00flow.",
10185            "Checkout\x07flow.",
10186            "Checkout\x1bflow.",
10187            "Checkout\x7fflow.",
10188        ] {
10189            let c = caixa_with_descricao(Some(s));
10190            let err = c.validate_descricao().unwrap_err();
10191            assert!(
10192                matches!(err, ManifestError::DescricaoInvalid { .. }),
10193                "{s:?} got {err:?}",
10194            );
10195            assert!(
10196                err.to_string().contains("control character"),
10197                "{s:?} got {err}",
10198            );
10199        }
10200    }
10201
10202    #[test]
10203    fn validate_descricao_accepts_unicode_prose() {
10204        // Positive control: Unicode prose is accepted — the
10205        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
10206        // and `Caixa::template`'s `"FIXME — describe this caixa"`
10207        // scaffold every `feira init` emits must continue to pass.
10208        for s in [
10209            "Canonical Rust→wasm32-wasip2 caixa Servico.",
10210            "FIXME — describe this caixa",
10211            "Caixa pour le projet tâche",
10212            "日本語の説明",
10213        ] {
10214            let c = caixa_with_descricao(Some(s));
10215            c.validate_descricao()
10216                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
10217        }
10218    }
10219
10220    #[test]
10221    fn validate_descricao_empty_takes_precedence_over_shape() {
10222        // Cascade pin: a `Some("")` surfaces the narrower
10223        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
10224        // shape-predicate arm. Mirrors the peer
10225        // `validate_licenca_empty_takes_precedence_over_shape` pin
10226        // on the sibling `:licenca` axis.
10227        let c = caixa_with_descricao(Some(""));
10228        let err = c.validate_descricao().unwrap_err();
10229        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
10230    }
10231
10232    #[test]
10233    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
10234        // Diagnostic-shape pin: the error's Display surfaces both
10235        // the `:descricao` slot name and the offending value
10236        // verbatim, so a `feira lint` run can render the diagnostic
10237        // without re-parsing and the author can grep their caixa.lisp
10238        // for the offending `:descricao` line. Mirrors the peer
10239        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
10240        // pin (ee2e888) on the sibling `:licenca` axis.
10241        // The `{descricao:?}` Debug format escapes embedded control
10242        // bytes; the quoted offending value surfaces as
10243        // `"Checkout\nflow."` (literal backslash-n) in the rendered
10244        // diagnostic. The author can grep their caixa.lisp for the
10245        // literal `Checkout` summary prefix.
10246        let c = caixa_with_descricao(Some("Checkout\nflow."));
10247        let rendered = c.validate_descricao().unwrap_err().to_string();
10248        assert!(
10249            rendered.contains(":descricao"),
10250            "diagnostic must name the offending slot: {rendered}",
10251        );
10252        assert!(
10253            rendered.contains("Checkout\\nflow."),
10254            "diagnostic must quote the offending value (debug-escaped): {rendered}",
10255        );
10256    }
10257
10258    #[test]
10259    fn validate_descricao_template_passes() {
10260        // Round-trip pin: the bare `Caixa::template` shape carries
10261        // `:descricao "FIXME — describe this caixa"` (a non-empty
10262        // sentinel), so the template-derived Caixa passes the gate by
10263        // construction. A future template-shape change that omits or
10264        // empties `:descricao` would surface here as a regression.
10265        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10266        c.validate_descricao().unwrap();
10267    }
10268
10269    #[test]
10270    fn validate_descricao_diagnostic_names_offending_slot() {
10271        // Diagnostic-shape pin (peer with
10272        // `validate_repositorio_diagnostic_carries_offending_value`):
10273        // the error's Display surfaces the `:descricao` slot name
10274        // verbatim, so a `feira lint` run can render the diagnostic
10275        // without re-parsing and the author can grep their caixa.lisp
10276        // for the offending `:descricao` line.
10277        let c = caixa_with_descricao(Some(""));
10278        let rendered = c.validate_descricao().unwrap_err().to_string();
10279        assert!(
10280            rendered.contains(":descricao"),
10281            "diagnostic must name the offending slot: {rendered}",
10282        );
10283    }
10284
10285    // ── validate_licenca — universal-axis chart README license shape ──
10286
10287    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
10288        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10289        c.licenca = licenca.map(String::from);
10290        c
10291    }
10292
10293    #[test]
10294    fn validate_licenca_accepts_none() {
10295        // The omit-the-slot identity: `:licenca` is optional. The
10296        // gate is a no-op when the author didn't declare a value —
10297        // every caixa without a `:licenca` line trivially passes,
10298        // and the substrate-side `caixa-helm` renderer falls back to
10299        // the documented `"MIT"` placeholder. Mirrors the peer
10300        // `validate_descricao_accepts_none` posture on the sibling
10301        // `Option<String>` Caixa slot.
10302        let c = caixa_with_licenca(None);
10303        c.validate_licenca().unwrap();
10304    }
10305
10306    #[test]
10307    fn validate_licenca_accepts_canonical_expressions() {
10308        // Positive control: every canonical SPDX expression shape
10309        // pleme-io carries in its existing fixtures + the canonical
10310        // SPDX dual-license / with-exception / `+`-suffix / grouped /
10311        // user-defined-reference shapes all pass the gate. Covers
10312        // the single-license, `OR`-compound, `AND`-compound,
10313        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
10314        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
10315        // production the SPDX 2.1 expression grammar admits that
10316        // sits within the alphabet floor the
10317        // `is_spdx_expression_shape` predicate enforces.
10318        for lic in [
10319            "MIT",
10320            "Apache-2.0",
10321            "Apache-2.0 OR MIT",
10322            "Apache-2.0 AND MIT",
10323            "BSD-3-Clause",
10324            "MPL-2.0",
10325            "GPL-3.0-or-later",
10326            "GPL-2.0+",
10327            "Apache-2.0 WITH LLVM-exception",
10328            "(MIT OR Apache-2.0) AND BSD-3-Clause",
10329            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
10330            "LicenseRef-MyLicense",
10331            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
10332            "x",
10333        ] {
10334            let c = caixa_with_licenca(Some(lic));
10335            c.validate_licenca()
10336                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
10337        }
10338    }
10339
10340    #[test]
10341    fn validate_licenca_rejects_trailing_whitespace() {
10342        // Paste-from-doc whitespace footgun. A trailing space in the
10343        // `:licenca` value would silently break a downstream SPDX
10344        // parser that splits on exact `AND` / `OR` / `WITH` keyword
10345        // boundaries. The shape predicate refuses every trailing
10346        // whitespace byte by construction. Peer with
10347        // `validate_repositorio_rejects_whitespace` and
10348        // `validate_edicao_rejects_trailing_whitespace`.
10349        let c = caixa_with_licenca(Some("MIT "));
10350        let err = c.validate_licenca().unwrap_err();
10351        let ManifestError::LicencaInvalid { licenca, .. } = err else {
10352            panic!("expected LicencaInvalid, got {err:?}");
10353        };
10354        assert_eq!(licenca, "MIT ");
10355    }
10356
10357    #[test]
10358    fn validate_licenca_rejects_leading_whitespace() {
10359        // Symmetric paste-from-doc whitespace footgun on the leading
10360        // boundary — the gate refuses every shape that starts with a
10361        // space byte by construction. Peer with
10362        // `validate_edicao_rejects_leading_whitespace`.
10363        let c = caixa_with_licenca(Some(" MIT"));
10364        let err = c.validate_licenca().unwrap_err();
10365        assert!(
10366            matches!(err, ManifestError::LicencaInvalid { .. }),
10367            "got {err:?}",
10368        );
10369    }
10370
10371    #[test]
10372    fn validate_licenca_rejects_control_char() {
10373        // Paste-from-multiline-doc CRLF footgun — control characters
10374        // at the value boundary land as a malformed line in the
10375        // rendered chart `README.md` `## License` section. Peer with
10376        // `validate_repositorio_rejects_control_char` and
10377        // `validate_edicao_rejects_control_char`.
10378        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
10379            let c = caixa_with_licenca(Some(lic));
10380            let err = c.validate_licenca().unwrap_err();
10381            assert!(
10382                matches!(err, ManifestError::LicencaInvalid { .. }),
10383                "expected LicencaInvalid on {lic:?}, got {err:?}",
10384            );
10385        }
10386    }
10387
10388    #[test]
10389    fn validate_licenca_rejects_tab() {
10390        // Tab-from-aligned-doc footgun — SPDX expressions use a
10391        // single ASCII space between tokens; a tab breaks every
10392        // downstream SPDX parser that splits on exact `" "`
10393        // boundaries.
10394        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
10395        let err = c.validate_licenca().unwrap_err();
10396        assert!(
10397            matches!(err, ManifestError::LicencaInvalid { .. }),
10398            "got {err:?}",
10399        );
10400    }
10401
10402    #[test]
10403    fn validate_licenca_rejects_non_ascii() {
10404        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
10405        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
10406        // ".")` production. The shape predicate refuses every
10407        // non-ASCII byte by construction; peer with
10408        // `validate_edicao_rejects_non_ascii_lookalike`.
10409        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
10410            let c = caixa_with_licenca(Some(lic));
10411            let err = c.validate_licenca().unwrap_err();
10412            assert!(
10413                matches!(err, ManifestError::LicencaInvalid { .. }),
10414                "expected LicencaInvalid on {lic:?}, got {err:?}",
10415            );
10416        }
10417    }
10418
10419    #[test]
10420    fn validate_licenca_rejects_underscore() {
10421        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
10422        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
10423        // snake-case identifier conventions that don't apply to the
10424        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
10425        // "-" / "."`). The shape predicate refuses every underscore
10426        // byte by construction.
10427        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
10428            let c = caixa_with_licenca(Some(lic));
10429            let err = c.validate_licenca().unwrap_err();
10430            assert!(
10431                matches!(err, ManifestError::LicencaInvalid { .. }),
10432                "expected LicencaInvalid on {lic:?}, got {err:?}",
10433            );
10434        }
10435    }
10436
10437    #[test]
10438    fn validate_licenca_rejects_comma_separator() {
10439        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
10440        // SPDX expressions compose multiple licenses via `AND` / `OR`
10441        // keywords, not the comma separator. The shape predicate
10442        // refuses every comma byte by construction.
10443        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
10444            let c = caixa_with_licenca(Some(lic));
10445            let err = c.validate_licenca().unwrap_err();
10446            assert!(
10447                matches!(err, ManifestError::LicencaInvalid { .. }),
10448                "expected LicencaInvalid on {lic:?}, got {err:?}",
10449            );
10450        }
10451    }
10452
10453    #[test]
10454    fn validate_licenca_rejects_slash_dual_license() {
10455        // Slash-dual-license colloquial idiom footgun — the
10456        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
10457        // `package.license` field but non-SPDX; the SPDX equivalent
10458        // is `MIT OR Apache-2.0`. The shape predicate refuses every
10459        // forward-slash byte by construction.
10460        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
10461            let c = caixa_with_licenca(Some(lic));
10462            let err = c.validate_licenca().unwrap_err();
10463            assert!(
10464                matches!(err, ManifestError::LicencaInvalid { .. }),
10465                "expected LicencaInvalid on {lic:?}, got {err:?}",
10466            );
10467        }
10468    }
10469
10470    #[test]
10471    fn validate_licenca_rejects_semicolon_separator() {
10472        // Semicolon-list-separator confusion footgun — adjacent to
10473        // the comma-separator idiom, every list-separator-belongs-
10474        // to-list-grammar confusion lands here.
10475        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
10476        let err = c.validate_licenca().unwrap_err();
10477        assert!(
10478            matches!(err, ManifestError::LicencaInvalid { .. }),
10479            "got {err:?}",
10480        );
10481    }
10482
10483    #[test]
10484    fn validate_licenca_empty_takes_precedence_over_shape() {
10485        // Empty-first cascade pin: the empty `Some("")` surfaces the
10486        // narrower `LicencaEmpty` not the shape-predicate-wrapped
10487        // `LicencaInvalid`, mirroring the peer
10488        // `validate_edicao_empty_takes_precedence_over_shape` and
10489        // `validate_repositorio_empty_takes_precedence_over_shape`
10490        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
10491        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
10492        // The shape predicate also refuses the empty input
10493        // (defensively — `"must not be empty"`), but the manifest-
10494        // layer empty arm runs first to surface the narrower
10495        // diagnostic verbatim.
10496        let c = caixa_with_licenca(Some(""));
10497        let err = c.validate_licenca().unwrap_err();
10498        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
10499    }
10500
10501    #[test]
10502    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
10503        // Diagnostic-shape pin on the shape-predicate arm (peer with
10504        // `validate_edicao_invalid_diagnostic_carries_offending_value`
10505        // and `validate_repositorio_diagnostic_carries_offending_value`):
10506        // the error's Display surfaces the offending value + slot
10507        // name verbatim, so a `feira lint` run can render the
10508        // diagnostic without re-parsing and the author can grep
10509        // their caixa.lisp for the offending `:licenca` value.
10510        let c = caixa_with_licenca(Some("Apache_2.0"));
10511        let rendered = c.validate_licenca().unwrap_err().to_string();
10512        assert!(
10513            rendered.contains(":licenca"),
10514            "diagnostic must name the offending slot: {rendered}",
10515        );
10516        assert!(
10517            rendered.contains("Apache_2.0"),
10518            "diagnostic must quote the offending value: {rendered}",
10519        );
10520    }
10521
10522    #[test]
10523    fn validate_licenca_rejects_empty_some() {
10524        // Canonical paste-from-blank-doc footgun. Without this gate
10525        // the empty `Some("")` silently passed the renderer's
10526        // `Option::unwrap_or_else(|| "MIT".into())` (which only
10527        // fires on `None`) and landed as a bare trailing period in
10528        // the rendered chart `README.md` `## License` section.
10529        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
10530        // arm on the sibling `Option<String>` Caixa slot.
10531        let c = caixa_with_licenca(Some(""));
10532        let err = c.validate_licenca().unwrap_err();
10533        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
10534    }
10535
10536    #[test]
10537    fn validate_licenca_template_passes() {
10538        // Round-trip pin: the bare `Caixa::template` shape (whether
10539        // it carries `:licenca` or omits it) passes the gate by
10540        // construction. A future template-shape change that
10541        // introduced `(:licenca "")` would surface here as a
10542        // regression. Mirrors the peer
10543        // `validate_descricao_template_passes` pin.
10544        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10545        c.validate_licenca().unwrap();
10546    }
10547
10548    #[test]
10549    fn validate_licenca_diagnostic_names_offending_slot() {
10550        // Diagnostic-shape pin (peer with
10551        // `validate_descricao_diagnostic_names_offending_slot`):
10552        // the error's Display surfaces the `:licenca` slot name
10553        // verbatim, so a `feira lint` run can render the diagnostic
10554        // without re-parsing and the author can grep their caixa.lisp
10555        // for the offending `:licenca` line.
10556        let c = caixa_with_licenca(Some(""));
10557        let rendered = c.validate_licenca().unwrap_err().to_string();
10558        assert!(
10559            rendered.contains(":licenca"),
10560            "diagnostic must name the offending slot: {rendered}",
10561        );
10562    }
10563
10564    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
10565
10566    #[test]
10567    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
10568        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
10569        // pin: [`Caixa::licenca`] must return the `:licenca` typed
10570        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10571        // raw `self.licenca.as_deref()` access across every
10572        // representative value in the accept-set — `None` (the "omit
10573        // the slot to defer to the caixa-helm renderer's `MIT`
10574        // fallback" arm every existing fixture without a `:licenca`
10575        // line carries), `Some("")` (a past-the-guard sentinel that
10576        // pins the accessor doesn't perform a silent
10577        // `Some("") → None` collapse on the empty arm — validate
10578        // rejects `Some("")` through `LicencaEmpty` but the accessor
10579        // must ship the raw slot verbatim so a validate-time gate
10580        // regression surfaces at the caixa-helm emit boundary rather
10581        // than being silently absorbed into the fallback), `Some("MIT")`
10582        // (the canonical single-license shape every `feira init`
10583        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
10584        // canonical `OR`-compound shape the peer
10585        // `validate_licenca_accepts_canonical_expressions` positive
10586        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
10587        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
10588        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
10589        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
10590        // guard sentinels — validate rejects each through
10591        // `LicencaInvalid` but the accessor must ship the raw slot
10592        // verbatim).
10593        //
10594        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
10595        // accessor pin on the substrate primitive — opens the "outer
10596        // [`Caixa`] `Option<&str>` scalar" projection pattern the
10597        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
10598        // future lifts fold on. Sibling in shape to the peer per-`:placement`
10599        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10600        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10601        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10602        // axes, extended onto the outer top-level [`Caixa`] universal-
10603        // axis surface. Pins against a future silent detour that
10604        // returned an owned `Option<String>` (which would type-check
10605        // but silently allocate on every accessor call, breaking the
10606        // zero-cost projection every peer sibling accessor carries), a
10607        // `Some("") → None` collapse (which would silently absorb the
10608        // `LicencaEmpty` refusal case at the accessor boundary and the
10609        // caixa-helm emit path would silently fall back to `"MIT"` on
10610        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
10611        // `None → Some("MIT")` collapse (which would silently reify
10612        // the caixa-helm renderer's `"MIT"` fallback at the accessor
10613        // boundary and every downstream consumer keying off the
10614        // `Option::is_none()` discriminator would lose the "author
10615        // omitted the slot" signal).
10616        for licenca in [
10617            None,
10618            Some(""),
10619            Some("MIT"),
10620            Some("Apache-2.0 OR MIT"),
10621            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
10622            Some("MIT "),
10623            Some(" MIT"),
10624            Some("MIT\n"),
10625            Some("Apache_2.0"),
10626            Some("MIT,Apache-2.0"),
10627        ] {
10628            let c = caixa_with_licenca(licenca);
10629            assert_eq!(
10630                c.licenca(),
10631                licenca,
10632                "Caixa::licenca must return :licenca verbatim (got {:?}, \
10633                 expected {licenca:?})",
10634                c.licenca(),
10635            );
10636            assert_eq!(
10637                c.licenca(),
10638                c.licenca.as_deref(),
10639                "Caixa::licenca must byte-equal the raw \
10640                 `self.licenca.as_deref()` field access across every \
10641                 value in the Option<&str> accept-set",
10642            );
10643        }
10644    }
10645
10646    #[test]
10647    fn validate_licenca_empty_arm_routes_through_accessor() {
10648        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
10649        // must key off [`Caixa::licenca`], not the raw
10650        // `self.licenca.as_deref()` field access. Structurally: a
10651        // `Caixa { licenca: Some(""), .. }` must surface the
10652        // `LicencaEmpty` refusal exactly, and a
10653        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
10654        // single-license form) must pass validate. The pair jointly
10655        // pins the accessor + validate-gate composition: any future
10656        // silent detour that had the accessor return `None` on the
10657        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10658        // silently absorb the `LicencaEmpty` refusal at the accessor
10659        // boundary and the validate gate would accept a struct-literal
10660        // `Caixa { licenca: Some(""), .. }` — the composition pin
10661        // catches that at caixa-core build time.
10662        //
10663        // Peer of the per-`:politicas :circuit-breaker`
10664        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
10665        // accessor-composition pin
10666        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
10667        // on the sibling per-M3-mesh-slot required-`u32` axis — same
10668        // "the validate / shape-gate predicate must route through the
10669        // substrate-primitive typed dispatch" discipline extended onto
10670        // the outer top-level [`Caixa`] universal-axis
10671        // `Option<&str>`-composition surface.
10672        let c = caixa_with_licenca(Some(""));
10673        assert!(
10674            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
10675            "validate_licenca must reject licenca == Some(\"\") with \
10676             LicencaEmpty — the accessor and the validate gate must \
10677             route through the same substrate-primitive typed dispatch \
10678             on the :licenca empty arm",
10679        );
10680        let c = caixa_with_licenca(Some("MIT"));
10681        assert!(
10682            c.validate_licenca().is_ok(),
10683            "validate_licenca must accept licenca == Some(\"MIT\") \
10684             (the canonical single-license SPDX shape)",
10685        );
10686    }
10687
10688    #[test]
10689    fn licenca_projects_option_str_by_borrow() {
10690        // The by-borrow pin: [`Caixa::licenca`] returns
10691        // `Option<&str>` by borrow — the `&str` borrows the underlying
10692        // `String` storage of the `Option<String>` slot and the
10693        // accessor must not allocate a fresh `String` on every call.
10694        // Peer of the per-`:placement`
10695        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10696        // borrow pin on the peer per-M3-mesh-slot
10697        // `Option<&str>`-return axis, extended onto the outer top-
10698        // level [`Caixa`] universal-axis `Option<&str>` shape — the
10699        // accessor's returned `&str` must borrow from `&self` (the
10700        // returned reference's lifetime is tied to `&self`), and
10701        // calling the accessor twice on the same [`Caixa`] must yield
10702        // the same `Option<&str>` verbatim (idempotent, no side
10703        // effects on `&self`).
10704        //
10705        // Pins against a future silent detour that returned an owned
10706        // `Option<String>` (which would type-check but silently
10707        // allocate on every call, breaking the zero-cost projection
10708        // every peer sibling accessor carries), or a one-arm-only
10709        // accessor that returned a saturating value on some sentinel
10710        // input (breaking the pass-through invariant the sibling
10711        // required-scalar accessors carry).
10712        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
10713            let c = caixa_with_licenca(licenca);
10714            let first = c.licenca();
10715            let second = c.licenca();
10716            assert_eq!(
10717                first, second,
10718                "Caixa::licenca must be idempotent — two successive \
10719                 calls on the same &self must return the same \
10720                 Option<&str>",
10721            );
10722            assert_eq!(
10723                first, licenca,
10724                "Caixa::licenca must return :licenca verbatim by \
10725                 borrow — got {first:?}, expected {licenca:?}",
10726            );
10727        }
10728    }
10729
10730    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
10731
10732    #[test]
10733    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
10734        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
10735        // pin: [`Caixa::repositorio`] must return the `:repositorio`
10736        // typed byte-string verbatim as an `Option<&str>`, byte-equal
10737        // to the raw `self.repositorio.as_deref()` access across every
10738        // representative value in the accept-set — `None` (the "omit
10739        // the slot to defer to the per-renderer placeholder" arm every
10740        // existing fixture without a `:repositorio` line carries),
10741        // `Some("")` (a past-the-guard sentinel that pins the accessor
10742        // doesn't perform a silent `Some("") → None` collapse on the
10743        // empty arm — validate rejects `Some("")` through
10744        // `RepositorioEmpty` but the accessor must ship the raw slot
10745        // verbatim so a validate-time gate regression surfaces at the
10746        // caixa-helm / caixa-flux emit boundary rather than being
10747        // silently absorbed into the per-renderer fallback),
10748        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
10749        // shorthand every existing manifest fixture across
10750        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
10751        // `Some("https://github.com/pleme-io/checkout")` (the canonical
10752        // `https://` URL the README quickstart uses),
10753        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
10754        // `Some("git://github.com/pleme-io/checkout.git")` /
10755        // `Some("git@github.com:pleme-io/checkout.git")` /
10756        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
10757        // github scheme the shared `is_git_repo_url` predicate
10758        // documents), and five past-the-guard sentinels for the
10759        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
10760        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
10761        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
10762        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
10763        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
10764        // sentinels pin the accessor doesn't silently absorb the
10765        // refusal cases into a fallback).
10766        //
10767        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
10768        // accessor pin on the substrate primitive — sibling of the peer
10769        // [`Caixa::licenca`] (6d5bc28) pin
10770        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
10771        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
10772        // projection pin pattern this pin folds on. Sibling in shape to
10773        // the peer per-`:placement`
10774        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10775        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10776        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10777        // axes, extended onto the outer top-level [`Caixa`] universal-
10778        // axis surface. Pins against a future silent detour that
10779        // returned an owned `Option<String>` (which would type-check
10780        // but silently allocate on every accessor call, breaking the
10781        // zero-cost projection every peer sibling accessor carries), a
10782        // `Some("") → None` collapse (which would silently absorb the
10783        // `RepositorioEmpty` refusal case at the accessor boundary and
10784        // the caixa-helm `Chart.yaml` `home:` fold would silently
10785        // render a `home: null` / omitted field on a struct-literal
10786        // `Caixa { repositorio: Some(""), .. }`), or a
10787        // `None → Some(<default>)` collapse (which would silently reify
10788        // the per-renderer fallback at the accessor boundary and every
10789        // downstream consumer keying off the `Option::is_none()`
10790        // discriminator would lose the "author omitted the slot"
10791        // signal).
10792        for repositorio in [
10793            None,
10794            Some(""),
10795            Some("github:pleme-io/hello-rio"),
10796            Some("https://github.com/pleme-io/checkout"),
10797            Some("ssh://git@github.com/pleme-io/checkout.git"),
10798            Some("git://github.com/pleme-io/checkout.git"),
10799            Some("git@github.com:pleme-io/checkout.git"),
10800            Some("file:///opt/mirrors/pleme-io/checkout"),
10801            Some("pleme-io/checkout"),
10802            Some("-upload-pack=evil"),
10803            Some("github:pleme-io/checkout?ref=main"),
10804            Some("github:pleme-io/checkout#main"),
10805            Some("github:pleme-io/{tpl}"),
10806        ] {
10807            let c = caixa_with_repositorio(repositorio);
10808            assert_eq!(
10809                c.repositorio(),
10810                repositorio,
10811                "Caixa::repositorio must return :repositorio verbatim \
10812                 (got {:?}, expected {repositorio:?})",
10813                c.repositorio(),
10814            );
10815            assert_eq!(
10816                c.repositorio(),
10817                c.repositorio.as_deref(),
10818                "Caixa::repositorio must byte-equal the raw \
10819                 `self.repositorio.as_deref()` field access across every \
10820                 value in the Option<&str> accept-set",
10821            );
10822        }
10823    }
10824
10825    #[test]
10826    fn validate_repositorio_empty_arm_routes_through_accessor() {
10827        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
10828        // gate must key off [`Caixa::repositorio`], not the raw
10829        // `self.repositorio.as_deref()` field access. Structurally: a
10830        // `Caixa { repositorio: Some(""), .. }` must surface the
10831        // `RepositorioEmpty` refusal exactly, and a
10832        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
10833        // (the canonical `github:` shorthand form) must pass validate.
10834        // The pair jointly pins the accessor + validate-gate
10835        // composition: any future silent detour that had the accessor
10836        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
10837        // collapse) would silently absorb the `RepositorioEmpty` refusal
10838        // at the accessor boundary and the validate gate would accept a
10839        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
10840        // composition pin catches that at caixa-core build time.
10841        //
10842        // Peer of the [`Caixa::licenca`] (6d5bc28)
10843        // `validate_licenca_empty_arm_routes_through_accessor`
10844        // composition pin on the sibling outer top-level [`Caixa`]
10845        // `Option<&str>` universal-axis surface — same "the validate /
10846        // shape-gate predicate must route through the substrate-
10847        // primitive typed dispatch" discipline extended onto the second
10848        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10849        // composition surface.
10850        let c = caixa_with_repositorio(Some(""));
10851        assert!(
10852            matches!(
10853                c.validate_repositorio(),
10854                Err(ManifestError::RepositorioEmpty),
10855            ),
10856            "validate_repositorio must reject repositorio == Some(\"\") \
10857             with RepositorioEmpty — the accessor and the validate gate \
10858             must route through the same substrate-primitive typed \
10859             dispatch on the :repositorio empty arm",
10860        );
10861        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
10862        assert!(
10863            c.validate_repositorio().is_ok(),
10864            "validate_repositorio must accept repositorio == \
10865             Some(\"github:pleme-io/hello-rio\") (the canonical \
10866             `github:` shorthand git-repo-URL shape)",
10867        );
10868    }
10869
10870    #[test]
10871    fn repositorio_projects_option_str_by_borrow() {
10872        // The by-borrow pin: [`Caixa::repositorio`] returns
10873        // `Option<&str>` by borrow — the `&str` borrows the underlying
10874        // `String` storage of the `Option<String>` slot and the
10875        // accessor must not allocate a fresh `String` on every call.
10876        // Peer of the per-`:placement`
10877        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
10878        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
10879        // `Option<&str>`-return axes, extended onto the second outer
10880        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
10881        // the accessor's returned `&str` must borrow from `&self` (the
10882        // returned reference's lifetime is tied to `&self`), and
10883        // calling the accessor twice on the same [`Caixa`] must yield
10884        // the same `Option<&str>` verbatim (idempotent, no side effects
10885        // on `&self`).
10886        //
10887        // Pins against a future silent detour that returned an owned
10888        // `Option<String>` (which would type-check but silently
10889        // allocate on every call, breaking the zero-cost projection
10890        // every peer sibling accessor carries), or a one-arm-only
10891        // accessor that returned a saturating value on some sentinel
10892        // input (breaking the pass-through invariant the sibling
10893        // required-scalar accessors carry).
10894        for repositorio in [
10895            None,
10896            Some(""),
10897            Some("github:pleme-io/hello-rio"),
10898            Some("https://github.com/pleme-io/checkout"),
10899        ] {
10900            let c = caixa_with_repositorio(repositorio);
10901            let first = c.repositorio();
10902            let second = c.repositorio();
10903            assert_eq!(
10904                first, second,
10905                "Caixa::repositorio must be idempotent — two successive \
10906                 calls on the same &self must return the same \
10907                 Option<&str>",
10908            );
10909            assert_eq!(
10910                first, repositorio,
10911                "Caixa::repositorio must return :repositorio verbatim by \
10912                 borrow — got {first:?}, expected {repositorio:?}",
10913            );
10914        }
10915    }
10916
10917    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
10918
10919    #[test]
10920    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
10921        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
10922        // return the author-declared `:repositorio` byte-string verbatim
10923        // on the `Some` arm — no scheme rewrite, no trailing-slash
10924        // canonicalization, no `github:` → `https://github.com/`
10925        // desugaring. The resolved-URL composer is the projection of
10926        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
10927        // the `String`-return arity every substrate-side field-fill
10928        // consumer keys off; on the `Some` arm the projection is
10929        // `str::to_owned` verbatim, so every accept-set value the
10930        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
10931        // across_permutations` pin covers (`https://…`, `github:…`,
10932        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
10933        // guard sentinel `pleme-io/…`) must survive the accessor
10934        // byte-equal. Pins against a future silent detour that rewrote
10935        // the `github:` shorthand to the `https://github.com/` full URL
10936        // at the accessor boundary (which would silently split the
10937        // resolved-URL surface from the raw [`Caixa::repositorio`]
10938        // accessor's documented pass-through invariant), or a trailing-
10939        // slash normalization (which would silently break the
10940        // FluxCD `GitRepository` `spec.url` byte-exact match every
10941        // downstream consumer keys the source-controller reconcile off).
10942        for repositorio in [
10943            "github:pleme-io/hello-rio",
10944            "https://github.com/pleme-io/checkout",
10945            "ssh://git@github.com/pleme-io/checkout.git",
10946            "git://github.com/pleme-io/checkout.git",
10947            "git@github.com:pleme-io/checkout.git",
10948            "file:///opt/mirrors/pleme-io/checkout",
10949        ] {
10950            let c = caixa_with_repositorio(Some(repositorio));
10951            assert_eq!(
10952                c.canonical_git_url(),
10953                repositorio,
10954                "Caixa::canonical_git_url on the Some arm must return \
10955                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
10956                c.canonical_git_url(),
10957            );
10958        }
10959    }
10960
10961    #[test]
10962    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
10963        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
10964        // `None` arm must emit the substrate's canonical pleme-org github
10965        // URL derived from `caixa.nome()` — `https://github.com/<org>/
10966        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
10967        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
10968        // is the exact byte-image of the prior inline
10969        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
10970        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
10971        // re-derived open-coded. Pins against a future silent detour
10972        // that migrated the `<org>` segment to a different constant (a
10973        // fork rebranding that split off a new
10974        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
10975        // to migrate onto), a scheme change (`https://` → `git://` or
10976        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
10977        // override (which would break the substrate-wide single-source-
10978        // of-truth guarantee this method encodes).
10979        let c = caixa_with_repositorio(None);
10980        let expected = format!(
10981            "https://github.com/{org}/{nome}",
10982            org = crate::DEFAULT_PLEME_GIT_ORG,
10983            nome = c.nome(),
10984        );
10985        assert_eq!(
10986            c.canonical_git_url(),
10987            expected,
10988            "Caixa::canonical_git_url on the None arm must fold through \
10989             the substrate's canonical pleme-org github URL fallback \
10990             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
10991             {:?}, expected {expected:?}",
10992            c.canonical_git_url(),
10993        );
10994    }
10995
10996    #[test]
10997    fn canonical_git_url_byte_matches_manual_composition() {
10998        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
10999        // byte-identically to the manual open-coded
11000        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
11001        //  format!("https://github.com/{org}/{nome}", ...))` composition
11002        // every prior substrate-side caller re-derived. Guards the
11003        // paired-site convergence just applied at caixa-flux's
11004        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
11005        // now routes through this accessor): a future implementation of
11006        // this method that reordered the format arguments, swapped the
11007        // `<org>` constant for a different one, or interposed a
11008        // canonicalization pass on the `Some` arm surfaces here as a
11009        // caixa-core build-time test failure rather than as a downstream
11010        // FluxCD `GitRepository` reconcile mismatch far from this
11011        // method's source.
11012        for repositorio in [
11013            None,
11014            Some("github:pleme-io/hello-rio"),
11015            Some("https://github.com/pleme-io/checkout"),
11016            Some("ssh://git@github.com/pleme-io/checkout.git"),
11017        ] {
11018            let c = caixa_with_repositorio(repositorio);
11019            let manual = c.repositorio().map_or_else(
11020                || {
11021                    format!(
11022                        "https://github.com/{org}/{nome}",
11023                        org = crate::DEFAULT_PLEME_GIT_ORG,
11024                        nome = c.nome(),
11025                    )
11026                },
11027                str::to_owned,
11028            );
11029            assert_eq!(
11030                c.canonical_git_url(),
11031                manual,
11032                "Caixa::canonical_git_url must byte-equal the manual \
11033                 open-coded `repositorio().map(str::to_owned)\
11034                 .unwrap_or_else(|| format!(...))` composition across \
11035                 every representative :repositorio input — got {:?}, \
11036                 expected {manual:?}",
11037                c.canonical_git_url(),
11038            );
11039        }
11040    }
11041
11042    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
11043
11044    #[test]
11045    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
11046        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
11047        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
11048        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
11049        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
11050        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
11051        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
11052        // the `0.0.0` boundary case. Every accept-set value the peer
11053        // validate gate lets through must survive the resolved-tag
11054        // projection byte-equal.
11055        for versao in [
11056            "0.1.0",
11057            "0.0.0",
11058            "1.0.0",
11059            "1.2.3-rc.1",
11060            "1.2.3+build.42",
11061            "1.2.3-rc.1+build.42",
11062        ] {
11063            let c = caixa_with_versao(versao);
11064            let expected = format!(
11065                "{prefix}{versao}",
11066                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11067            );
11068            assert_eq!(
11069                c.publish_tag(),
11070                expected,
11071                "Caixa::publish_tag must compose \
11072                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
11073                 :versao ({versao:?}) verbatim — got {got:?}, \
11074                 expected {expected:?}",
11075                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11076                got = c.publish_tag(),
11077            );
11078        }
11079    }
11080
11081    #[test]
11082    fn publish_tag_starts_with_default_publish_tag_prefix() {
11083        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
11084        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
11085        // byte-string on every input, guarding a hypothetical future
11086        // implementation that migrated the prefix segment to an inline
11087        // literal (`"v"`) that would silently drift from any rebrand of
11088        // the lifted constant. Peer to the sibling caixa-flux
11089        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
11090        // test which pins the same prefix invariant at the reader-side
11091        // `GitRefSpec::Tag` emit site.
11092        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
11093            let c = caixa_with_versao(versao);
11094            let tag = c.publish_tag();
11095            assert!(
11096                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
11097                "Caixa::publish_tag emission {tag:?} must start with \
11098                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
11099                 ({prefix:?})",
11100                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11101            );
11102        }
11103    }
11104
11105    #[test]
11106    fn publish_tag_byte_matches_manual_composition() {
11107        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
11108        // identically to the manual open-coded
11109        // `format!("{prefix}{versao}", prefix =
11110        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
11111        //  caixa.versao())` composition every prior substrate-side
11112        // caller re-derived. Guards the paired-site convergence just
11113        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
11114        // `git_ref` composer (which now routes through this accessor):
11115        // a future implementation of this method that reordered the
11116        // format arguments, swapped the `<prefix>` constant for a
11117        // different one, or interposed a canonicalization pass on the
11118        // `:versao` axis surfaces here as a caixa-core build-time test
11119        // failure rather than as a downstream FluxCD `GitRepository`
11120        // reconcile mismatch far from this method's source.
11121        for versao in [
11122            "0.1.0",
11123            "0.0.0",
11124            "1.2.3-rc.1",
11125            "1.2.3+build.42",
11126            "1.2.3-rc.1+build.42",
11127        ] {
11128            let c = caixa_with_versao(versao);
11129            let manual = format!(
11130                "{prefix}{versao}",
11131                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11132                versao = c.versao(),
11133            );
11134            assert_eq!(
11135                c.publish_tag(),
11136                manual,
11137                "Caixa::publish_tag must byte-equal the manual \
11138                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
11139                 composition across every representative :versao input \
11140                 — got {got:?}, expected {manual:?}",
11141                got = c.publish_tag(),
11142            );
11143        }
11144    }
11145
11146    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
11147
11148    #[test]
11149    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
11150        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
11151        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
11152        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
11153        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
11154        // set sweep documents — single-word, hyphen-joined, version-
11155        // suffixed, single-char, two-char, digit-start, retry-suffixed.
11156        // Every accept-set value the peer validate gate lets through must
11157        // survive the resolved-chart-name projection byte-equal.
11158        for nome in [
11159            "checkout",
11160            "cart-v2",
11161            "a",
11162            "db",
11163            "3rd-party-shim",
11164            "payment-retry",
11165            "0",
11166        ] {
11167            let c = caixa_with_nome(nome);
11168            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
11169            assert_eq!(
11170                c.lareira_chart_name(),
11171                expected,
11172                "Caixa::lareira_chart_name must compose \
11173                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
11174                 :nome ({nome:?}) verbatim — got {got:?}, \
11175                 expected {expected:?}",
11176                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
11177                got = c.lareira_chart_name(),
11178            );
11179        }
11180    }
11181
11182    #[test]
11183    fn lareira_chart_name_starts_with_lifted_prefix() {
11184        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
11185        // must begin with the canonical
11186        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
11187        // input, guarding a hypothetical future implementation that
11188        // migrated the prefix segment to an inline literal (`"lareira-"`)
11189        // that would silently drift from any rebrand of the lifted
11190        // constant. Peer to the sibling
11191        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
11192        // the co-resident resolved-publish-tag composer's prefix axis.
11193        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
11194            let c = caixa_with_nome(nome);
11195            let chart = c.lareira_chart_name();
11196            assert!(
11197                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
11198                "Caixa::lareira_chart_name emission {chart:?} must start \
11199                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
11200                 ({prefix:?})",
11201                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
11202            );
11203        }
11204    }
11205
11206    #[test]
11207    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
11208        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
11209        // byte-identically to the manual open-coded
11210        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
11211        // composition every prior substrate-side caller re-derived.
11212        // Guards the paired-site convergence just applied at caixa-helm's
11213        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
11214        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
11215        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
11216        // composer (all of which now route through this accessor): a
11217        // future implementation of this method that reordered the
11218        // composition arguments, swapped the `<prefix>` constant for a
11219        // different one, or interposed a canonicalization pass on the
11220        // `:nome` axis surfaces here as a caixa-core build-time test
11221        // failure rather than as a downstream Helm chart-render / FluxCD
11222        // reconcile / tatara Process-CR mismatch far from this method's
11223        // source.
11224        for nome in [
11225            "checkout",
11226            "cart-v2",
11227            "a",
11228            "db",
11229            "3rd-party-shim",
11230            "payment-retry",
11231        ] {
11232            let c = caixa_with_nome(nome);
11233            let manual = crate::lareira_chart_name(c.nome());
11234            assert_eq!(
11235                c.lareira_chart_name(),
11236                manual,
11237                "Caixa::lareira_chart_name must byte-equal the manual \
11238                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
11239                 composition across every representative :nome input — \
11240                 got {got:?}, expected {manual:?}",
11241                got = c.lareira_chart_name(),
11242            );
11243        }
11244    }
11245
11246    // ── Caixa::oci_chart_ref — resolved-OCI-chart-ref composer ────────
11247
11248    #[test]
11249    fn oci_chart_ref_composes_scheme_and_lareira_chart_name_on_all_shapes() {
11250        // Fail-before-pass-after pin: [`Caixa::oci_chart_ref`] must
11251        // compose [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied
11252        // `registry` + [`crate::lareira_chart_name`]-of-[`Caixa::nome`]
11253        // across the full paired `(registry, :nome)` accept-set — every
11254        // representative registry the substrate-side emitters carry
11255        // (`ghcr.io/pleme-io/charts`, the canonical CAIXA-SDLC §II
11256        // ArtifactHub-tier registry; `ghcr.io/pleme-io`, the bare-org
11257        // arm the sibling `oci_chart_ref_pins_byte_shape_against_prior_
11258        // inline_format` render-side pin exercises; `registry.example.
11259        // com`, an off-org shape; `localhost:5000`, the local-dev shape
11260        // every `feira chart` iteration path lands under) × every DNS-
11261        // 1123 `:nome` shape the peer `validate_nome_accepts_canonical_
11262        // forms` positive-set sweep documents (single-word, hyphen-
11263        // joined, single-char, two-char, digit-start, retry-suffixed).
11264        // Every accept-set pair the peer validate gates let through must
11265        // survive the resolved-OCI-ref projection byte-equal.
11266        for registry in [
11267            "ghcr.io/pleme-io/charts",
11268            "ghcr.io/pleme-io",
11269            "registry.example.com",
11270            "localhost:5000",
11271        ] {
11272            for nome in [
11273                "checkout",
11274                "cart-v2",
11275                "a",
11276                "db",
11277                "3rd-party-shim",
11278                "payment-retry",
11279                "0",
11280            ] {
11281                let c = caixa_with_nome(nome);
11282                let expected = format!(
11283                    "{scheme}{registry}/{chart}",
11284                    scheme = crate::OCI_SCHEME_PREFIX,
11285                    chart = crate::lareira_chart_name(nome),
11286                );
11287                assert_eq!(
11288                    c.oci_chart_ref(registry),
11289                    expected,
11290                    "Caixa::oci_chart_ref must compose \
11291                     OCI_SCHEME_PREFIX ({scheme:?}) + registry ({registry:?}) + \
11292                     lareira_chart_name(:nome ({nome:?})) verbatim — got {got:?}, \
11293                     expected {expected:?}",
11294                    scheme = crate::OCI_SCHEME_PREFIX,
11295                    got = c.oci_chart_ref(registry),
11296                );
11297            }
11298        }
11299    }
11300
11301    #[test]
11302    fn oci_chart_ref_starts_with_lifted_scheme_prefix() {
11303        // Scheme-prefix-shape pin: every [`Caixa::oci_chart_ref`]
11304        // emission must begin with the canonical
11305        // [`crate::OCI_SCHEME_PREFIX`] byte-string on every input, guarding
11306        // a hypothetical future implementation that migrated the scheme
11307        // segment to an inline literal (`"oci://"`) that would silently
11308        // drift from any rebrand of the lifted constant. Peer to the
11309        // sibling [`publish_tag_starts_with_default_publish_tag_prefix`]
11310        // + [`lareira_chart_name_starts_with_lifted_prefix`] pins on the
11311        // co-resident resolved-publish-tag / resolved-chart-name
11312        // composers' prefix axes.
11313        for registry in [
11314            "ghcr.io/pleme-io/charts",
11315            "ghcr.io/pleme-io",
11316            "localhost:5000",
11317        ] {
11318            for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
11319                let c = caixa_with_nome(nome);
11320                let ref_ = c.oci_chart_ref(registry);
11321                assert!(
11322                    ref_.starts_with(crate::OCI_SCHEME_PREFIX),
11323                    "Caixa::oci_chart_ref emission {ref_:?} must start \
11324                     with the lifted crate::OCI_SCHEME_PREFIX ({scheme:?}) \
11325                     — registry ({registry:?}), :nome ({nome:?})",
11326                    scheme = crate::OCI_SCHEME_PREFIX,
11327                );
11328            }
11329        }
11330    }
11331
11332    #[test]
11333    fn oci_chart_ref_byte_matches_canonical_helper_composition() {
11334        // Byte-parity pin: [`Caixa::oci_chart_ref`] must render byte-
11335        // identically to the manual open-coded
11336        // `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step
11337        // composition every prior substrate-side caller re-derived.
11338        // Guards the paired-site convergence just applied at caixa-
11339        // tatara's [`derive_chart_ref`] helper (which now routes through
11340        // this accessor): a future implementation of this method that
11341        // reordered the composition arguments, swapped the `<scheme>`
11342        // constant for a different one, migrated the `<chart>` segment
11343        // off the paired [`crate::lareira_chart_name`] composer, or
11344        // interposed a canonicalization pass on either input axis
11345        // surfaces here as a caixa-core build-time test failure rather
11346        // than as a downstream `helm install` / FluxCD OCI-source
11347        // reconcile / tatara `Process`-CR mismatch far from this
11348        // method's source. Sibling to the peer
11349        // [`lareira_chart_name_byte_matches_canonical_helper_composition`]
11350        // / [`publish_tag_byte_matches_manual_composition`] /
11351        // [`canonical_git_url_byte_matches_manual_composition`] byte-
11352        // parity pins that carry the same discipline on the co-resident
11353        // resolved-chart-name / resolved-publish-tag / resolved-git-URL
11354        // composers.
11355        for registry in [
11356            "ghcr.io/pleme-io/charts",
11357            "ghcr.io/pleme-io",
11358            "registry.example.com",
11359            "localhost:5000",
11360        ] {
11361            for nome in [
11362                "checkout",
11363                "cart-v2",
11364                "a",
11365                "db",
11366                "3rd-party-shim",
11367                "payment-retry",
11368            ] {
11369                let c = caixa_with_nome(nome);
11370                let manual = crate::oci_chart_ref(registry, c.nome());
11371                assert_eq!(
11372                    c.oci_chart_ref(registry),
11373                    manual,
11374                    "Caixa::oci_chart_ref must byte-equal the manual \
11375                     open-coded `caixa_core::oci_chart_ref(registry, \
11376                     caixa.nome())` composition across every representative \
11377                     (registry, :nome) pair — registry ({registry:?}), \
11378                     :nome ({nome:?}), got {got:?}, expected {manual:?}",
11379                    got = c.oci_chart_ref(registry),
11380                );
11381            }
11382        }
11383    }
11384
11385    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
11386
11387    #[test]
11388    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
11389        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
11390        // pin: [`Caixa::descricao`] must return the `:descricao` typed
11391        // byte-string verbatim as an `Option<&str>`, byte-equal to the
11392        // raw `self.descricao.as_deref()` access across every
11393        // representative value in the accept-set — `None` (the "omit
11394        // the slot to defer to the per-renderer `caixa.nome`-derived
11395        // fallback" arm every existing fixture without a `:descricao`
11396        // line carries), `Some("")` (a past-the-guard sentinel that
11397        // pins the accessor doesn't perform a silent `Some("") → None`
11398        // collapse on the empty arm — validate rejects `Some("")`
11399        // through `DescricaoEmpty` but the accessor must ship the raw
11400        // slot verbatim so a validate-time gate regression surfaces at
11401        // the caixa-helm / caixa-feira emit boundary rather than being
11402        // silently absorbed into the per-renderer `caixa.nome`-derived
11403        // fallback), `Some("Checkout flow.")` (the canonical one-line
11404        // prose descriptor the peer
11405        // `validate_descricao_accepts_canonical_value` positive sweep
11406        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
11407        // Servico.")` (the multi-byte Unicode continuation-byte shape
11408        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
11409        // multi-glyph Unicode shape the peer
11410        // `is_chart_description_shape` predicate accepts), and five
11411        // past-the-guard sentinels for the `DescricaoInvalid` refusal
11412        // cases (`Some(" Checkout flow.")` leading-whitespace,
11413        // `Some("Checkout flow. ")` trailing-whitespace,
11414        // `Some("Checkout\nflow.")` embedded-LF,
11415        // `Some("Checkout\tflow.")` embedded-TAB, and
11416        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
11417        // the accessor doesn't silently absorb the refusal cases into
11418        // a fallback).
11419        //
11420        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
11421        // accessor pin on the substrate primitive — sibling of the peer
11422        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
11423        // (cc7332d) pins that opened the "outer [`Caixa`]
11424        // `Option<&str>` scalar" projection pin pattern this pin folds
11425        // on. Sibling in shape to the peer per-`:placement`
11426        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11427        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11428        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11429        // axes, extended onto the outer top-level [`Caixa`] universal-
11430        // axis surface. Pins against a future silent detour that
11431        // returned an owned `Option<String>` (which would type-check
11432        // but silently allocate on every accessor call, breaking the
11433        // zero-cost projection every peer sibling accessor carries), a
11434        // `Some("") → None` collapse (which would silently absorb the
11435        // `DescricaoEmpty` refusal case at the accessor boundary and
11436        // the caixa-helm `Chart.yaml` `description:` fold would
11437        // silently render a `caixa.nome`-derived fallback on a
11438        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
11439        // `None → Some(<default>)` collapse (which would silently
11440        // reify the per-renderer `caixa.nome`-derived fallback at the
11441        // accessor boundary and every downstream consumer keying off
11442        // the `Option::is_none()` discriminator would lose the "author
11443        // omitted the slot" signal).
11444        for descricao in [
11445            None,
11446            Some(""),
11447            Some("Checkout flow."),
11448            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
11449            Some("→ — · ✓"),
11450            Some(" Checkout flow."),
11451            Some("Checkout flow. "),
11452            Some("Checkout\nflow."),
11453            Some("Checkout\tflow."),
11454            Some("Checkout\x00flow."),
11455        ] {
11456            let c = caixa_with_descricao(descricao);
11457            assert_eq!(
11458                c.descricao(),
11459                descricao,
11460                "Caixa::descricao must return :descricao verbatim (got \
11461                 {:?}, expected {descricao:?})",
11462                c.descricao(),
11463            );
11464            assert_eq!(
11465                c.descricao(),
11466                c.descricao.as_deref(),
11467                "Caixa::descricao must byte-equal the raw \
11468                 `self.descricao.as_deref()` field access across every \
11469                 value in the Option<&str> accept-set",
11470            );
11471        }
11472    }
11473
11474    #[test]
11475    fn validate_descricao_empty_arm_routes_through_accessor() {
11476        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
11477        // gate must key off [`Caixa::descricao`], not the raw
11478        // `self.descricao.as_deref()` field access. Structurally: a
11479        // `Caixa { descricao: Some(""), .. }` must surface the
11480        // `DescricaoEmpty` refusal exactly, and a
11481        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
11482        // canonical one-line-prose form) must pass validate. The pair
11483        // jointly pins the accessor + validate-gate composition: any
11484        // future silent detour that had the accessor return `None` on
11485        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
11486        // silently absorb the `DescricaoEmpty` refusal at the accessor
11487        // boundary and the validate gate would accept a struct-literal
11488        // `Caixa { descricao: Some(""), .. }` — the composition pin
11489        // catches that at caixa-core build time.
11490        //
11491        // Peer of the [`Caixa::licenca`] (6d5bc28)
11492        // `validate_licenca_empty_arm_routes_through_accessor` and
11493        // [`Caixa::repositorio`] (cc7332d)
11494        // `validate_repositorio_empty_arm_routes_through_accessor`
11495        // composition pins on the sibling outer top-level [`Caixa`]
11496        // `Option<&str>` universal-axis surface — same "the validate /
11497        // shape-gate predicate must route through the substrate-
11498        // primitive typed dispatch" discipline extended onto the third
11499        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
11500        // composition surface.
11501        let c = caixa_with_descricao(Some(""));
11502        assert!(
11503            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
11504            "validate_descricao must reject descricao == Some(\"\") \
11505             with DescricaoEmpty — the accessor and the validate gate \
11506             must route through the same substrate-primitive typed \
11507             dispatch on the :descricao empty arm",
11508        );
11509        let c = caixa_with_descricao(Some("Checkout flow."));
11510        assert!(
11511            c.validate_descricao().is_ok(),
11512            "validate_descricao must accept descricao == \
11513             Some(\"Checkout flow.\") (the canonical one-line-prose \
11514             chart-description shape)",
11515        );
11516    }
11517
11518    #[test]
11519    fn descricao_projects_option_str_by_borrow() {
11520        // The by-borrow pin: [`Caixa::descricao`] returns
11521        // `Option<&str>` by borrow — the `&str` borrows the underlying
11522        // `String` storage of the `Option<String>` slot and the
11523        // accessor must not allocate a fresh `String` on every call.
11524        // Peer of the [`Caixa::licenca`] (6d5bc28) and
11525        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
11526        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
11527        // the per-`:placement`
11528        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11529        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
11530        // return axis, extended onto the third outer top-level
11531        // [`Caixa`] universal-axis `Option<&str>` shape — the
11532        // accessor's returned `&str` must borrow from `&self` (the
11533        // returned reference's lifetime is tied to `&self`), and
11534        // calling the accessor twice on the same [`Caixa`] must yield
11535        // the same `Option<&str>` verbatim (idempotent, no side
11536        // effects on `&self`).
11537        //
11538        // Pins against a future silent detour that returned an owned
11539        // `Option<String>` (which would type-check but silently
11540        // allocate on every call, breaking the zero-cost projection
11541        // every peer sibling accessor carries), or a one-arm-only
11542        // accessor that returned a saturating value on some sentinel
11543        // input (breaking the pass-through invariant the sibling
11544        // required-scalar accessors carry).
11545        for descricao in [
11546            None,
11547            Some(""),
11548            Some("Checkout flow."),
11549            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
11550        ] {
11551            let c = caixa_with_descricao(descricao);
11552            let first = c.descricao();
11553            let second = c.descricao();
11554            assert_eq!(
11555                first, second,
11556                "Caixa::descricao must be idempotent — two successive \
11557                 calls on the same &self must return the same \
11558                 Option<&str>",
11559            );
11560            assert_eq!(
11561                first, descricao,
11562                "Caixa::descricao must return :descricao verbatim by \
11563                 borrow — got {first:?}, expected {descricao:?}",
11564            );
11565        }
11566    }
11567
11568    // ── validate_edicao — universal-axis language-edition shape ──
11569
11570    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
11571        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11572        c.edicao = edicao.map(String::from);
11573        c
11574    }
11575
11576    #[test]
11577    fn validate_edicao_accepts_none() {
11578        // The omit-the-slot identity: `:edicao` is optional. The
11579        // gate is a no-op when the author didn't declare a value —
11580        // every caixa without an `:edicao` line trivially passes,
11581        // and the substrate-side build pipeline falls back to the
11582        // documented default edition. Mirrors the peer
11583        // `validate_licenca_accepts_none` posture on the sibling
11584        // `Option<String>` Caixa slot.
11585        let c = caixa_with_edicao(None);
11586        c.validate_edicao().unwrap();
11587    }
11588
11589    #[test]
11590    fn validate_edicao_accepts_canonical_value() {
11591        // Positive control: the canonical `"2026"` edition every
11592        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
11593        // `caixa-mesh`) carries by construction passes the gate.
11594        // Future-introduced sibling editions (`"2027"`, `"2030"`,
11595        // `"2049"`) that match the same 4-digit ASCII decimal year
11596        // shape must also trivially pass — the structural shape
11597        // predicate accepts every well-formed year regardless of
11598        // whether the substrate yet understands the specific value
11599        // (a future known-edition allowlist tightens that).
11600        for ed in ["2026", "2027", "2030", "2049"] {
11601            let c = caixa_with_edicao(Some(ed));
11602            c.validate_edicao()
11603                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
11604        }
11605    }
11606
11607    #[test]
11608    fn validate_edicao_rejects_empty_some() {
11609        // Canonical paste-from-blank-doc footgun. Without this gate
11610        // the empty `Some("")` silently lands as `(:edicao "")` in
11611        // the rendered caixa.lisp and a future renderer-side
11612        // consumer's `Option::unwrap_or_else` (which only fires on
11613        // `None`) skips its fallback. Mirrors the peer
11614        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
11615        // `Option<String>` Caixa slot.
11616        let c = caixa_with_edicao(Some(""));
11617        let err = c.validate_edicao().unwrap_err();
11618        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
11619    }
11620
11621    #[test]
11622    fn validate_edicao_rejects_free_form_non_year() {
11623        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
11624        // `"nightly"` shapes carry no operational meaning on the
11625        // substrate's build-time edition selector. Until this gate
11626        // landed the bare empty-arm check let every such value
11627        // through and broke far from the source caixa.lisp. Peer
11628        // with the shape-predicate cascade
11629        // `validate_repositorio_rejects_missing_colon_separator`
11630        // establishes past its own empty arm.
11631        for ed in ["x", "latest", "nightly", "stable"] {
11632            let c = caixa_with_edicao(Some(ed));
11633            let err = c.validate_edicao().unwrap_err();
11634            assert!(
11635                matches!(err, ManifestError::EdicaoInvalid { .. }),
11636                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11637            );
11638        }
11639    }
11640
11641    #[test]
11642    fn validate_edicao_rejects_trailing_whitespace() {
11643        // Paste-from-doc whitespace footgun. A trailing space in
11644        // the `:edicao` value would silently break the substrate's
11645        // build-time edition match-table lookup at the rendered
11646        // artifact's edition-selector consumer. The shape predicate
11647        // refuses every whitespace byte by construction (any byte
11648        // outside `0-9` fails `is_ascii_digit`). Peer with
11649        // `validate_repositorio_rejects_whitespace`.
11650        let c = caixa_with_edicao(Some("2026 "));
11651        let err = c.validate_edicao().unwrap_err();
11652        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
11653            panic!("expected EdicaoInvalid, got {err:?}");
11654        };
11655        assert_eq!(edicao, "2026 ");
11656    }
11657
11658    #[test]
11659    fn validate_edicao_rejects_leading_whitespace() {
11660        // Symmetric paste-from-doc whitespace footgun on the leading
11661        // boundary — the gate refuses every shape with a non-digit
11662        // byte by construction.
11663        let c = caixa_with_edicao(Some(" 2026"));
11664        let err = c.validate_edicao().unwrap_err();
11665        assert!(
11666            matches!(err, ManifestError::EdicaoInvalid { .. }),
11667            "got {err:?}",
11668        );
11669    }
11670
11671    #[test]
11672    fn validate_edicao_rejects_control_char() {
11673        // Paste-from-multiline-doc CRLF footgun — control characters
11674        // at the value boundary break the substrate's build-time
11675        // edition-selector parser. Peer with
11676        // `validate_repositorio_rejects_control_char`.
11677        let c = caixa_with_edicao(Some("2026\n"));
11678        let err = c.validate_edicao().unwrap_err();
11679        assert!(
11680            matches!(err, ManifestError::EdicaoInvalid { .. }),
11681            "got {err:?}",
11682        );
11683    }
11684
11685    #[test]
11686    fn validate_edicao_rejects_non_ascii_lookalike() {
11687        // Fullwidth-keyboard look-alike footgun — `"2026"` is
11688        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
11689        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
11690        // edition selector wants an ASCII year, and the gate
11691        // refuses every non-ASCII shape by construction (length in
11692        // bytes is 12 ≠ 4, *and* every byte falls outside
11693        // `is_ascii_digit`'s `0-9` range).
11694        let c = caixa_with_edicao(Some("2026"));
11695        let err = c.validate_edicao().unwrap_err();
11696        assert!(
11697            matches!(err, ManifestError::EdicaoInvalid { .. }),
11698            "got {err:?}",
11699        );
11700    }
11701
11702    #[test]
11703    fn validate_edicao_rejects_version_tag_prefix() {
11704        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
11705        // / `"r2026"` are familiar shapes from git-tag / Rust
11706        // edition / release-tag conventions that don't apply to
11707        // the year-shaped edition axis. The shape predicate refuses
11708        // every leading non-digit prefix.
11709        for ed in ["v2026", "e2026", "r2026"] {
11710            let c = caixa_with_edicao(Some(ed));
11711            let err = c.validate_edicao().unwrap_err();
11712            assert!(
11713                matches!(err, ManifestError::EdicaoInvalid { .. }),
11714                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11715            );
11716        }
11717    }
11718
11719    #[test]
11720    fn validate_edicao_rejects_decimal_shape() {
11721        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
11722        // `"2026.0"` are familiar shapes from semver / float
11723        // conventions that don't apply to the year-shaped edition
11724        // axis. The shape predicate refuses every non-digit byte
11725        // (`.` falls outside `is_ascii_digit`).
11726        for ed in ["2026.1", "2026.0", "2026.0.1"] {
11727            let c = caixa_with_edicao(Some(ed));
11728            let err = c.validate_edicao().unwrap_err();
11729            assert!(
11730                matches!(err, ManifestError::EdicaoInvalid { .. }),
11731                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11732            );
11733        }
11734    }
11735
11736    #[test]
11737    fn validate_edicao_rejects_wrong_length_numeric() {
11738        // Wrong-length numeric footgun — `"26"` (truncated) /
11739        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
11740        // (zero-padded too wide) all parse as integers but don't
11741        // name a 4-digit year. The shape predicate refuses every
11742        // value whose length isn't exactly 4 bytes.
11743        for ed in ["26", "202", "20260", "00026", "9"] {
11744            let c = caixa_with_edicao(Some(ed));
11745            let err = c.validate_edicao().unwrap_err();
11746            assert!(
11747                matches!(err, ManifestError::EdicaoInvalid { .. }),
11748                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11749            );
11750        }
11751    }
11752
11753    #[test]
11754    fn validate_edicao_empty_takes_precedence_over_shape() {
11755        // Empty-first cascade pin: the empty `Some("")` surfaces
11756        // the narrower `EdicaoEmpty` not the shape-predicate-
11757        // wrapped `EdicaoInvalid`, mirroring the peer
11758        // `validate_repositorio_empty_takes_precedence_over_shape`
11759        // (`RepositorioEmpty` → `RepositorioInvalid`),
11760        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
11761        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
11762        // cascades. The shape predicate also refuses the empty
11763        // input (defensively — `s.len() != 4`), but the
11764        // manifest-layer empty arm runs first to surface the
11765        // narrower diagnostic verbatim.
11766        let c = caixa_with_edicao(Some(""));
11767        let err = c.validate_edicao().unwrap_err();
11768        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
11769    }
11770
11771    #[test]
11772    fn validate_edicao_template_passes() {
11773        // Round-trip pin: the bare `Caixa::template` shape (which
11774        // carries `:edicao "2026"` verbatim) passes the gate by
11775        // construction. A future template-shape change that
11776        // introduced `(:edicao "")` or a non-year value would
11777        // surface here as a regression. Mirrors the peer
11778        // `validate_licenca_template_passes` pin.
11779        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11780        c.validate_edicao().unwrap();
11781    }
11782
11783    #[test]
11784    fn validate_edicao_diagnostic_names_offending_slot() {
11785        // Diagnostic-shape pin (peer with
11786        // `validate_licenca_diagnostic_names_offending_slot`): the
11787        // error's Display surfaces the `:edicao` slot name verbatim,
11788        // so a `feira lint` run can render the diagnostic without
11789        // re-parsing and the author can grep their caixa.lisp for
11790        // the offending `:edicao` line.
11791        let c = caixa_with_edicao(Some(""));
11792        let rendered = c.validate_edicao().unwrap_err().to_string();
11793        assert!(
11794            rendered.contains(":edicao"),
11795            "diagnostic must name the offending slot: {rendered}",
11796        );
11797    }
11798
11799    #[test]
11800    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
11801        // Diagnostic-shape pin on the shape-predicate arm (peer
11802        // with `validate_repositorio_diagnostic_carries_offending_value`):
11803        // the error's Display surfaces the offending value + slot
11804        // name verbatim, so a `feira lint` run can render the
11805        // diagnostic without re-parsing and the author can grep
11806        // their caixa.lisp for the offending `:edicao` value.
11807        let c = caixa_with_edicao(Some("v2026"));
11808        let rendered = c.validate_edicao().unwrap_err().to_string();
11809        assert!(
11810            rendered.contains(":edicao"),
11811            "diagnostic must name the offending slot: {rendered}",
11812        );
11813        assert!(
11814            rendered.contains("v2026"),
11815            "diagnostic must quote the offending value: {rendered}",
11816        );
11817    }
11818
11819    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
11820
11821    #[test]
11822    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
11823        // The canonical per-`Caixa` `:edicao` language-edition scalar
11824        // pin: [`Caixa::edicao`] must return the `:edicao` typed
11825        // byte-string verbatim as an `Option<&str>`, byte-equal to the
11826        // raw `self.edicao.as_deref()` access across every representative
11827        // value in the accept-set — `None` (the "omit the slot to defer
11828        // to the substrate's default edition" arm every existing
11829        // [`caixa-resolver`] fixture without an `:edicao` line carries),
11830        // `Some("")` (a past-the-guard sentinel that pins the accessor
11831        // doesn't perform a silent `Some("") → None` collapse on the
11832        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
11833        // but the accessor must ship the raw slot verbatim so a
11834        // validate-time gate regression surfaces at any future edition-
11835        // aware consumer's boundary rather than being silently absorbed
11836        // into the substrate's default edition), `Some("2026")` (the
11837        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
11838        // template scaffolds via [`Caixa::template`] and every
11839        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
11840        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
11841        // carries by construction), `Some("2018")` / `Some("2021")` /
11842        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
11843        // peer with Cargo's `[package] edition` grammar every future-
11844        // introduced sibling to `"2026"` will follow), and eight
11845        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
11846        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
11847        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
11848        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
11849        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
11850        // length-numeric, `Some("latest")` free-form-non-year — the
11851        // sentinels pin the accessor doesn't silently absorb the
11852        // refusal cases into a substrate-default-edition fallback).
11853        //
11854        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
11855        // return scalar accessor pin on the substrate primitive —
11856        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
11857        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
11858        // (3f16e2f) pins that opened the "outer [`Caixa`]
11859        // `Option<&str>` scalar" projection pin pattern this pin folds
11860        // on. Sibling in shape to the peer per-`:placement`
11861        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11862        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11863        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11864        // axes, extended onto the outer top-level [`Caixa`] universal-
11865        // axis surface's last unlifted `Option<String>` slot. Pins
11866        // against a future silent detour that returned an owned
11867        // `Option<String>` (which would type-check but silently
11868        // allocate on every accessor call, breaking the zero-cost
11869        // projection every peer sibling accessor carries), a
11870        // `Some("") → None` collapse (which would silently absorb the
11871        // `EdicaoEmpty` refusal case at the accessor boundary and any
11872        // future edition-aware consumer would silently fall back to
11873        // the substrate's default edition on a struct-literal
11874        // `Caixa { edicao: Some(""), .. }`), or a
11875        // `None → Some("2026")` collapse (which would silently reify
11876        // the substrate's default edition at the accessor boundary
11877        // and every downstream consumer keying off the
11878        // `Option::is_none()` discriminator would lose the "author
11879        // omitted the slot" signal).
11880        for edicao in [
11881            None,
11882            Some(""),
11883            Some("2026"),
11884            Some("2018"),
11885            Some("2021"),
11886            Some("2024"),
11887            Some("2026 "),
11888            Some(" 2026"),
11889            Some("2026\n"),
11890            Some("2026"),
11891            Some("v2026"),
11892            Some("2026.1"),
11893            Some("26"),
11894            Some("latest"),
11895        ] {
11896            let c = caixa_with_edicao(edicao);
11897            assert_eq!(
11898                c.edicao(),
11899                edicao,
11900                "Caixa::edicao must return :edicao verbatim (got {:?}, \
11901                 expected {edicao:?})",
11902                c.edicao(),
11903            );
11904            assert_eq!(
11905                c.edicao(),
11906                c.edicao.as_deref(),
11907                "Caixa::edicao must byte-equal the raw \
11908                 `self.edicao.as_deref()` field access across every \
11909                 value in the Option<&str> accept-set",
11910            );
11911        }
11912    }
11913
11914    #[test]
11915    fn validate_edicao_empty_arm_routes_through_accessor() {
11916        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
11917        // must key off [`Caixa::edicao`], not the raw
11918        // `self.edicao.as_deref()` field access. Structurally: a
11919        // `Caixa { edicao: Some(""), .. }` must surface the
11920        // `EdicaoEmpty` refusal exactly, and a
11921        // `Caixa { edicao: Some("2026"), .. }` (the canonical
11922        // 4-digit-ASCII-decimal-year form) must pass validate. The
11923        // pair jointly pins the accessor + validate-gate composition:
11924        // any future silent detour that had the accessor return `None`
11925        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
11926        // would silently absorb the `EdicaoEmpty` refusal at the
11927        // accessor boundary and the validate gate would accept a
11928        // struct-literal `Caixa { edicao: Some(""), .. }` — the
11929        // composition pin catches that at caixa-core build time.
11930        //
11931        // Peer of the [`Caixa::licenca`] (6d5bc28)
11932        // `validate_licenca_empty_arm_routes_through_accessor`,
11933        // [`Caixa::repositorio`] (cc7332d)
11934        // `validate_repositorio_empty_arm_routes_through_accessor`,
11935        // and [`Caixa::descricao`] (3f16e2f)
11936        // `validate_descricao_empty_arm_routes_through_accessor`
11937        // composition pins on the sibling outer top-level [`Caixa`]
11938        // `Option<&str>` universal-axis surface — same "the validate /
11939        // shape-gate predicate must route through the substrate-
11940        // primitive typed dispatch" discipline extended onto the
11941        // fourth and final outer top-level [`Caixa`] universal-axis
11942        // `Option<&str>`-composition surface, closing the accessor-
11943        // composition family.
11944        let c = caixa_with_edicao(Some(""));
11945        assert!(
11946            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
11947            "validate_edicao must reject edicao == Some(\"\") with \
11948             EdicaoEmpty — the accessor and the validate gate must \
11949             route through the same substrate-primitive typed dispatch \
11950             on the :edicao empty arm",
11951        );
11952        let c = caixa_with_edicao(Some("2026"));
11953        assert!(
11954            c.validate_edicao().is_ok(),
11955            "validate_edicao must accept edicao == Some(\"2026\") \
11956             (the canonical 4-digit-ASCII-decimal-year shape)",
11957        );
11958    }
11959
11960    #[test]
11961    fn edicao_projects_option_str_by_borrow() {
11962        // The by-borrow pin: [`Caixa::edicao`] returns
11963        // `Option<&str>` by borrow — the `&str` borrows the underlying
11964        // `String` storage of the `Option<String>` slot and the
11965        // accessor must not allocate a fresh `String` on every call.
11966        // Peer of the [`Caixa::licenca`] (6d5bc28),
11967        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
11968        // (3f16e2f) by-borrow pins on the peer outer top-level
11969        // [`Caixa`] `Option<&str>`-return axes, and of the
11970        // per-`:placement`
11971        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11972        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
11973        // return axis, extended onto the fourth and final outer top-
11974        // level [`Caixa`] universal-axis `Option<&str>` shape — the
11975        // accessor's returned `&str` must borrow from `&self` (the
11976        // returned reference's lifetime is tied to `&self`), and
11977        // calling the accessor twice on the same [`Caixa`] must yield
11978        // the same `Option<&str>` verbatim (idempotent, no side
11979        // effects on `&self`).
11980        //
11981        // Pins against a future silent detour that returned an owned
11982        // `Option<String>` (which would type-check but silently
11983        // allocate on every call, breaking the zero-cost projection
11984        // every peer sibling accessor carries), or a one-arm-only
11985        // accessor that returned a saturating value on some sentinel
11986        // input (breaking the pass-through invariant the sibling
11987        // required-scalar accessors carry).
11988        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
11989            let c = caixa_with_edicao(edicao);
11990            let first = c.edicao();
11991            let second = c.edicao();
11992            assert_eq!(
11993                first, second,
11994                "Caixa::edicao must be idempotent — two successive \
11995                 calls on the same &self must return the same \
11996                 Option<&str>",
11997            );
11998            assert_eq!(
11999                first, edicao,
12000                "Caixa::edicao must return :edicao verbatim by \
12001                 borrow — got {first:?}, expected {edicao:?}",
12002            );
12003        }
12004    }
12005
12006    #[test]
12007    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
12008        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
12009        // label caixa-identity scalar pin: [`Caixa::nome`] must return
12010        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
12011        // the raw field access across every representative value in
12012        // the accept-set — the canonical `"demo"` template baseline
12013        // (the same `feira init`-scaffolded default the sibling
12014        // `validate_nome_accepts_canonical_template` positive-control
12015        // gate pins), plus every sibling per-typed-slot atom accessor's
12016        // canonical positive-arm byte-string (`"catalog"` per
12017        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
12018        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
12019        // `caixa-helm`/`caixa-flux` cross-crate integration-test
12020        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
12021        // canonical example), plus every past-the-guard sentinel for
12022        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
12023        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
12024        // the bare DNS-1123 63-byte cap but overflows the joint
12025        // `lareira-<nome>` chart-name budget the sibling
12026        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
12027        //
12028        // The past-the-guard sentinels pin the accessor doesn't
12029        // silently absorb the refusal cases into a template-derived
12030        // fallback (a future `.nome().is_empty().then(|| "demo")`
12031        // collapse would silently absorb the `NomeEmpty` refusal at
12032        // the accessor boundary and the validate gate would accept a
12033        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
12034        // catches that at caixa-core build time).
12035        //
12036        // First outer top-level [`Caixa`] `&str`-return required-
12037        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
12038        // required-scalar" projection pattern the sibling per-`Caixa`
12039        // `:versao` future lift folds on. Sibling in shape to the peer
12040        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
12041        // required-`String`-carry accessor pin on the sibling per-
12042        // sub-struct required-axis, extended onto the outer top-level
12043        // [`Caixa`] universal-axis required-`String`-carry axis.
12044        for nome in [
12045            "demo",
12046            "catalog",
12047            "cart",
12048            "hello-rio",
12049            "checkout",
12050            "",
12051            "Bad_Name",
12052            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
12053        ] {
12054            let c = caixa_with_nome(nome);
12055            assert_eq!(
12056                c.nome(),
12057                nome,
12058                "Caixa::nome must return :nome verbatim (got {}, \
12059                 expected {nome})",
12060                c.nome(),
12061            );
12062            assert_eq!(
12063                c.nome(),
12064                c.nome.as_str(),
12065                "Caixa::nome must byte-equal the raw .nome field \
12066                 access across every value in the String accept-set",
12067            );
12068        }
12069    }
12070
12071    #[test]
12072    fn validate_nome_empty_arm_routes_through_accessor() {
12073        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
12074        // key off [`Caixa::nome`], not the raw `.nome` field access.
12075        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
12076        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
12077        // template baseline (the peer positive-arm the sibling
12078        // `validate_nome_accepts_canonical_template` gate carves out)
12079        // must pass validate. The pair jointly pins the accessor +
12080        // validate-gate composition: any future silent detour that
12081        // had the accessor return a fresh `"demo"` on the empty arm
12082        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
12083        // would silently absorb the `NomeEmpty` refusal at the
12084        // accessor boundary and the validate gate would accept a
12085        // struct-literal `Caixa { nome: "".into(), .. }` — the
12086        // composition pin catches that at caixa-core build time.
12087        //
12088        // Peer of the sibling per-`Caixa`
12089        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
12090        // / `validate_repositorio_empty_arm_routes_through_accessor`
12091        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
12092        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
12093        // (2641cbd) composition pins on the sibling outer top-level
12094        // [`Caixa`] `Option<&str>` axes — same "the validate /
12095        // shape-gate predicate must route through the substrate-
12096        // primitive typed dispatch" discipline extended onto the peer
12097        // outer top-level [`Caixa`] required-`&str` composition axis.
12098        let c = caixa_with_nome("");
12099        assert!(
12100            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
12101            "validate_nome must reject nome == \"\" with NomeEmpty — \
12102             the accessor and the validate gate must route through the \
12103             same substrate-primitive typed dispatch on the :nome \
12104             empty-arm",
12105        );
12106        let c = caixa_with_nome("demo");
12107        assert!(
12108            c.validate_nome().is_ok(),
12109            "validate_nome must accept nome == \"demo\" (the canonical \
12110             DNS-1123-label template baseline)",
12111        );
12112    }
12113
12114    #[test]
12115    fn nome_projects_str_by_borrow() {
12116        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
12117        // — the `&str` borrows the underlying `String` storage of the
12118        // required `nome` slot and the accessor must not allocate a
12119        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
12120        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
12121        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
12122        // by-borrow pins on the peer outer top-level [`Caixa`]
12123        // `Option<&str>`-return axes, extended onto the first outer
12124        // top-level [`Caixa`] required-`&str`-return axis — the
12125        // accessor's returned `&str` must borrow from `&self` (the
12126        // returned reference's lifetime is tied to `&self`), and
12127        // calling the accessor twice on the same [`Caixa`] must yield
12128        // the same `&str` verbatim (idempotent, no side effects on
12129        // `&self`).
12130        //
12131        // Pins against a future silent detour that returned an owned
12132        // `String` (which would type-check but silently allocate on
12133        // every call, breaking the zero-cost projection every peer
12134        // sibling accessor carries), an accidental
12135        // `.nome.to_lowercase()` detour that returned a fresh
12136        // allocation through an already-DNS-1123-lowercase-only
12137        // string (breaking a future `const fn` regression), or a
12138        // one-arm-only accessor that returned a canonicalized value
12139        // on some sentinel input (breaking the pass-through invariant
12140        // the sibling required-scalar accessors carry).
12141        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
12142            let c = caixa_with_nome(nome);
12143            let first = c.nome();
12144            let second = c.nome();
12145            assert_eq!(
12146                first, second,
12147                "Caixa::nome must be idempotent — two successive calls \
12148                 on the same &self must return the same &str",
12149            );
12150            assert_eq!(
12151                first, nome,
12152                "Caixa::nome must return :nome verbatim by borrow — \
12153                 got {first}, expected {nome}",
12154            );
12155        }
12156    }
12157
12158    #[test]
12159    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
12160        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
12161        // pinned-version scalar pin: [`Caixa::versao`] must return the
12162        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
12163        // raw `.versao` field access across every representative value
12164        // in the accept-set — the canonical `"0.1.0"` template baseline
12165        // (the same `feira init`-scaffolded default the sibling
12166        // `validate_versao_accepts_canonical_template` positive-control
12167        // gate pins), plus every canonical SemVer-2 shape the sibling
12168        // `validate_versao_accepts_canonical_forms` positive-arm sweep
12169        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
12170        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
12171        // `"10.20.30"`), plus every past-the-guard sentinel for the
12172        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
12173        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
12174        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
12175        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
12176        // `"latest"` the docker-tag-shape footgun — the sentinels pin
12177        // the accessor doesn't silently absorb the refusal cases into a
12178        // template-derived fallback like `"0.1.0"`).
12179        //
12180        // The past-the-guard sentinels pin the accessor doesn't silently
12181        // absorb the refusal cases into a template-derived fallback (a
12182        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
12183        // silently absorb the `VersaoEmpty` refusal at the accessor
12184        // boundary and the validate gate would accept a struct-literal
12185        // `Caixa { versao: "".into(), .. }` — the pin catches that at
12186        // caixa-core build time).
12187        //
12188        // Second outer top-level [`Caixa`] `&str`-return required-scalar
12189        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
12190        // scalar" projection pattern the sibling per-`Caixa`
12191        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
12192        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
12193        // (4127bb6) / per-`:children`
12194        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
12195        // / per-`:upgrade-from`
12196        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
12197        // struct `:versao`-shaped `&str`-return accessor pins on the
12198        // sibling per-typed-slot version-carrier axes, extended onto the
12199        // second outer top-level [`Caixa`] universal-axis required-
12200        // `String`-carry axis so the two universal-axis identity-
12201        // carrying scalars every `defcaixa` form supplies (`:nome` +
12202        // `:versao`) share the same "one typed dispatch per axis" pin
12203        // discipline.
12204        for versao in [
12205            "0.1.0",
12206            "0.0.0",
12207            "1.0.0",
12208            "0.2.0-rc.1",
12209            "1.0.0-alpha.0",
12210            "1.0.0+build.42",
12211            "1.0.0-rc.1+build.42",
12212            "10.20.30",
12213            "",
12214            "v0.1.0",
12215            "0.1",
12216            "^0.1",
12217            "0.1.0.0",
12218            "latest",
12219        ] {
12220            let c = caixa_with_versao(versao);
12221            assert_eq!(
12222                c.versao(),
12223                versao,
12224                "Caixa::versao must return :versao verbatim (got {}, \
12225                 expected {versao})",
12226                c.versao(),
12227            );
12228            assert_eq!(
12229                c.versao(),
12230                c.versao.as_str(),
12231                "Caixa::versao must byte-equal the raw .versao field \
12232                 access across every value in the String accept-set",
12233            );
12234        }
12235    }
12236
12237    #[test]
12238    fn validate_versao_empty_arm_routes_through_accessor() {
12239        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
12240        // must key off [`Caixa::versao`], not the raw `.versao` field
12241        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
12242        // surface the `VersaoEmpty` refusal exactly, and the canonical
12243        // `"0.1.0"` template baseline (the peer positive-arm the sibling
12244        // `validate_versao_accepts_canonical_template` gate carves out)
12245        // must pass validate. The pair jointly pins the accessor +
12246        // validate-gate composition: any future silent detour that had
12247        // the accessor return a fresh `"0.1.0"` on the empty arm
12248        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
12249        // would silently absorb the `VersaoEmpty` refusal at the
12250        // accessor boundary and the validate gate would accept a
12251        // struct-literal `Caixa { versao: "".into(), .. }` — the
12252        // composition pin catches that at caixa-core build time.
12253        //
12254        // Peer of the sibling per-`Caixa`
12255        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
12256        // composition pin on the sibling outer top-level [`Caixa`]
12257        // required-`&str` universal-axis surface — same "the validate /
12258        // shape-gate predicate must route through the substrate-
12259        // primitive typed dispatch" discipline extended onto the peer
12260        // outer top-level [`Caixa`] required-`&str` universal-axis
12261        // pinned-version composition axis, closing the second
12262        // coordinate of the "one canonical typed dispatch per per-Caixa
12263        // required-`&str` universal-axis" discipline.
12264        let c = caixa_with_versao("");
12265        assert!(
12266            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
12267            "validate_versao must reject versao == \"\" with VersaoEmpty — \
12268             the accessor and the validate gate must route through the \
12269             same substrate-primitive typed dispatch on the :versao \
12270             empty-arm",
12271        );
12272        let c = caixa_with_versao("0.1.0");
12273        assert!(
12274            c.validate_versao().is_ok(),
12275            "validate_versao must accept versao == \"0.1.0\" (the \
12276             canonical SemVer-2 template baseline)",
12277        );
12278    }
12279
12280    #[test]
12281    fn versao_projects_str_by_borrow() {
12282        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
12283        // — the `&str` borrows the underlying `String` storage of the
12284        // required `versao` slot and the accessor must not allocate a
12285        // fresh `String` on every call. Peer of the [`Caixa::nome`]
12286        // (e6b7d97) by-borrow pin on the sibling outer top-level
12287        // [`Caixa`] required-`&str`-return axis, extended onto the
12288        // second outer top-level [`Caixa`] required-`&str`-return
12289        // universal-axis pinned-version surface — the accessor's
12290        // returned `&str` must borrow from `&self` (the returned
12291        // reference's lifetime is tied to `&self`), and calling the
12292        // accessor twice on the same [`Caixa`] must yield the same
12293        // `&str` verbatim (idempotent, no side effects on `&self`).
12294        //
12295        // Pins against a future silent detour that returned an owned
12296        // `String` (which would type-check but silently allocate on
12297        // every call, breaking the zero-cost projection every peer
12298        // sibling accessor carries), an accidental
12299        // `semver::Version::parse(&self.versao).unwrap().to_string()`
12300        // detour that returned a canonicalized fresh allocation through
12301        // an already-canonical byte-string (breaking a future `const fn`
12302        // regression and silently absorbing the `VersaoInvalid` refusal
12303        // at the accessor boundary), or a one-arm-only accessor that
12304        // returned a canonicalized value on some sentinel input
12305        // (breaking the pass-through invariant the sibling required-
12306        // scalar accessors carry).
12307        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
12308            let c = caixa_with_versao(versao);
12309            let first = c.versao();
12310            let second = c.versao();
12311            assert_eq!(
12312                first, second,
12313                "Caixa::versao must be idempotent — two successive \
12314                 calls on the same &self must return the same &str",
12315            );
12316            assert_eq!(
12317                first, versao,
12318                "Caixa::versao must return :versao verbatim by borrow \
12319                 — got {first}, expected {versao}",
12320            );
12321        }
12322    }
12323
12324    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
12325        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12326        c.kind = kind;
12327        c
12328    }
12329
12330    #[test]
12331    fn kind_returns_kind_variant_verbatim_across_permutations() {
12332        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
12333        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
12334        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
12335        // the raw `.kind` field access across every variant in the
12336        // closed accept-set (`Biblioteca` — the library kind that
12337        // exports lisp forms; `Binario` — the nix-built executable kind
12338        // under `exe/`; `Servico` — the wasm-component daemon kind
12339        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
12340        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
12341        // composition kind).
12342        //
12343        // Pins against a future silent detour that re-derived the kind
12344        // from a peer axis (an accidental fallback to
12345        // `if !servicos.is_empty() { Servico } else if
12346        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
12347        // collapse that read the code-surface / mesh-slot columns into
12348        // the kind discriminator), a variant remap the operator
12349        // authors on one consumer without the other, or a stale-derive
12350        // detour that substituted [`CaixaKind::Biblioteca`] as the
12351        // default when the field held any other variant (which would
12352        // silently collapse the distinction between "author explicitly
12353        // declared `:kind Servico`" and "author declared any other
12354        // kind" every downstream renderer-dispatch site depends on).
12355        //
12356        // First outer top-level [`Caixa`] `Copy`-return required-enum-
12357        // discriminant accessor pin — opens the "outer [`Caixa`]
12358        // `Copy`-return required-discriminant" projection pattern.
12359        // Sibling in shape to the peer per-`:supervisor`
12360        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
12361        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
12362        // (921fe1b), and per-`:children`
12363        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
12364        // `Copy`-return closed-set-enum discriminant accessor pins on
12365        // the sibling nested-spec typed-slot discriminator axes,
12366        // extended here to the outer top-level [`Caixa`] universal-
12367        // axis surface.
12368        for kind in [
12369            CaixaKind::Biblioteca,
12370            CaixaKind::Binario,
12371            CaixaKind::Servico,
12372            CaixaKind::Supervisor,
12373            CaixaKind::Aplicacao,
12374        ] {
12375            let c = caixa_with_kind(kind);
12376            assert_eq!(
12377                c.kind(),
12378                kind,
12379                "Caixa::kind must return :kind verbatim (got {:?}, \
12380                 expected {kind:?})",
12381                c.kind(),
12382            );
12383            assert_eq!(
12384                c.kind(),
12385                c.kind,
12386                "Caixa::kind accessor and .kind field access must \
12387                 byte-equal — the accessor is the substrate-primitive \
12388                 typed dispatch every downstream kind-gate consumer \
12389                 must route through",
12390            );
12391        }
12392    }
12393
12394    #[test]
12395    fn require_kind_reads_through_lifted_kind_accessor() {
12396        // Two-consumer coherence pin: the [`crate::render::require_kind`]
12397        // entry-gate predicate (the canonical two-line
12398        // `require_kind(caixa, Servico)?` prelude every per-Servico /
12399        // per-Aplicacao renderer runs at its entry-point) and the
12400        // sibling [`crate::render::KindMismatch`] error carrier's
12401        // `actual:` field (which names the offending caixa's variant
12402        // in the diagnostic) must both key off the lifted accessor, so
12403        // any future rebrand on the typed slot's reader shape lands at
12404        // exactly one place. Pins the two-site coherence by exercising
12405        // every off-diagonal `(actual, expected)` pair across the
12406        // closed accept-set — the `KindMismatch { actual, expected }`
12407        // surfaced on the mismatch arm must byte-equal the pair the
12408        // accessor returns for each side.
12409        //
12410        // Peer of the sibling per-`:placement`
12411        // `validate_placement_reads_through_lifted_estrategia_accessor`
12412        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
12413        // `Copy`-return discriminant axis — same "the entry-gate
12414        // predicate and the error carrier's `actual:` field must route
12415        // through the substrate-primitive typed dispatch" discipline
12416        // extended onto the outer top-level [`Caixa`] universal-axis
12417        // discriminant surface.
12418        for expected in [
12419            CaixaKind::Biblioteca,
12420            CaixaKind::Binario,
12421            CaixaKind::Servico,
12422            CaixaKind::Supervisor,
12423            CaixaKind::Aplicacao,
12424        ] {
12425            for actual in [
12426                CaixaKind::Biblioteca,
12427                CaixaKind::Binario,
12428                CaixaKind::Servico,
12429                CaixaKind::Supervisor,
12430                CaixaKind::Aplicacao,
12431            ] {
12432                let c = caixa_with_kind(actual);
12433                let result = crate::render::require_kind(&c, expected);
12434                if expected == actual {
12435                    assert!(
12436                        result.is_ok(),
12437                        "require_kind must accept when actual == expected \
12438                         (actual={actual:?}, expected={expected:?})",
12439                    );
12440                } else {
12441                    let err = result.expect_err("require_kind must reject when actual != expected");
12442                    assert_eq!(
12443                        err.actual,
12444                        c.kind(),
12445                        "KindMismatch.actual must byte-equal Caixa::kind() \
12446                         — the error carrier's `actual:` field reads \
12447                         through the lifted accessor",
12448                    );
12449                    assert_eq!(
12450                        err.expected, expected,
12451                        "KindMismatch.expected must byte-equal the \
12452                         expected variant passed to require_kind",
12453                    );
12454                }
12455            }
12456        }
12457    }
12458
12459    #[test]
12460    fn aplicacao_view_kind_gate_routes_through_accessor() {
12461        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
12462        // must key off [`Caixa::kind`], not the raw `.kind` field
12463        // access. Structurally: a `Caixa { kind: X, .. }` for any
12464        // non-`Aplicacao` variant must fold to `None` on the
12465        // `aplicacao_view` composer (the "kind mismatch → no typed
12466        // view" contract every downstream Aplicacao consumer keys off
12467        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
12468        // `Some(_)`. The pair jointly pins the accessor + view-gate
12469        // composition: any future silent detour that had the accessor
12470        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
12471        // input would silently absorb the kind-mismatch case at the
12472        // accessor boundary and every per-Aplicacao renderer would
12473        // silently render a non-Aplicacao caixa's mesh slots — the
12474        // composition pin catches that at caixa-core build time.
12475        //
12476        // Peer of the sibling per-`Caixa`
12477        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
12478        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
12479        // composition pins on the sibling outer top-level [`Caixa`]
12480        // required-`&str` universal-axis surfaces — same "the
12481        // composer / validate gate must route through the substrate-
12482        // primitive typed dispatch" discipline extended onto the
12483        // outer top-level [`Caixa`] `Copy`-return required-
12484        // discriminant composition axis.
12485        for kind in [
12486            CaixaKind::Biblioteca,
12487            CaixaKind::Binario,
12488            CaixaKind::Servico,
12489            CaixaKind::Supervisor,
12490        ] {
12491            let c = caixa_with_kind(kind);
12492            assert!(
12493                c.aplicacao_view().is_none(),
12494                "aplicacao_view must return None on non-Aplicacao \
12495                 kind {kind:?} — the composer's kind-gate must route \
12496                 through Caixa::kind()",
12497            );
12498        }
12499        let c = caixa_with_kind(CaixaKind::Aplicacao);
12500        assert!(
12501            c.aplicacao_view().is_some(),
12502            "aplicacao_view must return Some on kind Aplicacao — \
12503             the composer's kind-gate must accept the matching arm \
12504             through Caixa::kind()",
12505        );
12506    }
12507
12508    #[test]
12509    fn supervisor_view_kind_gate_routes_through_accessor() {
12510        // Composition pin (mirror of the sibling
12511        // `aplicacao_view_kind_gate_routes_through_accessor` on the
12512        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
12513        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
12514        // field access. A `Caixa { kind: X, .. }` for any non-
12515        // `Supervisor` variant must fold to `None` on the
12516        // `supervisor_view` composer, and a `Caixa { kind:
12517        // Supervisor, .. }` must fold to `Some(_)`. Same peer
12518        // composition pin discipline on the second `_view` composer
12519        // axis.
12520        for kind in [
12521            CaixaKind::Biblioteca,
12522            CaixaKind::Binario,
12523            CaixaKind::Servico,
12524            CaixaKind::Aplicacao,
12525        ] {
12526            let c = caixa_with_kind(kind);
12527            assert!(
12528                c.supervisor_view().is_none(),
12529                "supervisor_view must return None on non-Supervisor \
12530                 kind {kind:?} — the composer's kind-gate must route \
12531                 through Caixa::kind()",
12532            );
12533        }
12534        let mut c = caixa_with_kind(CaixaKind::Supervisor);
12535        // A Supervisor caixa needs a strategy + at least one child to
12536        // fold to a Some(_) that also validates; the composer itself
12537        // requires only the kind arm, so bare kind flip is enough to
12538        // pin the `Some(_)` return, but we populate the minimum
12539        // supervisor shape so a future strengthening of the composer
12540        // to reject an empty spec doesn't false-positive this pin.
12541        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
12542        c.children = vec![crate::supervisor::ChildSpec {
12543            caixa: "child".into(),
12544            versao: "^0.1".into(),
12545            restart: crate::supervisor::RestartPolicy::Permanent,
12546        }];
12547        assert!(
12548            c.supervisor_view().is_some(),
12549            "supervisor_view must return Some on kind Supervisor — \
12550             the composer's kind-gate must accept the matching arm \
12551             through Caixa::kind()",
12552        );
12553    }
12554
12555    #[test]
12556    fn kind_projects_by_copy() {
12557        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
12558        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
12559        // `&self` (the returned value is owned, `Copy`-projected from
12560        // the underlying [`CaixaKind`] storage; two calls on the same
12561        // [`Caixa`] must yield byte-equal values). Peer of the peer
12562        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
12563        // `SupervisorSpec::estrategia` / per-`:children`
12564        // `ChildSpec::restart` `Copy`-return discriminant accessor
12565        // pins on the sibling nested-spec typed-slot discriminator
12566        // axes, extended onto the first outer top-level [`Caixa`]
12567        // required-`Copy`-return axis — pins against a future silent
12568        // detour that returned `&CaixaKind` (which would type-check
12569        // but silently constrain every consumer's callsite to a
12570        // borrow-shaped dispatch, breaking the zero-cost `Copy`
12571        // projection every peer sibling accessor carries).
12572        for kind in [
12573            CaixaKind::Biblioteca,
12574            CaixaKind::Binario,
12575            CaixaKind::Servico,
12576            CaixaKind::Supervisor,
12577            CaixaKind::Aplicacao,
12578        ] {
12579            let c = caixa_with_kind(kind);
12580            let first: CaixaKind = c.kind();
12581            let second: CaixaKind = c.kind();
12582            assert_eq!(
12583                first, second,
12584                "Caixa::kind must be idempotent — two successive \
12585                 calls on the same &self must return the same \
12586                 CaixaKind variant",
12587            );
12588            assert_eq!(
12589                first, kind,
12590                "Caixa::kind must return :kind verbatim by Copy — \
12591                 got {first:?}, expected {kind:?}",
12592            );
12593        }
12594    }
12595
12596    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
12597
12598    #[test]
12599    fn autores_returns_autores_slice_verbatim_across_permutations() {
12600        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
12601        // name-list slice pin: [`Caixa::autores`] must return the
12602        // `:autores` typed [`Vec<String>`] list verbatim as a
12603        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
12604        // access across every representative value in the accept-set —
12605        // `[]` (the "no maintainers declared" arm every existing
12606        // fixture without an `:autores` line carries), `[""]` (a past-
12607        // the-guard sentinel that pins the accessor doesn't perform a
12608        // silent `[""] → []` collapse on the empty-entry arm — validate
12609        // rejects `[""]` through `AutorEmpty` but the accessor must
12610        // ship the raw slot verbatim so a validate-time gate regression
12611        // surfaces at the caixa-helm emit boundary rather than being
12612        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
12613        // canonical single-maintainer form every `feira init` template
12614        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
12615        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
12616        // (the canonical RFC-5322 `<name> <email>` form the
12617        // `is_chart_maintainer_name_shape` predicate accepts), and
12618        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
12619        // sentinel — validate rejects through `AutorDuplicate` but the
12620        // accessor must ship the raw slot verbatim).
12621        //
12622        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
12623        // pin on the substrate primitive — opens the "outer [`Caixa`]
12624        // `&[T]` slice" projection pattern the sibling per-`Caixa`
12625        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
12626        // / `:servicos` / `:upgrade-from` / `:children` future lifts
12627        // fold on. Sibling in shape to the peer per-`:supervisor`
12628        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
12629        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
12630        // (a6e18d7), per-`:membros`
12631        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
12632        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
12633        // (0dcc926), and per-`:upgrade-from :instructions`
12634        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
12635        // `&[T]`-return slice accessor pins on the sibling per-M2 /
12636        // per-M3 typed-slot list axes, extended onto the outer top-
12637        // level [`Caixa`] universal-axis surface. Pins against a future
12638        // silent detour that returned an owned `Vec<String>` (which
12639        // would type-check but silently clone on every accessor call,
12640        // breaking the zero-cost projection every peer sibling slice
12641        // accessor carries), a `[""] → []` collapse (which would
12642        // silently absorb the `AutorEmpty` refusal case at the accessor
12643        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
12644        // would silently absorb the `AutorDuplicate` refusal case at
12645        // the accessor boundary and the caixa-helm `maintainers:` fold
12646        // would silently render a dedupped list on a struct-literal
12647        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
12648        for autores in [
12649            vec![],
12650            vec![""],
12651            vec!["pleme-io"],
12652            vec!["alice", "bob"],
12653            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
12654            vec!["pleme-io", "pleme-io"],
12655        ] {
12656            let c = caixa_with_autores(autores.clone());
12657            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
12658            assert_eq!(
12659                c.autores(),
12660                expected.as_slice(),
12661                "Caixa::autores must return :autores verbatim (got {:?}, \
12662                 expected {expected:?})",
12663                c.autores(),
12664            );
12665            assert_eq!(
12666                c.autores(),
12667                c.autores.as_slice(),
12668                "Caixa::autores must byte-equal the raw \
12669                 `self.autores.as_slice()` field access across every \
12670                 value in the Vec<String> accept-set",
12671            );
12672        }
12673    }
12674
12675    #[test]
12676    fn validate_autores_empty_entry_arm_routes_through_accessor() {
12677        // Composition pin: [`Caixa::validate_autores`]'s per-entry
12678        // empty-arm gate must key off [`Caixa::autores`], not the raw
12679        // `&self.autores` field-borrow walk. Structurally: a
12680        // `Caixa { autores: vec!["".into()], .. }` must surface the
12681        // `AutorEmpty` refusal exactly, and a
12682        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
12683        // canonical single-maintainer form) must pass validate. The
12684        // pair jointly pins the accessor + validate-gate composition:
12685        // any future silent detour that had the accessor return an
12686        // empty slice on the `[""]` arm (a
12687        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
12688        // would silently absorb the `AutorEmpty` refusal at the
12689        // accessor boundary and the validate gate would accept a
12690        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
12691        // the composition pin catches that at caixa-core build time.
12692        //
12693        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
12694        // accessor-composition pin
12695        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
12696        // sibling `Option<&str>`-composition axis and the
12697        // per-`:politicas :circuit-breaker`
12698        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
12699        // accessor-composition pin
12700        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
12701        // on the sibling required-`u32`-composition axis — same "the
12702        // validate / shape-gate predicate must route through the
12703        // substrate-primitive typed dispatch" discipline extended onto
12704        // the outer top-level [`Caixa`] universal-axis `&[T]`-
12705        // composition surface.
12706        let c = caixa_with_autores(vec![""]);
12707        assert!(
12708            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
12709            "validate_autores must reject autores == vec![\"\"] with \
12710             AutorEmpty — the accessor and the validate gate must \
12711             route through the same substrate-primitive typed dispatch \
12712             on the :autores per-entry empty arm",
12713        );
12714        let c = caixa_with_autores(vec!["pleme-io"]);
12715        assert!(
12716            c.validate_autores().is_ok(),
12717            "validate_autores must accept autores == vec![\"pleme-io\"] \
12718             (the canonical single-maintainer shape every `feira init` \
12719             template scaffolds)",
12720        );
12721    }
12722
12723    #[test]
12724    fn autores_projects_slice_by_borrow() {
12725        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
12726        // borrow — the returned slice borrows the underlying
12727        // `Vec<String>` storage of the `:autores` slot and the
12728        // accessor must not clone the backing `Vec` on every call.
12729        // Peer of the per-`:membros`
12730        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
12731        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
12732        // (0dcc926) / per-`:placement`
12733        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
12734        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
12735        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
12736        // typed-slot `&[T]`-return axes, extended onto the outer top-
12737        // level [`Caixa`] universal-axis `&[String]` shape — the
12738        // accessor's returned slice must borrow from `&self` (the
12739        // returned reference's lifetime is tied to `&self`), and
12740        // calling the accessor twice on the same [`Caixa`] must yield
12741        // slices that are pointer-equal (the underlying byte-buffer is
12742        // the storage `Vec`'s allocation, not a fresh copy) as well as
12743        // value-equal (idempotent, no side effects on `&self`).
12744        //
12745        // Pins against a future silent detour that returned an owned
12746        // `Vec<String>` (which would type-check but silently clone on
12747        // every call, breaking the zero-cost projection every peer
12748        // sibling slice accessor carries), a `&Vec<String>` return
12749        // (which would leak the backing `Vec`'s grow/push/reserve
12750        // surface no downstream consumer reaches for), or a one-arm-
12751        // only accessor that returned a saturating value on some
12752        // sentinel input (breaking the pass-through invariant the
12753        // sibling slice accessors carry).
12754        for autores in [
12755            vec![],
12756            vec!["pleme-io"],
12757            vec!["alice", "bob"],
12758            vec!["pleme-io", "pleme-io"],
12759        ] {
12760            let c = caixa_with_autores(autores.clone());
12761            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
12762            let first = c.autores();
12763            let second = c.autores();
12764            assert_eq!(
12765                first, second,
12766                "Caixa::autores must be idempotent — two successive \
12767                 calls on the same &self must return the same \
12768                 &[String]",
12769            );
12770            assert_eq!(
12771                first.as_ptr(),
12772                second.as_ptr(),
12773                "Caixa::autores must borrow the underlying Vec<String> \
12774                 storage — two successive calls must return slices \
12775                 with the same backing pointer (a fresh Vec<String> \
12776                 clone would change the pointer on every call)",
12777            );
12778            assert_eq!(
12779                first,
12780                expected.as_slice(),
12781                "Caixa::autores must return :autores verbatim by \
12782                 borrow — got {first:?}, expected {expected:?}",
12783            );
12784        }
12785    }
12786
12787    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
12788
12789    #[test]
12790    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
12791        // The canonical per-`Caixa` `:etiquetas` universal-axis
12792        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
12793        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
12794        // as a `&[String]`, byte-equal to the raw
12795        // `self.etiquetas.as_slice()` access across every representative
12796        // value in the accept-set — `[]` (the "no tags declared" arm
12797        // every existing fixture without an `:etiquetas` line carries),
12798        // `[""]` (a past-the-guard sentinel that pins the accessor
12799        // doesn't perform a silent `[""] → []` collapse on the empty-
12800        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
12801        // but the accessor must ship the raw slot verbatim so a
12802        // validate-time gate regression surfaces at the caixa-helm emit
12803        // boundary rather than being silently absorbed into a keyword-
12804        // drop), `["demo"]` (the canonical single-tag form every
12805        // `feira init` template scaffolds), `["example", "aplicacao",
12806        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
12807        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
12808        // (a past-the-guard duplicate sentinel — validate rejects
12809        // through `EtiquetaDuplicate` but the accessor must ship the
12810        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
12811        // at chart-render time isn't silently promoted into the
12812        // accessor boundary and struct-literal
12813        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
12814        // fixtures continue to expose the duplicate at the accessor).
12815        //
12816        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
12817        // pin on the substrate primitive — folds on the "outer
12818        // [`Caixa`] `&[T]` slice" projection pattern
12819        // `autores_returns_autores_slice_verbatim_across_permutations`
12820        // (b5d813f) opened, sibling in shape and idiom. Pins against a
12821        // future silent detour that returned an owned `Vec<String>`
12822        // (which would type-check but silently clone on every accessor
12823        // call, breaking the zero-cost projection every peer sibling
12824        // slice accessor carries), a `[""] → []` collapse (which would
12825        // silently absorb the `EtiquetaEmpty` refusal case at the
12826        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
12827        // (which would silently absorb the `EtiquetaDuplicate` refusal
12828        // case at the accessor boundary — the caixa-helm chart-render
12829        // `BTreeSet::collect` dedup is downstream of the accessor and
12830        // must not be silently promoted into it).
12831        for etiquetas in [
12832            vec![],
12833            vec![""],
12834            vec!["demo"],
12835            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
12836            vec!["demo", "demo"],
12837        ] {
12838            let c = caixa_with_etiquetas(etiquetas.clone());
12839            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
12840            assert_eq!(
12841                c.etiquetas(),
12842                expected.as_slice(),
12843                "Caixa::etiquetas must return :etiquetas verbatim (got \
12844                 {:?}, expected {expected:?})",
12845                c.etiquetas(),
12846            );
12847            assert_eq!(
12848                c.etiquetas(),
12849                c.etiquetas.as_slice(),
12850                "Caixa::etiquetas must byte-equal the raw \
12851                 `self.etiquetas.as_slice()` field access across every \
12852                 value in the Vec<String> accept-set",
12853            );
12854        }
12855    }
12856
12857    #[test]
12858    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
12859        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
12860        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
12861        // `&self.etiquetas` field-borrow walk. Structurally: a
12862        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
12863        // `EtiquetaEmpty` refusal exactly, and a
12864        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
12865        // single-tag form) must pass validate. The pair jointly pins
12866        // the accessor + validate-gate composition: any future silent
12867        // detour that had the accessor return an empty slice on the
12868        // `[""]` arm (a
12869        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
12870        // silently absorb the `EtiquetaEmpty` refusal at the accessor
12871        // boundary and the validate gate would accept a struct-literal
12872        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
12873        // pin catches that at caixa-core build time.
12874        //
12875        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
12876        // through_accessor` (b5d813f) accessor-composition pin on the
12877        // sibling `&[T]`-composition axis — same "the validate / shape-
12878        // gate predicate must route through the substrate-primitive
12879        // typed dispatch" discipline extended onto the sibling outer
12880        // top-level [`Caixa`] `&[T]`-composition surface.
12881        let c = caixa_with_etiquetas(vec![""]);
12882        assert!(
12883            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
12884            "validate_etiquetas must reject etiquetas == vec![\"\"] \
12885             with EtiquetaEmpty — the accessor and the validate gate \
12886             must route through the same substrate-primitive typed \
12887             dispatch on the :etiquetas per-entry empty arm",
12888        );
12889        let c = caixa_with_etiquetas(vec!["demo"]);
12890        assert!(
12891            c.validate_etiquetas().is_ok(),
12892            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
12893             (the canonical single-tag shape every `feira init` \
12894             template scaffolds)",
12895        );
12896    }
12897
12898    #[test]
12899    fn etiquetas_projects_slice_by_borrow() {
12900        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
12901        // by borrow — the returned slice borrows the underlying
12902        // `Vec<String>` storage of the `:etiquetas` slot and the
12903        // accessor must not clone the backing `Vec` on every call.
12904        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12905        // (b5d813f) by-borrow pin on the sibling outer top-level
12906        // [`Caixa`] `&[String]`-return axis — the accessor's returned
12907        // slice must borrow from `&self` (the returned reference's
12908        // lifetime is tied to `&self`), and calling the accessor twice
12909        // on the same [`Caixa`] must yield slices that are pointer-
12910        // equal (the underlying byte-buffer is the storage `Vec`'s
12911        // allocation, not a fresh copy) as well as value-equal
12912        // (idempotent, no side effects on `&self`).
12913        //
12914        // Pins against a future silent detour that returned an owned
12915        // `Vec<String>` (which would type-check but silently clone on
12916        // every call, breaking the zero-cost projection every peer
12917        // sibling slice accessor carries), a `&Vec<String>` return
12918        // (which would leak the backing `Vec`'s grow/push/reserve
12919        // surface no downstream consumer reaches for), or a one-arm-
12920        // only accessor that returned a saturating value on some
12921        // sentinel input (breaking the pass-through invariant the
12922        // sibling slice accessors carry).
12923        for etiquetas in [
12924            vec![],
12925            vec!["demo"],
12926            vec!["example", "aplicacao", "mesh"],
12927            vec!["demo", "demo"],
12928        ] {
12929            let c = caixa_with_etiquetas(etiquetas.clone());
12930            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
12931            let first = c.etiquetas();
12932            let second = c.etiquetas();
12933            assert_eq!(
12934                first, second,
12935                "Caixa::etiquetas must be idempotent — two successive \
12936                 calls on the same &self must return the same \
12937                 &[String]",
12938            );
12939            assert_eq!(
12940                first.as_ptr(),
12941                second.as_ptr(),
12942                "Caixa::etiquetas must borrow the underlying \
12943                 Vec<String> storage — two successive calls must \
12944                 return slices with the same backing pointer (a fresh \
12945                 Vec<String> clone would change the pointer on every \
12946                 call)",
12947            );
12948            assert_eq!(
12949                first,
12950                expected.as_slice(),
12951                "Caixa::etiquetas must return :etiquetas verbatim by \
12952                 borrow — got {first:?}, expected {expected:?}",
12953            );
12954        }
12955    }
12956
12957    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
12958
12959    #[test]
12960    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
12961        // The canonical per-`Caixa` `:bibliotecas` universal-axis
12962        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
12963        // must return the `:bibliotecas` typed [`Vec<String>`] list
12964        // verbatim as a `&[String]`, byte-equal to the raw
12965        // `self.bibliotecas.as_slice()` access across every
12966        // representative value in the accept-set — `[]` (the "no
12967        // libraries declared" arm every `:kind` other than `Biblioteca`
12968        // + every `Biblioteca` relying on the canonical
12969        // `lib/<nome>.lisp` implicit-default path carries; the
12970        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
12971        // fires exactly on this empty-slot + `Biblioteca`-kind
12972        // combination), `[""]` (a past-the-guard sentinel that pins
12973        // the accessor doesn't perform a silent `[""] → []` collapse
12974        // on the empty-entry arm — validate rejects `[""]` through
12975        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
12976        // must ship the raw slot verbatim so a validate-time gate
12977        // regression surfaces at the `feira build` phase-1 parse
12978        // boundary rather than being silently absorbed into a
12979        // library-drop), `["lib/demo.lisp"]` (the canonical single-
12980        // entry form `Caixa::template` scaffolds and every `feira init`
12981        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
12982        // (the canonical multi-library form the
12983        // `validate_code_paths_accepts_explicit_relative_paths_on_
12984        // every_slot` fixture emits), and `["lib/foo.lisp",
12985        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
12986        // validate rejects through `CodePathDuplicate { slot:
12987        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
12988        // but the accessor must ship the raw slot verbatim so the
12989        // `feira build` `for entry in caixa.bibliotecas()` parse walk
12990        // sees the duplicate at the accessor boundary and struct-
12991        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
12992        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
12993        // the duplicate at the accessor).
12994        //
12995        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
12996        // pin on the substrate primitive — folds on the "outer
12997        // [`Caixa`] `&[T]` slice" projection pattern
12998        // `autores_returns_autores_slice_verbatim_across_permutations`
12999        // (b5d813f) opened and
13000        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13001        // (78c7d3c) folded on, sibling in shape and idiom. Pins
13002        // against a future silent detour that returned an owned
13003        // `Vec<String>` (which would type-check but silently clone on
13004        // every accessor call, breaking the zero-cost projection
13005        // every peer sibling slice accessor carries), a `[""] → []`
13006        // collapse (which would silently absorb the `CodePathEmpty`
13007        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
13008        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
13009        // would silently absorb the `CodePathDuplicate` refusal case
13010        // at the accessor boundary — the per-slot set-not-multiset
13011        // gate is downstream of the accessor and must not be silently
13012        // promoted into it).
13013        for bibliotecas in [
13014            vec![],
13015            vec![""],
13016            vec!["lib/demo.lisp"],
13017            vec!["lib/demo.lisp", "lib/helpers.lisp"],
13018            vec!["lib/foo.lisp", "lib/foo.lisp"],
13019        ] {
13020            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
13021            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
13022            assert_eq!(
13023                c.bibliotecas(),
13024                expected.as_slice(),
13025                "Caixa::bibliotecas must return :bibliotecas verbatim \
13026                 (got {:?}, expected {expected:?})",
13027                c.bibliotecas(),
13028            );
13029            assert_eq!(
13030                c.bibliotecas(),
13031                c.bibliotecas.as_slice(),
13032                "Caixa::bibliotecas must byte-equal the raw \
13033                 `self.bibliotecas.as_slice()` field access across \
13034                 every value in the Vec<String> accept-set",
13035            );
13036        }
13037    }
13038
13039    #[test]
13040    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
13041        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
13042        // empty-arm gate on the `:bibliotecas` slot must key off
13043        // [`Caixa::bibliotecas`], not a divergent raw
13044        // `&self.bibliotecas` field-borrow walk. Structurally: a
13045        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
13046        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
13047        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
13048        // into()], .. }` (the canonical single-library form
13049        // `Caixa::template` scaffolds) must pass validate. The pair
13050        // jointly pins the accessor + validate-gate composition: any
13051        // future silent detour that had the accessor return an empty
13052        // slice on the `[""]` arm (a `.iter().filter(|s|
13053        // !s.is_empty()).collect()` collapse) would silently absorb
13054        // the `CodePathEmpty` refusal at the accessor boundary and
13055        // the validate gate would accept a struct-literal
13056        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
13057        // composition pin catches that at caixa-core build time.
13058        //
13059        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
13060        // through_accessor` (b5d813f) and
13061        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13062        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
13063        // composition axes — same "the validate / shape-gate
13064        // predicate must route through the substrate-primitive typed
13065        // dispatch" discipline extended onto the sibling outer top-
13066        // level [`Caixa`] `&[T]`-composition surface. Nominally the
13067        // in-tree `validate_code_paths` production body still keys
13068        // off the internal `[(":bibliotecas", &self.bibliotecas,
13069        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
13070        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
13071        // (the tuple's homogeneous slice-typed shape blocks a per-
13072        // element accessor swap in isolation — a future companion
13073        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
13074        // `&[T]` slice-accessor axis closes that tuple onto the
13075        // triple of typed dispatches as a unit); the composition pin
13076        // catches any future accessor-side silent filter drop against
13077        // that eventual tuple-closure regardless of whether the
13078        // `:bibliotecas` slot is threaded through the accessor or the
13079        // raw field access at the tuple's construction site.
13080        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
13081        assert!(
13082            matches!(
13083                c.validate_code_paths(),
13084                Err(ManifestError::CodePathEmpty {
13085                    slot: ":bibliotecas"
13086                })
13087            ),
13088            "validate_code_paths must reject bibliotecas == vec![\"\"] \
13089             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
13090             accessor and the validate gate must route through the \
13091             same substrate-primitive typed dispatch on the \
13092             :bibliotecas per-entry empty arm",
13093        );
13094        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
13095        assert!(
13096            c.validate_code_paths().is_ok(),
13097            "validate_code_paths must accept bibliotecas == \
13098             vec![\"lib/demo.lisp\"] (the canonical single-library \
13099             shape every `feira init` template scaffolds)",
13100        );
13101    }
13102
13103    #[test]
13104    fn bibliotecas_projects_slice_by_borrow() {
13105        // The by-borrow pin: [`Caixa::bibliotecas`] returns
13106        // `&[String]` by borrow — the returned slice borrows the
13107        // underlying `Vec<String>` storage of the `:bibliotecas` slot
13108        // and the accessor must not clone the backing `Vec` on every
13109        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13110        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
13111        // by-borrow pins on the sibling outer top-level [`Caixa`]
13112        // `&[String]`-return axes — the accessor's returned slice
13113        // must borrow from `&self` (the returned reference's lifetime
13114        // is tied to `&self`), and calling the accessor twice on the
13115        // same [`Caixa`] must yield slices that are pointer-equal
13116        // (the underlying byte-buffer is the storage `Vec`'s
13117        // allocation, not a fresh copy) as well as value-equal
13118        // (idempotent, no side effects on `&self`).
13119        //
13120        // Pins against a future silent detour that returned an owned
13121        // `Vec<String>` (which would type-check but silently clone on
13122        // every call, breaking the zero-cost projection every peer
13123        // sibling slice accessor carries), a `&Vec<String>` return
13124        // (which would leak the backing `Vec`'s grow/push/reserve
13125        // surface no downstream consumer reaches for), or a one-arm-
13126        // only accessor that returned a saturating value on some
13127        // sentinel input (breaking the pass-through invariant the
13128        // sibling slice accessors carry).
13129        for bibliotecas in [
13130            vec![],
13131            vec!["lib/demo.lisp"],
13132            vec!["lib/demo.lisp", "lib/helpers.lisp"],
13133            vec!["lib/foo.lisp", "lib/foo.lisp"],
13134        ] {
13135            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
13136            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
13137            let first = c.bibliotecas();
13138            let second = c.bibliotecas();
13139            assert_eq!(
13140                first, second,
13141                "Caixa::bibliotecas must be idempotent — two \
13142                 successive calls on the same &self must return the \
13143                 same &[String]",
13144            );
13145            assert_eq!(
13146                first.as_ptr(),
13147                second.as_ptr(),
13148                "Caixa::bibliotecas must borrow the underlying \
13149                 Vec<String> storage — two successive calls must \
13150                 return slices with the same backing pointer (a \
13151                 fresh Vec<String> clone would change the pointer on \
13152                 every call)",
13153            );
13154            assert_eq!(
13155                first,
13156                expected.as_slice(),
13157                "Caixa::bibliotecas must return :bibliotecas verbatim \
13158                 by borrow — got {first:?}, expected {expected:?}",
13159            );
13160        }
13161    }
13162
13163    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
13164
13165    #[test]
13166    fn exe_returns_exe_slice_verbatim_across_permutations() {
13167        // The canonical per-`Caixa` `:exe` universal-axis
13168        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
13169        // must return the `:exe` typed [`Vec<String>`] list verbatim as
13170        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
13171        // access across every representative value in the accept-set —
13172        // `[]` (the "no executable declared" arm every `:kind` other
13173        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
13174        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
13175        // + `Binario`-kind combination), `[""]` (a past-the-guard
13176        // sentinel that pins the accessor doesn't perform a silent
13177        // `[""] → []` collapse on the empty-entry arm — validate rejects
13178        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
13179        // accessor must ship the raw slot verbatim so a validate-time
13180        // gate regression surfaces at the layout / `feira nix` boundary
13181        // rather than being silently absorbed into an executable-drop),
13182        // `["exe/cli"]` (the canonical single-entry Binario form every
13183        // in-tree `caixa_with_code_paths` positive control uses),
13184        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
13185        // form the `validate_code_paths_accepts_explicit_relative_paths_
13186        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
13187        // (a past-the-guard duplicate sentinel — validate rejects
13188        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
13189        // set-not-multiset gate, but the accessor must ship the raw
13190        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
13191        // into(), "exe/cli".into()], .. }` fixtures continue to expose
13192        // the duplicate at the accessor).
13193        //
13194        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
13195        // pin on the substrate primitive — folds on the "outer
13196        // [`Caixa`] `&[T]` slice" projection pattern
13197        // `autores_returns_autores_slice_verbatim_across_permutations`
13198        // (b5d813f) opened,
13199        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13200        // (78c7d3c) folded on, and
13201        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
13202        // (8a36c23) closed the universal-axis text-tag family of.
13203        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
13204        // the sibling `:servicos` future lift closes onto. Pins against
13205        // a future silent detour that returned an owned `Vec<String>`
13206        // (which would type-check but silently clone on every accessor
13207        // call, breaking the zero-cost projection every peer sibling
13208        // slice accessor carries), a `[""] → []` collapse (which would
13209        // silently absorb the `CodePathEmpty` refusal case at the
13210        // accessor boundary), or an `["exe/cli", "exe/cli"] →
13211        // ["exe/cli"]` dedup collapse (which would silently absorb the
13212        // `CodePathDuplicate` refusal case at the accessor boundary —
13213        // the per-slot set-not-multiset gate is downstream of the
13214        // accessor and must not be silently promoted into it).
13215        for exe in [
13216            vec![],
13217            vec![""],
13218            vec!["exe/cli"],
13219            vec!["exe/cli", "exe/serve"],
13220            vec!["exe/cli", "exe/cli"],
13221        ] {
13222            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
13223            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
13224            assert_eq!(
13225                c.exe(),
13226                expected.as_slice(),
13227                "Caixa::exe must return :exe verbatim (got {:?}, \
13228                 expected {expected:?})",
13229                c.exe(),
13230            );
13231            assert_eq!(
13232                c.exe(),
13233                c.exe.as_slice(),
13234                "Caixa::exe must byte-equal the raw \
13235                 `self.exe.as_slice()` field access across every value \
13236                 in the Vec<String> accept-set",
13237            );
13238        }
13239    }
13240
13241    #[test]
13242    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
13243        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
13244        // empty-arm gate on the `:exe` slot must key off
13245        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
13246        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
13247        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
13248        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
13249        // (the canonical single-executable form every in-tree
13250        // `caixa_with_code_paths` positive control uses) must pass
13251        // validate. The pair jointly pins the accessor + validate-gate
13252        // composition: any future silent detour that had the accessor
13253        // return an empty slice on the `[""]` arm (a
13254        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
13255        // silently absorb the `CodePathEmpty` refusal at the accessor
13256        // boundary and the validate gate would accept a struct-literal
13257        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
13258        // catches that at caixa-core build time.
13259        //
13260        // Peer of the per-`Caixa`
13261        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
13262        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
13263        // (b5d813f), and
13264        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13265        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
13266        // composition axes — same "the validate / shape-gate predicate
13267        // must route through the substrate-primitive typed dispatch"
13268        // discipline extended onto the sibling outer top-level [`Caixa`]
13269        // `&[T]`-composition surface. Nominally the in-tree
13270        // `validate_code_paths` production body still keys off the
13271        // internal `[(":bibliotecas", &self.bibliotecas,
13272        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
13273        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
13274        // (the tuple's homogeneous slice-typed shape blocks a per-
13275        // element accessor swap in isolation — a future companion lift
13276        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
13277        // accessor axis closes that tuple onto the triple of typed
13278        // dispatches as a unit); the composition pin catches any future
13279        // accessor-side silent filter drop against that eventual tuple-
13280        // closure regardless of whether the `:exe` slot is threaded
13281        // through the accessor or the raw field access at the tuple's
13282        // construction site.
13283        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
13284        assert!(
13285            matches!(
13286                c.validate_code_paths(),
13287                Err(ManifestError::CodePathEmpty { slot: ":exe" })
13288            ),
13289            "validate_code_paths must reject exe == vec![\"\"] \
13290             with CodePathEmpty {{ slot: \":exe\" }} — the \
13291             accessor and the validate gate must route through the \
13292             same substrate-primitive typed dispatch on the \
13293             :exe per-entry empty arm",
13294        );
13295        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
13296        assert!(
13297            c.validate_code_paths().is_ok(),
13298            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
13299             (the canonical single-executable shape every in-tree \
13300             `caixa_with_code_paths` positive control uses)",
13301        );
13302    }
13303
13304    #[test]
13305    fn exe_projects_slice_by_borrow() {
13306        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
13307        // borrow — the returned slice borrows the underlying
13308        // `Vec<String>` storage of the `:exe` slot and the accessor
13309        // must not clone the backing `Vec` on every call. Peer of the
13310        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
13311        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
13312        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
13313        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
13314        // return axes — the accessor's returned slice must borrow from
13315        // `&self` (the returned reference's lifetime is tied to
13316        // `&self`), and calling the accessor twice on the same
13317        // [`Caixa`] must yield slices that are pointer-equal (the
13318        // underlying byte-buffer is the storage `Vec`'s allocation,
13319        // not a fresh copy) as well as value-equal (idempotent, no
13320        // side effects on `&self`).
13321        //
13322        // Pins against a future silent detour that returned an owned
13323        // `Vec<String>` (which would type-check but silently clone on
13324        // every call, breaking the zero-cost projection every peer
13325        // sibling slice accessor carries), a `&Vec<String>` return
13326        // (which would leak the backing `Vec`'s grow/push/reserve
13327        // surface no downstream consumer reaches for), or a one-arm-
13328        // only accessor that returned a saturating value on some
13329        // sentinel input (breaking the pass-through invariant the
13330        // sibling slice accessors carry).
13331        for exe in [
13332            vec![],
13333            vec!["exe/cli"],
13334            vec!["exe/cli", "exe/serve"],
13335            vec!["exe/cli", "exe/cli"],
13336        ] {
13337            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
13338            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
13339            let first = c.exe();
13340            let second = c.exe();
13341            assert_eq!(
13342                first, second,
13343                "Caixa::exe must be idempotent — two successive calls \
13344                 on the same &self must return the same &[String]",
13345            );
13346            assert_eq!(
13347                first.as_ptr(),
13348                second.as_ptr(),
13349                "Caixa::exe must borrow the underlying Vec<String> \
13350                 storage — two successive calls must return slices \
13351                 with the same backing pointer (a fresh Vec<String> \
13352                 clone would change the pointer on every call)",
13353            );
13354            assert_eq!(
13355                first,
13356                expected.as_slice(),
13357                "Caixa::exe must return :exe verbatim by borrow — \
13358                 got {first:?}, expected {expected:?}",
13359            );
13360        }
13361    }
13362
13363    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
13364
13365    #[test]
13366    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
13367        // The canonical per-`Caixa` `:servicos` universal-axis
13368        // ComputeUnit-CR-YAML-entry-path-list slice pin:
13369        // [`Caixa::servicos`] must return the `:servicos` typed
13370        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
13371        // the raw `self.servicos.as_slice()` access across every
13372        // representative value in the accept-set — `[]` (the "no
13373        // ComputeUnit-CR declared" arm every `:kind` other than
13374        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
13375        // `ServicoWithoutServicos` arm-gate fires exactly on this
13376        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
13377        // guard sentinel that pins the accessor doesn't perform a
13378        // silent `[""] → []` collapse on the empty-entry arm — validate
13379        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
13380        // but the accessor must ship the raw slot verbatim so a
13381        // validate-time gate regression surfaces at the layout /
13382        // per-Servico renderer boundary rather than being silently
13383        // absorbed into a component-drop),
13384        // `["servicos/demo.computeunit.yaml"]` (the canonical
13385        // singleton V0-shape every in-tree `caixa_with_code_paths`
13386        // positive control uses; the same shape
13387        // [`crate::require_single_servico`] admits),
13388        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
13389        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
13390        // singularity gate rejects through `ServicoCountMismatch
13391        // { count: 2 }` but the accessor must ship the raw slot
13392        // verbatim so struct-literal `Caixa { servicos: vec![...,
13393        // ...], .. }` fixtures continue to expose the count at the
13394        // accessor), and `["servicos/a.computeunit.yaml",
13395        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
13396        // sentinel — validate rejects through
13397        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
13398        // set-not-multiset gate, but the accessor must ship the raw
13399        // slot verbatim so struct-literal fixtures continue to expose
13400        // the duplicate at the accessor).
13401        //
13402        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
13403        // slice accessor pin on the substrate primitive — folds on the
13404        // "outer [`Caixa`] `&[T]` slice" projection pattern
13405        // `autores_returns_autores_slice_verbatim_across_permutations`
13406        // (b5d813f) opened,
13407        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13408        // (78c7d3c) folded on,
13409        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
13410        // (8a36c23) closed the universal-axis text-tag family of, and
13411        // `exe_returns_exe_slice_verbatim_across_permutations`
13412        // (65d9527) opened the foreign-code-slot sub-family of. Closes
13413        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
13414        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
13415        // `:servicos`) now each carries a substrate-canonical slice
13416        // accessor. Pins against a future silent detour that returned
13417        // an owned `Vec<String>` (which would type-check but silently
13418        // clone on every accessor call, breaking the zero-cost
13419        // projection every peer sibling slice accessor carries), a
13420        // `[""] → []` collapse (which would silently absorb the
13421        // `CodePathEmpty` refusal case at the accessor boundary), an
13422        // `[a, a] → [a]` dedup collapse (which would silently absorb
13423        // the `CodePathDuplicate` refusal case at the accessor
13424        // boundary — the per-slot set-not-multiset gate is downstream
13425        // of the accessor and must not be silently promoted into it),
13426        // or a `[a, b] → [a]` singleton collapse (which would silently
13427        // absorb the V0 `ServicoCountMismatch` refusal case at the
13428        // accessor boundary — the V0 singularity gate is downstream of
13429        // the accessor and must not be silently promoted into it).
13430        for servicos in [
13431            vec![],
13432            vec![""],
13433            vec!["servicos/demo.computeunit.yaml"],
13434            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
13435            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
13436        ] {
13437            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
13438            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
13439            assert_eq!(
13440                c.servicos(),
13441                expected.as_slice(),
13442                "Caixa::servicos must return :servicos verbatim (got \
13443                 {:?}, expected {expected:?})",
13444                c.servicos(),
13445            );
13446            assert_eq!(
13447                c.servicos(),
13448                c.servicos.as_slice(),
13449                "Caixa::servicos must byte-equal the raw \
13450                 `self.servicos.as_slice()` field access across every \
13451                 value in the Vec<String> accept-set",
13452            );
13453        }
13454    }
13455
13456    #[test]
13457    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
13458        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
13459        // empty-arm gate on the `:servicos` slot must key off
13460        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
13461        // field-borrow walk. Structurally: a `Caixa { servicos:
13462        // vec!["".into()], .. }` must surface the `CodePathEmpty
13463        // { slot: ":servicos" }` refusal exactly, and a `Caixa
13464        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
13465        // .. }` (the canonical singleton V0-shape every in-tree
13466        // `caixa_with_code_paths` positive control uses) must pass
13467        // validate. The pair jointly pins the accessor + validate-gate
13468        // composition: any future silent detour that had the accessor
13469        // return an empty slice on the `[""]` arm (a `.iter().filter
13470        // (|s| !s.is_empty()).collect()` collapse) would silently
13471        // absorb the `CodePathEmpty` refusal at the accessor boundary
13472        // and the validate gate would accept a struct-literal
13473        // `Caixa { servicos: vec!["".into()], .. }` — the composition
13474        // pin catches that at caixa-core build time.
13475        //
13476        // Peer of the per-`Caixa`
13477        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
13478        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
13479        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
13480        // (b5d813f), and
13481        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13482        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
13483        // composition axes — same "the validate / shape-gate predicate
13484        // must route through the substrate-primitive typed dispatch"
13485        // discipline extended onto the sibling outer top-level
13486        // [`Caixa`] `&[T]`-composition surface, closing the trio of
13487        // code-surface accessor-composition pins on the same axis.
13488        // Nominally the in-tree `validate_code_paths` production body
13489        // still keys off the internal
13490        // `[(":bibliotecas", &self.bibliotecas,
13491        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
13492        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
13493        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
13494        // per-element accessor swap in isolation — a future companion
13495        // lift promotes the tuple's element type to `&[String]` and
13496        // threads the triple of typed dispatches through as a unit);
13497        // the composition pin catches any future accessor-side silent
13498        // filter drop against that eventual tuple-closure regardless
13499        // of whether the `:servicos` slot is threaded through the
13500        // accessor or the raw field access at the tuple's construction
13501        // site.
13502        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
13503        assert!(
13504            matches!(
13505                c.validate_code_paths(),
13506                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
13507            ),
13508            "validate_code_paths must reject servicos == vec![\"\"] \
13509             with CodePathEmpty {{ slot: \":servicos\" }} — the \
13510             accessor and the validate gate must route through the \
13511             same substrate-primitive typed dispatch on the \
13512             :servicos per-entry empty arm",
13513        );
13514        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
13515        assert!(
13516            c.validate_code_paths().is_ok(),
13517            "validate_code_paths must accept servicos == \
13518             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
13519             singleton V0-shape every in-tree `caixa_with_code_paths` \
13520             positive control uses)",
13521        );
13522    }
13523
13524    #[test]
13525    fn servicos_projects_slice_by_borrow() {
13526        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
13527        // borrow — the returned slice borrows the underlying
13528        // `Vec<String>` storage of the `:servicos` slot and the
13529        // accessor must not clone the backing `Vec` on every call.
13530        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13531        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
13532        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
13533        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
13534        // the sibling outer top-level [`Caixa`] `&[String]`-return
13535        // axes — the accessor's returned slice must borrow from
13536        // `&self` (the returned reference's lifetime is tied to
13537        // `&self`), and calling the accessor twice on the same
13538        // [`Caixa`] must yield slices that are pointer-equal (the
13539        // underlying byte-buffer is the storage `Vec`'s allocation,
13540        // not a fresh copy) as well as value-equal (idempotent, no
13541        // side effects on `&self`).
13542        //
13543        // Pins against a future silent detour that returned an owned
13544        // `Vec<String>` (which would type-check but silently clone on
13545        // every call, breaking the zero-cost projection every peer
13546        // sibling slice accessor carries), a `&Vec<String>` return
13547        // (which would leak the backing `Vec`'s grow/push/reserve
13548        // surface no downstream consumer reaches for), or a one-arm-
13549        // only accessor that returned a saturating value on some
13550        // sentinel input (breaking the pass-through invariant the
13551        // sibling slice accessors carry).
13552        for servicos in [
13553            vec![],
13554            vec!["servicos/demo.computeunit.yaml"],
13555            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
13556            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
13557        ] {
13558            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
13559            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
13560            let first = c.servicos();
13561            let second = c.servicos();
13562            assert_eq!(
13563                first, second,
13564                "Caixa::servicos must be idempotent — two successive \
13565                 calls on the same &self must return the same &[String]",
13566            );
13567            assert_eq!(
13568                first.as_ptr(),
13569                second.as_ptr(),
13570                "Caixa::servicos must borrow the underlying \
13571                 Vec<String> storage — two successive calls must \
13572                 return slices with the same backing pointer (a fresh \
13573                 Vec<String> clone would change the pointer on every \
13574                 call)",
13575            );
13576            assert_eq!(
13577                first,
13578                expected.as_slice(),
13579                "Caixa::servicos must return :servicos verbatim by \
13580                 borrow — got {first:?}, expected {expected:?}",
13581            );
13582        }
13583    }
13584
13585    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
13586
13587    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
13588        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13589        c.deps = deps;
13590        c
13591    }
13592
13593    #[test]
13594    fn deps_returns_deps_slice_verbatim_across_permutations() {
13595        // The canonical per-`Caixa` `:deps` universal-axis runtime-
13596        // dependency-declaration-list slice pin: [`Caixa::deps`] must
13597        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
13598        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
13599        // access across every representative value in the accept-set —
13600        // `[]` (the "no runtime deps declared" arm every existing
13601        // fixture without a `:deps` line carries; the
13602        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
13603        // single-entry list (the shape most consumer caixas carry), a
13604        // canonical two-entry list (the multi-dep runtime closure), and
13605        // two past-the-guard sentinels — a `[""]`-`:nome` entry
13606        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
13607        // `NomeInvalid` but the accessor must ship the raw slot
13608        // verbatim) and a `[a, a]` duplicate (validate rejects through
13609        // `DuplicateNome { list: ":deps" }` but the accessor must ship
13610        // the raw slot verbatim so struct-literal fixtures continue to
13611        // expose the duplicate at the accessor).
13612        //
13613        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
13614        // pin on the substrate primitive — opens the outer-`Caixa`
13615        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
13616        // future lift closes on. Peer of the closed outer-`Caixa`
13617        // foreign-code-slot `&[String]` sub-family
13618        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
13619        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
13620        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
13621        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
13622        // (`autores_returns_autores_slice_verbatim_across_permutations`
13623        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13624        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
13625        // projection pattern onto a novel element-type axis (`Dep`
13626        // composite vs the prior sibling family's `String` scalar).
13627        // Pins against a future silent detour that returned an owned
13628        // `Vec<Dep>` (which would type-check but silently clone on every
13629        // accessor call, breaking the zero-cost projection every peer
13630        // sibling slice accessor carries), a `[""] → []` collapse (which
13631        // would silently absorb the `NomeEmpty` refusal case at the
13632        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
13633        // would silently absorb the `DuplicateNome` refusal case at the
13634        // accessor boundary).
13635        for deps in [
13636            vec![],
13637            vec![Dep::simple("", "^0.1")],
13638            vec![Dep::simple("caixa-teia", "^0.1")],
13639            vec![
13640                Dep::simple("caixa-teia", "^0.1"),
13641                Dep::simple("caixa-core", "^0.1"),
13642            ],
13643            vec![
13644                Dep::simple("caixa-teia", "^0.1"),
13645                Dep::simple("caixa-teia", "^0.2"),
13646            ],
13647        ] {
13648            let c = caixa_with_deps(deps.clone());
13649            assert_eq!(
13650                c.deps(),
13651                deps.as_slice(),
13652                "Caixa::deps must return :deps verbatim (got {:?}, \
13653                 expected {deps:?})",
13654                c.deps(),
13655            );
13656            assert_eq!(
13657                c.deps(),
13658                c.deps.as_slice(),
13659                "Caixa::deps must element-equal the raw \
13660                 `self.deps.as_slice()` field access across every \
13661                 value in the Vec<Dep> accept-set",
13662            );
13663        }
13664    }
13665
13666    #[test]
13667    fn validate_deps_duplicate_arm_routes_through_accessor() {
13668        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
13669        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
13670        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
13671        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
13672        // "^0.2")], .. }` must surface the `DuplicateNome { list:
13673        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
13674        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
13675        // form) must pass validate. The pair jointly pins the accessor +
13676        // validate-gate composition: any future silent detour that had
13677        // the accessor return a dedupped slice on the `[a, a]` arm (a
13678        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
13679        // would silently absorb the `DuplicateNome` refusal at the
13680        // accessor boundary and the validate gate would accept a
13681        // struct-literal `Caixa` carrying the drift — the composition
13682        // pin catches that at caixa-core build time.
13683        //
13684        // Peer of the per-`Caixa`
13685        // `validate_autores_empty_entry_arm_routes_through_accessor`
13686        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13687        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
13688        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
13689        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
13690        // (611f78b) accessor-composition pins on the sibling `&[T]`-
13691        // composition axes — same "the validate gate must route through
13692        // the substrate-primitive typed dispatch" discipline extended
13693        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
13694        // composition surface, opening the outer-`Caixa` dependency-slot
13695        // arm of the composition-pin family.
13696        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
13697        let err = c.validate_deps().unwrap_err();
13698        assert!(
13699            matches!(
13700                err,
13701                DepError::DuplicateNome { ref nome, list } if nome == "d"
13702                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
13703            ),
13704            "validate_deps must reject deps == \
13705             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
13706             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
13707             accessor and the validate gate must route through the \
13708             same substrate-primitive typed dispatch on the :deps \
13709             within-list duplicate arm (got {err:?})",
13710        );
13711        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
13712        assert!(
13713            c.validate_deps().is_ok(),
13714            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
13715             (the canonical single-entry form)",
13716        );
13717    }
13718
13719    #[test]
13720    fn deps_projects_slice_by_borrow() {
13721        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
13722        // — the returned slice borrows the underlying `Vec<Dep>` storage
13723        // of the `:deps` slot and the accessor must not clone the
13724        // backing `Vec` on every call. Peer of the per-`Caixa`
13725        // `autores_projects_slice_by_borrow` (b5d813f),
13726        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
13727        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
13728        // `exe_projects_slice_by_borrow` (65d9527), and
13729        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
13730        // on the sibling outer top-level [`Caixa`] `&[String]`-return
13731        // axes — the accessor's returned slice must borrow from `&self`
13732        // (the returned reference's lifetime is tied to `&self`), and
13733        // calling the accessor twice on the same [`Caixa`] must yield
13734        // slices that are pointer-equal (the underlying byte-buffer is
13735        // the storage `Vec`'s allocation, not a fresh copy) as well as
13736        // value-equal (idempotent, no side effects on `&self`).
13737        //
13738        // Pins against a future silent detour that returned an owned
13739        // `Vec<Dep>` (which would type-check but silently clone on
13740        // every call), a `&Vec<Dep>` return (which would leak the
13741        // backing `Vec`'s grow/push/reserve surface no downstream
13742        // consumer reaches for), or a one-arm-only accessor that
13743        // returned a saturating value on some sentinel input.
13744        for deps in [
13745            vec![],
13746            vec![Dep::simple("caixa-teia", "^0.1")],
13747            vec![
13748                Dep::simple("caixa-teia", "^0.1"),
13749                Dep::simple("caixa-core", "^0.1"),
13750            ],
13751        ] {
13752            let c = caixa_with_deps(deps.clone());
13753            let first = c.deps();
13754            let second = c.deps();
13755            assert_eq!(
13756                first, second,
13757                "Caixa::deps must be idempotent — two successive calls \
13758                 on the same &self must return the same &[Dep]",
13759            );
13760            assert_eq!(
13761                first.as_ptr(),
13762                second.as_ptr(),
13763                "Caixa::deps must borrow the underlying Vec<Dep> \
13764                 storage — two successive calls must return slices \
13765                 with the same backing pointer (a fresh Vec<Dep> clone \
13766                 would change the pointer on every call)",
13767            );
13768            assert_eq!(
13769                first,
13770                deps.as_slice(),
13771                "Caixa::deps must return :deps verbatim by borrow — \
13772                 got {first:?}, expected {deps:?}",
13773            );
13774        }
13775    }
13776
13777    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
13778
13779    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
13780        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13781        c.deps_dev = deps_dev;
13782        c
13783    }
13784
13785    #[test]
13786    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
13787        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
13788        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
13789        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
13790        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
13791        // access across every representative value in the accept-set —
13792        // `[]` (the "no dev deps declared" arm every existing fixture
13793        // without a `:deps-dev` line carries; the [`Caixa::template`]
13794        // scaffold emits `:deps-dev ()`), a canonical single-entry list
13795        // (the shape most consumer caixas carry — a `tatara-check` dev
13796        // pin), a canonical two-entry list (the multi-dev-dep closure),
13797        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
13798        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
13799        // `NomeInvalid` but the accessor must ship the raw slot
13800        // verbatim) and a `[a, a]` duplicate (validate rejects through
13801        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
13802        // ship the raw slot verbatim so struct-literal fixtures continue
13803        // to expose the duplicate at the accessor).
13804        //
13805        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
13806        // pin on the substrate primitive — closes the outer-`Caixa`
13807        // dependency-slot `&[Dep]` sub-family the sibling
13808        // `deps_returns_deps_slice_verbatim_across_permutations`
13809        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
13810        // slice" projection pattern onto the sibling dev-dep axis —
13811        // pins against a future silent detour that returned an owned
13812        // `Vec<Dep>` (which would type-check but silently clone on every
13813        // accessor call, breaking the zero-cost projection every peer
13814        // sibling slice accessor carries), a `[""] → []` collapse (which
13815        // would silently absorb the `NomeEmpty` refusal case at the
13816        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
13817        // would silently absorb the `DuplicateNome` refusal case at the
13818        // accessor boundary).
13819        for deps_dev in [
13820            vec![],
13821            vec![Dep::simple("", "^0.1")],
13822            vec![Dep::simple("tatara-check", "^0.1")],
13823            vec![
13824                Dep::simple("tatara-check", "^0.1"),
13825                Dep::simple("caixa-lint", "^0.1"),
13826            ],
13827            vec![
13828                Dep::simple("tatara-check", "^0.1"),
13829                Dep::simple("tatara-check", "^0.2"),
13830            ],
13831        ] {
13832            let c = caixa_with_deps_dev(deps_dev.clone());
13833            assert_eq!(
13834                c.deps_dev(),
13835                deps_dev.as_slice(),
13836                "Caixa::deps_dev must return :deps-dev verbatim (got \
13837                 {:?}, expected {deps_dev:?})",
13838                c.deps_dev(),
13839            );
13840            assert_eq!(
13841                c.deps_dev(),
13842                c.deps_dev.as_slice(),
13843                "Caixa::deps_dev must element-equal the raw \
13844                 `self.deps_dev.as_slice()` field access across every \
13845                 value in the Vec<Dep> accept-set",
13846            );
13847        }
13848    }
13849
13850    #[test]
13851    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
13852        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
13853        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
13854        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
13855        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
13856        // Dep::simple("d", "^0.2")], .. }` must surface the
13857        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
13858        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
13859        // canonical single-entry form) must pass validate. The pair
13860        // jointly pins the accessor + validate-gate composition: any
13861        // future silent detour that had the accessor return a dedupped
13862        // slice on the `[a, a]` arm (a
13863        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
13864        // would silently absorb the `DuplicateNome` refusal at the
13865        // accessor boundary and the validate gate would accept a
13866        // struct-literal `Caixa` carrying the drift — the composition
13867        // pin catches that at caixa-core build time.
13868        //
13869        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
13870        // (ad34b4e) on the sibling `:deps` axis — same "the validate
13871        // gate must route through the substrate-primitive typed
13872        // dispatch" discipline folded onto the sibling `:deps-dev`
13873        // axis, closing the two-list dep-graph composition-pin family.
13874        // The `:deps-dev` diagnostic must carry the
13875        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
13876        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
13877        // offending list unambiguously.
13878        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
13879        let err = c.validate_deps().unwrap_err();
13880        assert!(
13881            matches!(
13882                err,
13883                DepError::DuplicateNome { ref nome, list } if nome == "d"
13884                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
13885            ),
13886            "validate_deps must reject deps_dev == \
13887             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
13888             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
13889             accessor and the validate gate must route through the \
13890             same substrate-primitive typed dispatch on the :deps-dev \
13891             within-list duplicate arm (got {err:?})",
13892        );
13893        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
13894        assert!(
13895            c.validate_deps().is_ok(),
13896            "validate_deps must accept deps_dev == \
13897             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
13898        );
13899    }
13900
13901    #[test]
13902    fn deps_dev_projects_slice_by_borrow() {
13903        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
13904        // borrow — the returned slice borrows the underlying `Vec<Dep>`
13905        // storage of the `:deps-dev` slot and the accessor must not
13906        // clone the backing `Vec` on every call. Peer of
13907        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
13908        // `:deps` axis, and of the per-`Caixa`
13909        // `autores_projects_slice_by_borrow` (b5d813f),
13910        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
13911        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
13912        // `exe_projects_slice_by_borrow` (65d9527), and
13913        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
13914        // on the sibling outer top-level [`Caixa`] `&[String]`-return
13915        // axes — the accessor's returned slice must borrow from `&self`
13916        // (the returned reference's lifetime is tied to `&self`), and
13917        // calling the accessor twice on the same [`Caixa`] must yield
13918        // slices that are pointer-equal (the underlying byte-buffer is
13919        // the storage `Vec`'s allocation, not a fresh copy) as well as
13920        // value-equal (idempotent, no side effects on `&self`).
13921        //
13922        // Pins against a future silent detour that returned an owned
13923        // `Vec<Dep>` (which would type-check but silently clone on
13924        // every call), a `&Vec<Dep>` return (which would leak the
13925        // backing `Vec`'s grow/push/reserve surface no downstream
13926        // consumer reaches for), or a one-arm-only accessor that
13927        // returned a saturating value on some sentinel input.
13928        for deps_dev in [
13929            vec![],
13930            vec![Dep::simple("tatara-check", "^0.1")],
13931            vec![
13932                Dep::simple("tatara-check", "^0.1"),
13933                Dep::simple("caixa-lint", "^0.1"),
13934            ],
13935        ] {
13936            let c = caixa_with_deps_dev(deps_dev.clone());
13937            let first = c.deps_dev();
13938            let second = c.deps_dev();
13939            assert_eq!(
13940                first, second,
13941                "Caixa::deps_dev must be idempotent — two successive \
13942                 calls on the same &self must return the same &[Dep]",
13943            );
13944            assert_eq!(
13945                first.as_ptr(),
13946                second.as_ptr(),
13947                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
13948                 storage — two successive calls must return slices \
13949                 with the same backing pointer (a fresh Vec<Dep> clone \
13950                 would change the pointer on every call)",
13951            );
13952            assert_eq!(
13953                first,
13954                deps_dev.as_slice(),
13955                "Caixa::deps_dev must return :deps-dev verbatim by \
13956                 borrow — got {first:?}, expected {deps_dev:?}",
13957            );
13958        }
13959    }
13960
13961    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
13962
13963    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
13964        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13965        c.limits = limits;
13966        c
13967    }
13968
13969    #[test]
13970    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
13971        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
13972        // composite optional-composite-reference-shape pin:
13973        // [`Caixa::limits`] must return the `:limits` typed
13974        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
13975        // reference over the same backing storage the raw
13976        // `self.limits.as_ref()` field access borrows from, byte-equal
13977        // across every representative fixture in the accept-set — the
13978        // author-omitted `None` shape (the "engine-default applies"
13979        // partition every downstream Servico M2 overlay emitter treats
13980        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
13981        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
13982        // per-axis cap is `None`, so the peer M2 overlay emitter's
13983        // `.is_empty()`-gated projection still emits nothing but the
13984        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
13985        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
13986        // fixture (only `:memory` set — the canonical shape most
13987        // memory-heavy Servicos carry), and a fully-populated composite
13988        // (every per-axis cap set — the canonical shape a
13989        // sandboxed-by-default Servico carries).
13990        //
13991        // Pins against a future silent detour that returned a fresh-
13992        // cloned [`LimitsSpec`] copy (which would type-check via the
13993        // `Clone` impl but silently break every downstream caller that
13994        // relied on the reference sharing the composite's backing
13995        // identity), a reference to an operator-resolved overlay (the
13996        // future per-cluster `:limits-overrides` slot — its resolution
13997        // must land at exactly this accessor body, not silently divert
13998        // the raw slot away from a second consumer), a
13999        // `None` → `Some(LimitsSpec::default)` cluster-default
14000        // projection (which would collapse the load-bearing
14001        // "author-omitted `:limits` ⇒ engine-default applies" partition
14002        // the peer [`crate::render::servico_m2_overlay`] emitter and
14003        // the peer [`Caixa::declared_servico_slots`] enumerator both
14004        // read), or an axis-shuffled projection (a future detour that
14005        // swapped `memory` and `fuel` through the accessor would
14006        // silently split the paired [`crate::StandardLayout::verify`]
14007        // per-`:limits` shape gate's traversal input from the peer
14008        // `servico_m2_overlay` emitter's projection input).
14009        //
14010        // First outer top-level [`Caixa`] `Option<&Composite>`-return
14011        // composite-reference accessor pin on the substrate primitive
14012        // — opens the outer-`Caixa` `Option<&Composite>` composite-
14013        // reference projection pattern the sibling `:behavior`
14014        // [`crate::BehaviorSpec`] / `:politicas`
14015        // [`crate::aplicacao::MeshPolicy`] / `:placement`
14016        // [`crate::aplicacao::Placement`] / `:entrada`
14017        // [`crate::aplicacao::Entrada`] future outer-composite lifts
14018        // fold on. Peer of the closed M3 outer-composite family the
14019        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
14020        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
14021        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
14022        // reference accessor pins already carry on the outer
14023        // [`crate::AplicacaoSpec`] altitude — extends the outer-
14024        // accessor byte-equal-projection discipline onto the outer
14025        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
14026        use crate::LimitsSpec;
14027        use std::time::Duration;
14028        let fixtures: Vec<Option<LimitsSpec>> = vec![
14029            None,
14030            Some(LimitsSpec::default()),
14031            Some(LimitsSpec {
14032                memory: Some(64 * 1024 * 1024),
14033                ..Default::default()
14034            }),
14035            Some(LimitsSpec {
14036                memory: Some(64 * 1024 * 1024),
14037                fuel: Some(1_000_000),
14038                wall_clock: Some(Duration::from_secs(30)),
14039                cpu: Some(500),
14040            }),
14041        ];
14042        for limits in fixtures {
14043            let c = caixa_with_limits(limits.clone());
14044            assert_eq!(
14045                c.limits(),
14046                limits.as_ref(),
14047                "Caixa::limits must return :limits verbatim (got {:?}, \
14048                 expected {:?})",
14049                c.limits(),
14050                limits.as_ref(),
14051            );
14052            match (c.limits(), c.limits.as_ref()) {
14053                (Some(a), Some(b)) => assert!(
14054                    std::ptr::eq(a, b),
14055                    "Caixa::limits accessor and self.limits.as_ref() \
14056                     field access must borrow the same backing storage \
14057                     — the accessor is the substrate-primitive typed \
14058                     dispatch every downstream Servico-M2-overlay \
14059                     composite consumer must route through, and a \
14060                     reference-identity split would silently break \
14061                     every consumer that relied on the borrow sharing \
14062                     the composite's storage",
14063                ),
14064                (None, None) => {}
14065                _ => panic!(
14066                    "Caixa::limits presence bit must byte-equal \
14067                     self.limits.is_some() — a presence-bit drift would \
14068                     silently split the paired StandardLayout::verify \
14069                     per-`:limits` shape gate's traversal head from \
14070                     the peer render::servico_m2_overlay M2 overlay \
14071                     emitter's traversal head from the peer \
14072                     Caixa::declared_servico_slots M2 declared-slot \
14073                     enumerator's presence probe",
14074                ),
14075            }
14076            assert_eq!(
14077                c.limits().is_some(),
14078                c.limits.is_some(),
14079                "Caixa::limits().is_some() must byte-equal \
14080                 self.limits.is_some() — a presence-bit drift would \
14081                 silently split every downstream Option<&LimitsSpec> \
14082                 consumer's partition on the engine-default arm",
14083            );
14084        }
14085    }
14086
14087    #[test]
14088    fn declared_servico_slots_limits_arm_routes_through_accessor() {
14089        // Composition pin: [`Caixa::declared_servico_slots`]'s
14090        // `:limits` presence-probe arm must key off [`Caixa::limits`],
14091        // not the raw `self.limits.is_some()` field-probe. Structurally:
14092        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
14093        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
14094        // (the presence bit is `Some`, so the M2 kind-coherence gate
14095        // must surface the slot as "declared" even when every per-axis
14096        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
14097        // push the label (the "author omitted the slot entirely"
14098        // partition). The pair jointly pins the accessor + declared-
14099        // slot enumerator composition: any future silent detour that
14100        // had the accessor collapse `Some(LimitsSpec::default())` to
14101        // `None` (a `.filter(|l| !l.is_empty())` projection) would
14102        // silently absorb the "declared but empty" arm at the
14103        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
14104        // kind-coherence gate would silently accept a
14105        // struct-literal `Caixa` carrying the drift.
14106        //
14107        // Peer of the sibling per-`Caixa`
14108        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
14109        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
14110        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
14111        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
14112        // enumerator gate must route through the substrate-primitive
14113        // typed dispatch" discipline extended onto the outer top-level
14114        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
14115        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
14116        // composition-pin family.
14117        use crate::LimitsSpec;
14118        let c = caixa_with_limits(Some(LimitsSpec::default()));
14119        let slots = c.declared_servico_slots();
14120        assert!(
14121            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
14122            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
14123             when `:limits` is Some (even for LimitsSpec::default()) \
14124             — the accessor and the enumerator gate must route through \
14125             the same substrate-primitive typed dispatch on the outer \
14126             :limits presence bit (got slots={slots:?})",
14127        );
14128        let c = caixa_with_limits(None);
14129        let slots = c.declared_servico_slots();
14130        assert!(
14131            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
14132            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
14133             when `:limits` is None — the author-omitted arm must \
14134             route through the accessor's None-return unchanged (got \
14135             slots={slots:?})",
14136        );
14137    }
14138
14139    #[test]
14140    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
14141        // Composition pin: [`crate::render::servico_m2_overlay`]'s
14142        // per-`:limits` M2 overlay emit arm must key off
14143        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
14144        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
14145        // Some(64 MiB), .. default }), .. }` must surface the
14146        // `M2_KEY_LIMITS` key with the per-axis
14147        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
14148        // limits: Some(LimitsSpec::default()), .. }` must omit the
14149        // key entirely (the `.is_empty()`-gated inner arm elides an
14150        // empty composite even when the outer presence bit is `Some`),
14151        // and a `Caixa { limits: None, .. }` must also omit the key
14152        // (the "author omitted the slot entirely" partition). The
14153        // three-fixture family jointly pins the accessor + M2 overlay
14154        // emitter composition: any future silent detour that had the
14155        // accessor return a fresh-cloned copy on the `Some` arm (a
14156        // `LimitsSpec::clone()` projection) would silently break the
14157        // reference-identity pin the peer per-axis
14158        // `serde_yaml::to_value(limits)` projection reads from.
14159        use crate::LimitsSpec;
14160        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
14161        let c = caixa_with_limits(Some(LimitsSpec {
14162            memory: Some(64 * 1024 * 1024),
14163            ..Default::default()
14164        }));
14165        let overlay = servico_m2_overlay(&c).unwrap();
14166        assert!(
14167            overlay.contains_key(M2_KEY_LIMITS),
14168            "servico_m2_overlay must surface M2_KEY_LIMITS when \
14169             `:limits` carries a non-empty composite — the accessor \
14170             and the M2 overlay emitter must route through the same \
14171             substrate-primitive typed dispatch on the outer :limits \
14172             composite (got overlay={overlay:?})",
14173        );
14174        let c = caixa_with_limits(Some(LimitsSpec::default()));
14175        let overlay = servico_m2_overlay(&c).unwrap();
14176        assert!(
14177            !overlay.contains_key(M2_KEY_LIMITS),
14178            "servico_m2_overlay must omit M2_KEY_LIMITS when \
14179             `:limits` is Some(LimitsSpec::default()) — the empty \
14180             composite's `.is_empty()`-gated inner arm must elide \
14181             the key regardless of the outer presence bit (got \
14182             overlay={overlay:?})",
14183        );
14184        let c = caixa_with_limits(None);
14185        let overlay = servico_m2_overlay(&c).unwrap();
14186        assert!(
14187            !overlay.contains_key(M2_KEY_LIMITS),
14188            "servico_m2_overlay must omit M2_KEY_LIMITS when \
14189             `:limits` is None — the author-omitted arm must route \
14190             through the accessor's None-return unchanged (got \
14191             overlay={overlay:?})",
14192        );
14193    }
14194
14195    #[test]
14196    fn limits_projects_option_ref_by_borrow() {
14197        // The by-borrow pin: [`Caixa::limits`] returns
14198        // `Option<&LimitsSpec>` by borrow — the returned reference
14199        // borrows the underlying `Option<LimitsSpec>` storage of the
14200        // `:limits` slot and the accessor must not clone the backing
14201        // composite on every call. Peer of the sibling
14202        // `deps_projects_slice_by_borrow` (ad34b4e) /
14203        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
14204        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
14205        // extended here to the outer [`Caixa`] `Option<&Composite>`-
14206        // return axis: the accessor's returned reference must borrow
14207        // from `&self` (the returned reference's lifetime is tied to
14208        // `&self`), and calling the accessor twice on the same
14209        // [`Caixa`] must yield references that are pointer-equal (the
14210        // underlying byte-buffer is the storage `LimitsSpec`'s
14211        // allocation, not a fresh copy) as well as value-equal
14212        // (idempotent, no side effects on `&self`).
14213        //
14214        // Pins against a future silent detour that returned an owned
14215        // `LimitsSpec` (which would type-check via the `Clone` impl
14216        // but silently clone on every call), a `&LimitsSpec` panic-
14217        // return on the `None` arm (which would collapse the load-
14218        // bearing `Option` presence-bit into a runtime panic), or a
14219        // one-arm-only accessor that returned a saturating composite
14220        // on some sentinel input.
14221        use crate::LimitsSpec;
14222        use std::time::Duration;
14223        for limits in [
14224            Some(LimitsSpec::default()),
14225            Some(LimitsSpec {
14226                memory: Some(64 * 1024 * 1024),
14227                fuel: Some(1_000_000),
14228                wall_clock: Some(Duration::from_secs(30)),
14229                cpu: Some(500),
14230            }),
14231        ] {
14232            let c = caixa_with_limits(limits.clone());
14233            let first = c.limits().unwrap();
14234            let second = c.limits().unwrap();
14235            assert_eq!(
14236                first, second,
14237                "Caixa::limits must be idempotent — two successive \
14238                 calls on the same &self must return the same \
14239                 &LimitsSpec",
14240            );
14241            assert!(
14242                std::ptr::eq(first, second),
14243                "Caixa::limits must borrow the underlying \
14244                 Option<LimitsSpec> storage — two successive calls \
14245                 must return references with the same backing pointer \
14246                 (a fresh LimitsSpec clone would change the pointer \
14247                 on every call)",
14248            );
14249            assert_eq!(
14250                Some(first),
14251                limits.as_ref(),
14252                "Caixa::limits must return :limits verbatim by borrow \
14253                 — got {first:?}, expected {:?}",
14254                limits.as_ref(),
14255            );
14256        }
14257        let c = caixa_with_limits(None);
14258        assert!(
14259            c.limits().is_none(),
14260            "Caixa::limits must return None when :limits is absent — \
14261             the author-omitted arm must project through the \
14262             accessor's Option::None unchanged",
14263        );
14264    }
14265
14266    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
14267
14268    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
14269        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14270        c.behavior = behavior;
14271        c
14272    }
14273
14274    #[test]
14275    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
14276        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
14277        // composite optional-composite-reference-shape pin:
14278        // [`Caixa::behavior`] must return the `:behavior` typed
14279        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
14280        // reference over the same backing storage the raw
14281        // `self.behavior.as_ref()` field access borrows from, byte-equal
14282        // across every representative fixture in the accept-set — the
14283        // author-omitted `None` shape (the "runtime-default applies"
14284        // partition every downstream Servico M2 overlay emitter treats
14285        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
14286        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
14287        // every per-callback path is `None`, so the peer M2 overlay
14288        // emitter's `.is_empty()`-gated projection still emits nothing
14289        // but the outer presence-bit is `Some`, so
14290        // [`Caixa::declared_servico_slots`] still pushes the
14291        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
14292        // (only `:on-state-change` set — the canonical shape a caixa
14293        // that only wires the hot-upgrade migration path carries), and
14294        // a fully-populated composite (every per-callback path set —
14295        // the canonical shape a fully-instrumented gen_server-shaped
14296        // Servico carries).
14297        //
14298        // Peer of the sibling
14299        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14300        // (b2bd9d7) opening fixture-family + reference-identity +
14301        // presence-bit tetrad pin on the outer top-level [`Caixa`]
14302        // `Option<&Composite>`-return sub-family — extended here to the
14303        // second axis of that sub-family so both of the currently-lifted
14304        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
14305        // `:behavior`) carry the same "byte-equal, borrow-shared,
14306        // presence-bit-preserved" outer-accessor discipline.
14307        //
14308        // Pins against a future silent detour that returned a fresh-
14309        // cloned [`crate::BehaviorSpec`] copy (which would type-check
14310        // via the `Clone` impl but silently break every downstream
14311        // caller that relied on the reference sharing the composite's
14312        // backing identity), a reference to an operator-resolved
14313        // overlay (a future per-cluster `:behavior-overrides` slot —
14314        // its resolution must land at exactly this accessor body, not
14315        // silently divert the raw slot away from a second consumer), a
14316        // `None` → `Some(BehaviorSpec::default)` cluster-default
14317        // projection (which would collapse the load-bearing
14318        // "author-omitted `:behavior` ⇒ runtime-default applies"
14319        // partition the peer [`crate::render::servico_m2_overlay`]
14320        // emitter, the peer [`Caixa::declared_servico_slots`]
14321        // enumerator, and the cross-slot
14322        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
14323        // gate all read), or a callback-shuffled projection (a future
14324        // detour that swapped `on_init` and `on_terminate` through the
14325        // accessor would silently split the paired
14326        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
14327        // traversal input from the peer `servico_m2_overlay` emitter's
14328        // projection input from the cross-slot `:state-change`
14329        // composition gate's traversal input).
14330        use crate::BehaviorSpec;
14331        use std::path::PathBuf;
14332        let fixtures: Vec<Option<BehaviorSpec>> = vec![
14333            None,
14334            Some(BehaviorSpec::default()),
14335            Some(BehaviorSpec {
14336                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14337                ..Default::default()
14338            }),
14339            Some(BehaviorSpec {
14340                on_init: Some(PathBuf::from("lib/init.lisp")),
14341                on_call: Some(PathBuf::from("lib/handlers.lisp")),
14342                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
14343                on_info: Some(PathBuf::from("lib/handlers.lisp")),
14344                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14345                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
14346            }),
14347        ];
14348        for behavior in fixtures {
14349            let c = caixa_with_behavior(behavior.clone());
14350            assert_eq!(
14351                c.behavior(),
14352                behavior.as_ref(),
14353                "Caixa::behavior must return :behavior verbatim (got \
14354                 {:?}, expected {:?})",
14355                c.behavior(),
14356                behavior.as_ref(),
14357            );
14358            match (c.behavior(), c.behavior.as_ref()) {
14359                (Some(a), Some(b)) => assert!(
14360                    std::ptr::eq(a, b),
14361                    "Caixa::behavior accessor and self.behavior.as_ref() \
14362                     field access must borrow the same backing storage \
14363                     — the accessor is the substrate-primitive typed \
14364                     dispatch every downstream Servico-M2-overlay \
14365                     composite consumer must route through, and a \
14366                     reference-identity split would silently break \
14367                     every consumer that relied on the borrow sharing \
14368                     the composite's storage",
14369                ),
14370                (None, None) => {}
14371                _ => panic!(
14372                    "Caixa::behavior presence bit must byte-equal \
14373                     self.behavior.is_some() — a presence-bit drift \
14374                     would silently split the paired \
14375                     StandardLayout::verify per-`:behavior` shape \
14376                     gate's traversal head from the peer \
14377                     render::servico_m2_overlay M2 overlay emitter's \
14378                     traversal head from the cross-slot \
14379                     validate_upgrade_from_against_behavior \
14380                     composition gate's traversal head from the peer \
14381                     Caixa::declared_servico_slots M2 declared-slot \
14382                     enumerator's presence probe",
14383                ),
14384            }
14385            assert_eq!(
14386                c.behavior().is_some(),
14387                c.behavior.is_some(),
14388                "Caixa::behavior().is_some() must byte-equal \
14389                 self.behavior.is_some() — a presence-bit drift would \
14390                 silently split every downstream Option<&BehaviorSpec> \
14391                 consumer's partition on the runtime-default arm",
14392            );
14393        }
14394    }
14395
14396    #[test]
14397    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
14398        // Composition pin: [`Caixa::declared_servico_slots`]'s
14399        // `:behavior` presence-probe arm must key off
14400        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
14401        // field-probe. Structurally: a `Caixa { behavior:
14402        // Some(BehaviorSpec::default()), .. }` must still push
14403        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
14404        // presence bit is `Some`, so the M2 kind-coherence gate must
14405        // surface the slot as "declared" even when every per-callback
14406        // path is unset), and a `Caixa { behavior: None, .. }` must
14407        // NOT push the label (the "author omitted the slot entirely"
14408        // partition). The pair jointly pins the accessor + declared-
14409        // slot enumerator composition: any future silent detour that
14410        // had the accessor collapse `Some(BehaviorSpec::default())`
14411        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
14412        // silently absorb the "declared but empty" arm at the
14413        // accessor boundary and the
14414        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
14415        // kind-coherence gate would silently accept a struct-literal
14416        // `Caixa` carrying the drift.
14417        //
14418        // Peer of the sibling
14419        // `declared_servico_slots_limits_arm_routes_through_accessor`
14420        // (b2bd9d7) composition pin on the sibling `:limits` outer-
14421        // `Option<&LimitsSpec>` arm of the same
14422        // [`Caixa::declared_servico_slots`] M2 declared-slot
14423        // enumerator's traversal — same "the enumerator gate must
14424        // route through the substrate-primitive typed dispatch"
14425        // discipline extended onto the outer top-level [`Caixa`]
14426        // `Option<&BehaviorSpec>`-composition surface.
14427        use crate::BehaviorSpec;
14428        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
14429        let slots = c.declared_servico_slots();
14430        assert!(
14431            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
14432            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
14433             when `:behavior` is Some (even for BehaviorSpec::default()) \
14434             — the accessor and the enumerator gate must route through \
14435             the same substrate-primitive typed dispatch on the outer \
14436             :behavior presence bit (got slots={slots:?})",
14437        );
14438        let c = caixa_with_behavior(None);
14439        let slots = c.declared_servico_slots();
14440        assert!(
14441            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
14442            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
14443             when `:behavior` is None — the author-omitted arm must \
14444             route through the accessor's None-return unchanged (got \
14445             slots={slots:?})",
14446        );
14447    }
14448
14449    #[test]
14450    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
14451        // Composition pin: [`crate::render::servico_m2_overlay`]'s
14452        // per-`:behavior` M2 overlay emit arm must key off
14453        // [`Caixa::behavior`], not the raw `&caixa.behavior`
14454        // field-borrow. Structurally: a `Caixa { behavior:
14455        // Some(BehaviorSpec { on_state_change: Some(...), .. default
14456        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
14457        // per-callback `onStateChange` sub-mapping in the overlay, a
14458        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
14459        // must omit the key entirely (the `.is_empty()`-gated inner
14460        // arm elides an empty composite even when the outer presence
14461        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
14462        // also omit the key (the "author omitted the slot entirely"
14463        // partition). The three-fixture family jointly pins the
14464        // accessor + M2 overlay emitter composition: any future
14465        // silent detour that had the accessor return a fresh-cloned
14466        // copy on the `Some` arm (a `BehaviorSpec::clone()`
14467        // projection) would silently break the reference-identity
14468        // pin the peer per-callback `serde_yaml::to_value(behavior)`
14469        // projection reads from.
14470        //
14471        // Peer of the sibling
14472        // `servico_m2_overlay_limits_arm_routes_through_accessor`
14473        // (b2bd9d7) composition pin on the sibling `:limits` outer-
14474        // `Option<&LimitsSpec>` arm of the same
14475        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
14476        // traversal — same "the emitter must route through the
14477        // substrate-primitive typed dispatch on the outer composite"
14478        // discipline extended onto the outer top-level [`Caixa`]
14479        // `Option<&BehaviorSpec>`-composition surface.
14480        use crate::BehaviorSpec;
14481        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
14482        use std::path::PathBuf;
14483        let c = caixa_with_behavior(Some(BehaviorSpec {
14484            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14485            ..Default::default()
14486        }));
14487        let overlay = servico_m2_overlay(&c).unwrap();
14488        assert!(
14489            overlay.contains_key(M2_KEY_BEHAVIOR),
14490            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
14491             `:behavior` carries a non-empty composite — the accessor \
14492             and the M2 overlay emitter must route through the same \
14493             substrate-primitive typed dispatch on the outer :behavior \
14494             composite (got overlay={overlay:?})",
14495        );
14496        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
14497        let overlay = servico_m2_overlay(&c).unwrap();
14498        assert!(
14499            !overlay.contains_key(M2_KEY_BEHAVIOR),
14500            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
14501             `:behavior` is Some(BehaviorSpec::default()) — the empty \
14502             composite's `.is_empty()`-gated inner arm must elide the \
14503             key regardless of the outer presence bit (got \
14504             overlay={overlay:?})",
14505        );
14506        let c = caixa_with_behavior(None);
14507        let overlay = servico_m2_overlay(&c).unwrap();
14508        assert!(
14509            !overlay.contains_key(M2_KEY_BEHAVIOR),
14510            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
14511             `:behavior` is None — the author-omitted arm must route \
14512             through the accessor's None-return unchanged (got \
14513             overlay={overlay:?})",
14514        );
14515    }
14516
14517    #[test]
14518    fn behavior_projects_option_ref_by_borrow() {
14519        // The by-borrow pin: [`Caixa::behavior`] returns
14520        // `Option<&BehaviorSpec>` by borrow — the returned reference
14521        // borrows the underlying `Option<BehaviorSpec>` storage of the
14522        // `:behavior` slot and the accessor must not clone the backing
14523        // composite on every call. Peer of the sibling
14524        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
14525        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
14526        // return sub-family — extended here to the second axis of the
14527        // same sub-family: the accessor's returned reference must
14528        // borrow from `&self` (the returned reference's lifetime is
14529        // tied to `&self`), and calling the accessor twice on the same
14530        // [`Caixa`] must yield references that are pointer-equal (the
14531        // underlying byte-buffer is the storage `BehaviorSpec`'s
14532        // allocation, not a fresh copy) as well as value-equal
14533        // (idempotent, no side effects on `&self`).
14534        //
14535        // Pins against a future silent detour that returned an owned
14536        // `BehaviorSpec` (which would type-check via the `Clone` impl
14537        // but silently clone on every call), a `&BehaviorSpec` panic-
14538        // return on the `None` arm (which would collapse the load-
14539        // bearing `Option` presence-bit into a runtime panic), or a
14540        // one-arm-only accessor that returned a saturating composite
14541        // on some sentinel input.
14542        use crate::BehaviorSpec;
14543        use std::path::PathBuf;
14544        for behavior in [
14545            Some(BehaviorSpec::default()),
14546            Some(BehaviorSpec {
14547                on_init: Some(PathBuf::from("lib/init.lisp")),
14548                on_call: Some(PathBuf::from("lib/handlers.lisp")),
14549                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
14550                on_info: Some(PathBuf::from("lib/handlers.lisp")),
14551                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14552                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
14553            }),
14554        ] {
14555            let c = caixa_with_behavior(behavior.clone());
14556            let first = c.behavior().unwrap();
14557            let second = c.behavior().unwrap();
14558            assert_eq!(
14559                first, second,
14560                "Caixa::behavior must be idempotent — two successive \
14561                 calls on the same &self must return the same \
14562                 &BehaviorSpec",
14563            );
14564            assert!(
14565                std::ptr::eq(first, second),
14566                "Caixa::behavior must borrow the underlying \
14567                 Option<BehaviorSpec> storage — two successive calls \
14568                 must return references with the same backing pointer \
14569                 (a fresh BehaviorSpec clone would change the pointer \
14570                 on every call)",
14571            );
14572            assert_eq!(
14573                Some(first),
14574                behavior.as_ref(),
14575                "Caixa::behavior must return :behavior verbatim by \
14576                 borrow — got {first:?}, expected {:?}",
14577                behavior.as_ref(),
14578            );
14579        }
14580        let c = caixa_with_behavior(None);
14581        assert!(
14582            c.behavior().is_none(),
14583            "Caixa::behavior must return None when :behavior is absent \
14584             — the author-omitted arm must project through the \
14585             accessor's Option::None unchanged",
14586        );
14587    }
14588
14589    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
14590
14591    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
14592        use crate::aplicacao::{Membro, WitContract};
14593        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14594        c.kind = CaixaKind::Aplicacao;
14595        c.membros = vec![Membro {
14596            caixa: "a".into(),
14597            versao: "^0.1".into(),
14598        }];
14599        c.contratos = vec![WitContract {
14600            de: "a".into(),
14601            para: "a".into(),
14602            wit: "wasi:http/proxy".into(),
14603            endpoint: Some("/x".into()),
14604            subject: None,
14605            slot: None,
14606        }];
14607        c.politicas = politicas;
14608        c
14609    }
14610
14611    #[test]
14612    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
14613        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
14614        // composite optional-composite-reference-shape pin:
14615        // [`Caixa::politicas`] must return the `:politicas` typed
14616        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
14617        // reference over the same backing storage the raw
14618        // `self.politicas.as_ref()` field access borrows from,
14619        // byte-equal across every representative fixture in the
14620        // accept-set — the author-omitted `None` shape (the "cluster-
14621        // default applies" partition every downstream mesh-artifact
14622        // emitter treats as "emit no `:politicas` overlay"), the
14623        // empty-composite `Some(MeshPolicy { .. default })` shape
14624        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
14625        // per-axis mesh-policy scalar is `None`, so the peer inner
14626        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
14627        // caixa-mesh overlay elides every per-axis emit but the outer
14628        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
14629        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
14630        // single-axis fixture (only `:timeout` set — the canonical
14631        // shape a latency-sensitive Aplicacao carries), and a
14632        // fully-populated composite (every per-axis mesh-policy
14633        // scalar set — the canonical shape a fully-governed
14634        // Aplicacao carries).
14635        //
14636        // Pins against a future silent detour that returned a fresh-
14637        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
14638        // type-check via the `Clone` impl but silently break every
14639        // downstream caller that relied on the reference sharing the
14640        // composite's backing identity), a reference to an operator-
14641        // resolved overlay (the future per-cluster
14642        // `:politicas-overrides` slot — its resolution must land at
14643        // exactly this accessor body, not silently divert the raw
14644        // slot away from the peer [`Caixa::declared_mesh_slots`]
14645        // enumerator's presence probe), a
14646        // `None` → `Some(MeshPolicy::default)` cluster-default
14647        // projection (which would collapse the load-bearing
14648        // "author-omitted `:politicas` ⇒ cluster-default applies"
14649        // partition the peer [`Caixa::declared_mesh_slots`]
14650        // enumerator and the peer [`Caixa::aplicacao_view`]
14651        // Aplicacao-composition seed both read), or an axis-shuffled
14652        // projection (a future detour that swapped `timeout` and
14653        // `retries` through the accessor would silently split the
14654        // paired [`Caixa::aplicacao_view`] seed's fold input from the
14655        // sibling M3 mesh-artifact emitter's projection input).
14656        //
14657        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
14658        // composite-reference accessor pin on the substrate primitive
14659        // — peer of the sibling
14660        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14661        // (b2bd9d7) and
14662        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14663        // (35d8b52) opening tetrad pins on the outer top-level
14664        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14665        // here to the first of the three M3 mesh-slot axes so the
14666        // opening third of the outer `Option<&Composite>` sub-family
14667        // carries the same "byte-equal, borrow-shared, presence-bit-
14668        // preserved" outer-accessor discipline.
14669        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
14670        use std::time::Duration;
14671        let fixtures: Vec<Option<MeshPolicy>> = vec![
14672            None,
14673            Some(MeshPolicy::default()),
14674            Some(MeshPolicy {
14675                timeout: Some(Duration::from_secs(30)),
14676                ..Default::default()
14677            }),
14678            Some(MeshPolicy {
14679                timeout: Some(Duration::from_secs(30)),
14680                retries: Some(3),
14681                circuit_breaker: Some(CircuitBreaker {
14682                    max_failures: 5,
14683                    window: Duration::from_secs(60),
14684                }),
14685                mtls_required: Some(true),
14686                rate_limit: Some(RateLimit {
14687                    rate: 100,
14688                    window: Duration::from_secs(1),
14689                }),
14690            }),
14691        ];
14692        for politicas in fixtures {
14693            let c = caixa_aplicacao_with_politicas(politicas.clone());
14694            assert_eq!(
14695                c.politicas(),
14696                politicas.as_ref(),
14697                "Caixa::politicas must return :politicas verbatim (got \
14698                 {:?}, expected {:?})",
14699                c.politicas(),
14700                politicas.as_ref(),
14701            );
14702            match (c.politicas(), c.politicas.as_ref()) {
14703                (Some(a), Some(b)) => assert!(
14704                    std::ptr::eq(a, b),
14705                    "Caixa::politicas accessor and self.politicas.as_ref() \
14706                     field access must borrow the same backing storage \
14707                     — the accessor is the substrate-primitive typed \
14708                     dispatch every downstream Aplicacao-mesh-overlay \
14709                     composite consumer must route through, and a \
14710                     reference-identity split would silently break \
14711                     every consumer that relied on the borrow sharing \
14712                     the composite's storage",
14713                ),
14714                (None, None) => {}
14715                _ => panic!(
14716                    "Caixa::politicas presence bit must byte-equal \
14717                     self.politicas.is_some() — a presence-bit drift \
14718                     would silently split the paired \
14719                     Caixa::aplicacao_view Aplicacao-composition seed's \
14720                     traversal head from the peer \
14721                     Caixa::declared_mesh_slots M3 declared-slot \
14722                     enumerator's presence probe",
14723                ),
14724            }
14725            assert_eq!(
14726                c.politicas().is_some(),
14727                c.politicas.is_some(),
14728                "Caixa::politicas().is_some() must byte-equal \
14729                 self.politicas.is_some() — a presence-bit drift would \
14730                 silently split every downstream Option<&MeshPolicy> \
14731                 consumer's partition on the cluster-default arm",
14732            );
14733        }
14734    }
14735
14736    #[test]
14737    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
14738        // Composition pin: [`Caixa::declared_mesh_slots`]'s
14739        // `:politicas` presence-probe arm must key off
14740        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
14741        // field-probe. Structurally: a `Caixa { politicas:
14742        // Some(MeshPolicy::default()), .. }` must still push
14743        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
14744        // presence bit is `Some`, so the M3 kind-coherence gate must
14745        // surface the slot as "declared" even when every per-axis
14746        // scalar is unset), and a `Caixa { politicas: None, .. }` must
14747        // NOT push the label (the "author omitted the slot entirely"
14748        // partition). The pair jointly pins the accessor + declared-
14749        // slot enumerator composition: any future silent detour that
14750        // had the accessor collapse `Some(MeshPolicy::default())` to
14751        // `None` (a `.filter(|p| !p.is_empty())` projection) would
14752        // silently absorb the "declared but empty" arm at the
14753        // accessor boundary and the
14754        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
14755        // coherence gate would silently accept a struct-literal
14756        // `Caixa` carrying the drift.
14757        //
14758        // Peer of the sibling
14759        // `declared_servico_slots_limits_arm_routes_through_accessor`
14760        // (b2bd9d7) and
14761        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14762        // (35d8b52) composition pins on the sibling `:limits` /
14763        // `:behavior` outer-`Option<&Composite>` arms of the peer
14764        // [`Caixa::declared_servico_slots`] M2 declared-slot
14765        // enumerator's traversal — same "the enumerator gate must
14766        // route through the substrate-primitive typed dispatch"
14767        // discipline extended onto the outer top-level [`Caixa`] M3
14768        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
14769        // enumerator carries the same routing invariant as its M2
14770        // sibling.
14771        use crate::aplicacao::MeshPolicy;
14772        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
14773        let slots = c.declared_mesh_slots();
14774        assert!(
14775            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
14776            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
14777             when `:politicas` is Some (even for MeshPolicy::default()) \
14778             — the accessor and the enumerator gate must route through \
14779             the same substrate-primitive typed dispatch on the outer \
14780             :politicas presence bit (got slots={slots:?})",
14781        );
14782        let c = caixa_aplicacao_with_politicas(None);
14783        let slots = c.declared_mesh_slots();
14784        assert!(
14785            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
14786            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
14787             when `:politicas` is None — the author-omitted arm must \
14788             route through the accessor's None-return unchanged (got \
14789             slots={slots:?})",
14790        );
14791    }
14792
14793    #[test]
14794    fn aplicacao_view_politicas_arm_folds_through_accessor() {
14795        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
14796        // Aplicacao-composition seed must fold through
14797        // [`Caixa::politicas`], not the raw
14798        // `self.politicas.clone().unwrap_or_default()` field-borrow.
14799        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
14800        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
14801        // must surface a projected [`crate::AplicacaoSpec`] whose
14802        // `politicas().timeout()` field byte-equals the outer
14803        // composite's `timeout` scalar (the fold must project the
14804        // authored composite verbatim), a `Caixa { politicas:
14805        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
14806        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
14807        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
14808        // fold's empty-composite arm collapses to the same default the
14809        // author-omitted arm does), and a `Caixa { politicas: None,
14810        // kind: Aplicacao, .. }` must surface an
14811        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
14812        // [`crate::aplicacao::MeshPolicy::default`] (the "author
14813        // omitted the slot entirely" arm folds through the
14814        // `unwrap_or_default` onto the cluster-default). The triad
14815        // jointly pins the accessor + Aplicacao-composition seed
14816        // composition: any future silent detour that had the accessor
14817        // divert the raw slot away from the seed's fold (an operator-
14818        // resolved overlay's default-fold arm silently differing from
14819        // the raw slot's default-fold arm) would silently split the
14820        // build-time mesh-artifact emission gate from the caixa-mesh
14821        // renderer's Aplicacao-view input at the composition boundary.
14822        use crate::aplicacao::MeshPolicy;
14823        use std::time::Duration;
14824        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
14825            timeout: Some(Duration::from_secs(30)),
14826            ..Default::default()
14827        }));
14828        let view = c.aplicacao_view().unwrap();
14829        assert_eq!(
14830            view.politicas().timeout(),
14831            Some(Duration::from_secs(30)),
14832            "Caixa::aplicacao_view must fold the authored :politicas \
14833             :timeout scalar through the accessor verbatim onto the \
14834             projected AplicacaoSpec — a future silent detour at the \
14835             seed's fold arm would surface here as a projected-scalar \
14836             drift (got {:?})",
14837            view.politicas().timeout(),
14838        );
14839        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
14840        let view = c.aplicacao_view().unwrap();
14841        assert_eq!(
14842            view.politicas(),
14843            &MeshPolicy::default(),
14844            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
14845             through the accessor onto MeshPolicy::default — the empty- \
14846             composite arm collapses to the same default the author- \
14847             omitted arm does (got {:?})",
14848            view.politicas(),
14849        );
14850        let c = caixa_aplicacao_with_politicas(None);
14851        let view = c.aplicacao_view().unwrap();
14852        assert_eq!(
14853            view.politicas(),
14854            &MeshPolicy::default(),
14855            "Caixa::aplicacao_view must fold None through the accessor's \
14856             unwrap_or_default onto MeshPolicy::default — the author- \
14857             omitted arm must route through the accessor's None-return \
14858             unchanged (got {:?})",
14859            view.politicas(),
14860        );
14861    }
14862
14863    #[test]
14864    fn politicas_projects_option_ref_by_borrow() {
14865        // The by-borrow pin: [`Caixa::politicas`] returns
14866        // `Option<&MeshPolicy>` by borrow — the returned reference
14867        // borrows the underlying `Option<MeshPolicy>` storage of the
14868        // `:politicas` slot and the accessor must not clone the
14869        // backing composite on every call. Peer of the sibling
14870        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
14871        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
14872        // pins on the outer top-level [`Caixa`]
14873        // `Option<&Composite>`-return sub-family — extended here to
14874        // the third axis of the same sub-family: the accessor's
14875        // returned reference must borrow from `&self` (the returned
14876        // reference's lifetime is tied to `&self`), and calling the
14877        // accessor twice on the same [`Caixa`] must yield references
14878        // that are pointer-equal (the underlying byte-buffer is the
14879        // storage `MeshPolicy`'s allocation, not a fresh copy) as
14880        // well as value-equal (idempotent, no side effects on
14881        // `&self`).
14882        //
14883        // Pins against a future silent detour that returned an owned
14884        // `MeshPolicy` (which would type-check via the `Clone` impl
14885        // but silently clone on every call), a `&MeshPolicy` panic-
14886        // return on the `None` arm (which would collapse the load-
14887        // bearing `Option` presence-bit into a runtime panic), or a
14888        // one-arm-only accessor that returned a saturating composite
14889        // on some sentinel input.
14890        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
14891        use std::time::Duration;
14892        for politicas in [
14893            Some(MeshPolicy::default()),
14894            Some(MeshPolicy {
14895                timeout: Some(Duration::from_secs(30)),
14896                retries: Some(3),
14897                circuit_breaker: Some(CircuitBreaker {
14898                    max_failures: 5,
14899                    window: Duration::from_secs(60),
14900                }),
14901                mtls_required: Some(true),
14902                rate_limit: Some(RateLimit {
14903                    rate: 100,
14904                    window: Duration::from_secs(1),
14905                }),
14906            }),
14907        ] {
14908            let c = caixa_aplicacao_with_politicas(politicas.clone());
14909            let first = c.politicas().unwrap();
14910            let second = c.politicas().unwrap();
14911            assert_eq!(
14912                first, second,
14913                "Caixa::politicas must be idempotent — two successive \
14914                 calls on the same &self must return the same \
14915                 &MeshPolicy",
14916            );
14917            assert!(
14918                std::ptr::eq(first, second),
14919                "Caixa::politicas must borrow the underlying \
14920                 Option<MeshPolicy> storage — two successive calls \
14921                 must return references with the same backing pointer \
14922                 (a fresh MeshPolicy clone would change the pointer on \
14923                 every call)",
14924            );
14925            assert_eq!(
14926                Some(first),
14927                politicas.as_ref(),
14928                "Caixa::politicas must return :politicas verbatim by \
14929                 borrow — got {first:?}, expected {:?}",
14930                politicas.as_ref(),
14931            );
14932        }
14933        let c = caixa_aplicacao_with_politicas(None);
14934        assert!(
14935            c.politicas().is_none(),
14936            "Caixa::politicas must return None when :politicas is \
14937             absent — the author-omitted arm must project through the \
14938             accessor's Option::None unchanged",
14939        );
14940    }
14941
14942    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
14943
14944    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
14945        use crate::aplicacao::{Membro, WitContract};
14946        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14947        c.kind = CaixaKind::Aplicacao;
14948        c.membros = vec![Membro {
14949            caixa: "a".into(),
14950            versao: "^0.1".into(),
14951        }];
14952        c.contratos = vec![WitContract {
14953            de: "a".into(),
14954            para: "a".into(),
14955            wit: "wasi:http/proxy".into(),
14956            endpoint: Some("/x".into()),
14957            subject: None,
14958            slot: None,
14959        }];
14960        c.placement = placement;
14961        c
14962    }
14963
14964    #[test]
14965    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
14966        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
14967        // composite optional-composite-reference-shape pin:
14968        // [`Caixa::placement`] must return the `:placement` typed
14969        // `Option<Placement>` verbatim as an `Option<&Placement>`
14970        // reference over the same backing storage the raw
14971        // `self.placement.as_ref()` field access borrows from,
14972        // byte-equal across every representative fixture in the
14973        // accept-set — the author-omitted `None` shape (the
14974        // "cluster-default applies" partition every downstream mesh-
14975        // artifact emitter treats as "emit no `:placement` overlay"),
14976        // the empty-composite `Some(Placement { .. default })` shape
14977        // (`estrategia: SingleNode`, empty clusters, no shard-key /
14978        // affinity — the outer presence-bit is `Some` so
14979        // [`Caixa::declared_mesh_slots`] still pushes the
14980        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
14981        // `Replicated`-on-two-clusters fixture (the canonical shape a
14982        // stateless HTTP Aplicacao carries), and a fully-populated
14983        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
14984        // shape a stateful Akka-style cluster-sharding Aplicacao
14985        // carries).
14986        //
14987        // Pins against a future silent detour that returned a fresh-
14988        // cloned [`crate::aplicacao::Placement`] copy (which would
14989        // type-check via the `Clone` impl but silently break every
14990        // downstream caller that relied on the reference sharing the
14991        // composite's backing identity), a reference to an operator-
14992        // resolved overlay (the future per-cluster
14993        // `:placement-overrides` slot — its resolution must land at
14994        // exactly this accessor body, not silently divert the raw
14995        // slot away from the peer [`Caixa::declared_mesh_slots`]
14996        // enumerator's presence probe), a `None` →
14997        // `Some(Placement::default)` cluster-default projection (which
14998        // would collapse the load-bearing "author-omitted `:placement`
14999        // ⇒ cluster-default applies" partition the peer
15000        // [`Caixa::declared_mesh_slots`] enumerator and the peer
15001        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
15002        // read), or an axis-shuffled projection (a future detour that
15003        // swapped `clusters` and `affinity` through the accessor would
15004        // silently split the paired [`Caixa::aplicacao_view`] seed's
15005        // fold input from the sibling M3 mesh-artifact emitter's
15006        // projection input).
15007        //
15008        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
15009        // composite-reference accessor pin on the substrate primitive
15010        // — peer of the sibling
15011        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15012        // (b2bd9d7),
15013        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
15014        // (35d8b52), and
15015        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
15016        // (5d23d29) opening triad pins on the outer top-level
15017        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
15018        // here to the second of the three M3 mesh-slot axes so the
15019        // opening four-fifths of the outer `Option<&Composite>` sub-
15020        // family carries the same "byte-equal, borrow-shared,
15021        // presence-bit-preserved" outer-accessor discipline.
15022        use crate::aplicacao::{Placement, PlacementStrategy};
15023        let fixtures: Vec<Option<Placement>> = vec![
15024            None,
15025            Some(Placement::default()),
15026            Some(Placement {
15027                estrategia: PlacementStrategy::Replicated,
15028                clusters: vec!["rio".into(), "sao-paulo".into()],
15029                affinity: None,
15030                shard_key: None,
15031            }),
15032            Some(Placement {
15033                estrategia: PlacementStrategy::Sharded,
15034                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
15035                affinity: Some("data-locality".into()),
15036                shard_key: Some("$tenantId".into()),
15037            }),
15038        ];
15039        for placement in fixtures {
15040            let c = caixa_aplicacao_with_placement(placement.clone());
15041            assert_eq!(
15042                c.placement(),
15043                placement.as_ref(),
15044                "Caixa::placement must return :placement verbatim (got \
15045                 {:?}, expected {:?})",
15046                c.placement(),
15047                placement.as_ref(),
15048            );
15049            match (c.placement(), c.placement.as_ref()) {
15050                (Some(a), Some(b)) => assert!(
15051                    std::ptr::eq(a, b),
15052                    "Caixa::placement accessor and self.placement.as_ref() \
15053                     field access must borrow the same backing storage \
15054                     — the accessor is the substrate-primitive typed \
15055                     dispatch every downstream Aplicacao-distribution- \
15056                     overlay composite consumer must route through, and \
15057                     a reference-identity split would silently break \
15058                     every consumer that relied on the borrow sharing \
15059                     the composite's storage",
15060                ),
15061                (None, None) => {}
15062                _ => panic!(
15063                    "Caixa::placement presence bit must byte-equal \
15064                     self.placement.is_some() — a presence-bit drift \
15065                     would silently split the paired \
15066                     Caixa::aplicacao_view Aplicacao-composition seed's \
15067                     traversal head from the peer \
15068                     Caixa::declared_mesh_slots M3 declared-slot \
15069                     enumerator's presence probe",
15070                ),
15071            }
15072            assert_eq!(
15073                c.placement().is_some(),
15074                c.placement.is_some(),
15075                "Caixa::placement().is_some() must byte-equal \
15076                 self.placement.is_some() — a presence-bit drift would \
15077                 silently split every downstream Option<&Placement> \
15078                 consumer's partition on the cluster-default arm",
15079            );
15080        }
15081    }
15082
15083    #[test]
15084    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
15085        // Composition pin: [`Caixa::declared_mesh_slots`]'s
15086        // `:placement` presence-probe arm must key off
15087        // [`Caixa::placement`], not the raw `self.placement.is_some()`
15088        // field-probe. Structurally: a `Caixa { placement:
15089        // Some(Placement::default()), .. }` must still push
15090        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
15091        // presence bit is `Some`, so the M3 kind-coherence gate must
15092        // surface the slot as "declared" even when every per-axis
15093        // scalar defers to the cluster-default arm), and a `Caixa {
15094        // placement: None, .. }` must NOT push the label (the "author
15095        // omitted the slot entirely" partition). The pair jointly pins
15096        // the accessor + declared-slot enumerator composition: any
15097        // future silent detour that had the accessor collapse
15098        // `Some(Placement::default())` to `None` (a `.filter(|p|
15099        // p.clusters().is_empty().not())` projection) would silently
15100        // absorb the "declared but empty" arm at the accessor boundary
15101        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
15102        // kind-coherence gate would silently accept a struct-literal
15103        // `Caixa` carrying the drift.
15104        //
15105        // Peer of the sibling
15106        // `declared_servico_slots_limits_arm_routes_through_accessor`
15107        // (b2bd9d7),
15108        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15109        // (35d8b52), and
15110        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
15111        // (5d23d29) composition pins on the sibling `:limits` /
15112        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
15113        // — same "the enumerator gate must route through the
15114        // substrate-primitive typed dispatch" discipline extended onto
15115        // the second of the three M3 mesh-slot axes so the
15116        // [`Caixa::declared_mesh_slots`] enumerator carries the same
15117        // routing invariant on the `:placement` arm as the peer
15118        // `:politicas` arm.
15119        use crate::aplicacao::Placement;
15120        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
15121        let slots = c.declared_mesh_slots();
15122        assert!(
15123            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
15124            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
15125             when `:placement` is Some (even for Placement::default()) \
15126             — the accessor and the enumerator gate must route through \
15127             the same substrate-primitive typed dispatch on the outer \
15128             :placement presence bit (got slots={slots:?})",
15129        );
15130        let c = caixa_aplicacao_with_placement(None);
15131        let slots = c.declared_mesh_slots();
15132        assert!(
15133            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
15134            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
15135             when `:placement` is None — the author-omitted arm must \
15136             route through the accessor's None-return unchanged (got \
15137             slots={slots:?})",
15138        );
15139    }
15140
15141    #[test]
15142    fn aplicacao_view_placement_arm_folds_through_accessor() {
15143        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
15144        // Aplicacao-composition seed must fold through
15145        // [`Caixa::placement`], not the raw
15146        // `self.placement.clone().unwrap_or_default()` field-borrow.
15147        // Structurally: a `Caixa { placement: Some(Placement {
15148        // estrategia: Replicated, clusters: ["rio"], .. default }),
15149        // kind: Aplicacao, .. }` must surface a projected
15150        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
15151        // `placement().clusters()` byte-equal the outer composite's
15152        // authored values (the fold must project the authored
15153        // composite verbatim), a `Caixa { placement:
15154        // Some(Placement::default()), kind: Aplicacao, .. }` must
15155        // surface an [`crate::AplicacaoSpec`] whose `placement()`
15156        // byte-equals [`crate::aplicacao::Placement::default`] (the
15157        // fold's empty-composite arm collapses to the same default
15158        // the author-omitted arm does), and a `Caixa { placement:
15159        // None, kind: Aplicacao, .. }` must surface an
15160        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
15161        // [`crate::aplicacao::Placement::default`] (the "author
15162        // omitted the slot entirely" arm folds through the
15163        // `unwrap_or_default` onto the cluster-default). The triad
15164        // jointly pins the accessor + Aplicacao-composition seed
15165        // composition: any future silent detour that had the accessor
15166        // divert the raw slot away from the seed's fold (an operator-
15167        // resolved overlay's default-fold arm silently differing from
15168        // the raw slot's default-fold arm) would silently split the
15169        // build-time distribution-artifact emission gate from the
15170        // caixa-mesh renderer's Aplicacao-view input at the
15171        // composition boundary.
15172        use crate::aplicacao::{Placement, PlacementStrategy};
15173        let c = caixa_aplicacao_with_placement(Some(Placement {
15174            estrategia: PlacementStrategy::Replicated,
15175            clusters: vec!["rio".into()],
15176            affinity: None,
15177            shard_key: None,
15178        }));
15179        let view = c.aplicacao_view().unwrap();
15180        assert_eq!(
15181            view.placement().estrategia(),
15182            PlacementStrategy::Replicated,
15183            "Caixa::aplicacao_view must fold the authored :placement \
15184             :estrategia scalar through the accessor verbatim onto the \
15185             projected AplicacaoSpec — a future silent detour at the \
15186             seed's fold arm would surface here as a projected-scalar \
15187             drift (got {:?})",
15188            view.placement().estrategia(),
15189        );
15190        assert_eq!(
15191            view.placement().clusters(),
15192            &["rio"],
15193            "Caixa::aplicacao_view must fold the authored :placement \
15194             :clusters list through the accessor verbatim onto the \
15195             projected AplicacaoSpec — a future silent detour at the \
15196             seed's fold arm would surface here as a projected-list \
15197             drift (got {:?})",
15198            view.placement().clusters(),
15199        );
15200        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
15201        let view = c.aplicacao_view().unwrap();
15202        assert_eq!(
15203            view.placement(),
15204            &Placement::default(),
15205            "Caixa::aplicacao_view must fold Some(Placement::default()) \
15206             through the accessor onto Placement::default — the empty- \
15207             composite arm collapses to the same default the author- \
15208             omitted arm does (got {:?})",
15209            view.placement(),
15210        );
15211        let c = caixa_aplicacao_with_placement(None);
15212        let view = c.aplicacao_view().unwrap();
15213        assert_eq!(
15214            view.placement(),
15215            &Placement::default(),
15216            "Caixa::aplicacao_view must fold None through the accessor's \
15217             unwrap_or_default onto Placement::default — the author- \
15218             omitted arm must route through the accessor's None-return \
15219             unchanged (got {:?})",
15220            view.placement(),
15221        );
15222    }
15223
15224    #[test]
15225    fn placement_projects_option_ref_by_borrow() {
15226        // The by-borrow pin: [`Caixa::placement`] returns
15227        // `Option<&Placement>` by borrow — the returned reference
15228        // borrows the underlying `Option<Placement>` storage of the
15229        // `:placement` slot and the accessor must not clone the
15230        // backing composite on every call. Peer of the sibling
15231        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
15232        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
15233        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
15234        // pins on the outer top-level [`Caixa`]
15235        // `Option<&Composite>`-return sub-family — extended here to
15236        // the fourth axis of the same sub-family: the accessor's
15237        // returned reference must borrow from `&self` (the returned
15238        // reference's lifetime is tied to `&self`), and calling the
15239        // accessor twice on the same [`Caixa`] must yield references
15240        // that are pointer-equal (the underlying byte-buffer is the
15241        // storage `Placement`'s allocation, not a fresh copy) as well
15242        // as value-equal (idempotent, no side effects on `&self`).
15243        //
15244        // Pins against a future silent detour that returned an owned
15245        // `Placement` (which would type-check via the `Clone` impl
15246        // but silently clone on every call), a `&Placement` panic-
15247        // return on the `None` arm (which would collapse the load-
15248        // bearing `Option` presence-bit into a runtime panic), or a
15249        // one-arm-only accessor that returned a saturating composite
15250        // on some sentinel input.
15251        use crate::aplicacao::{Placement, PlacementStrategy};
15252        for placement in [
15253            Some(Placement::default()),
15254            Some(Placement {
15255                estrategia: PlacementStrategy::Sharded,
15256                clusters: vec!["rio".into(), "sao-paulo".into()],
15257                affinity: Some("data-locality".into()),
15258                shard_key: Some("$tenantId".into()),
15259            }),
15260        ] {
15261            let c = caixa_aplicacao_with_placement(placement.clone());
15262            let first = c.placement().unwrap();
15263            let second = c.placement().unwrap();
15264            assert_eq!(
15265                first, second,
15266                "Caixa::placement must be idempotent — two successive \
15267                 calls on the same &self must return the same \
15268                 &Placement",
15269            );
15270            assert!(
15271                std::ptr::eq(first, second),
15272                "Caixa::placement must borrow the underlying \
15273                 Option<Placement> storage — two successive calls \
15274                 must return references with the same backing pointer \
15275                 (a fresh Placement clone would change the pointer on \
15276                 every call)",
15277            );
15278            assert_eq!(
15279                Some(first),
15280                placement.as_ref(),
15281                "Caixa::placement must return :placement verbatim by \
15282                 borrow — got {first:?}, expected {:?}",
15283                placement.as_ref(),
15284            );
15285        }
15286        let c = caixa_aplicacao_with_placement(None);
15287        assert!(
15288            c.placement().is_none(),
15289            "Caixa::placement must return None when :placement is \
15290             absent — the author-omitted arm must project through the \
15291             accessor's Option::None unchanged",
15292        );
15293    }
15294
15295    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
15296
15297    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
15298        use crate::aplicacao::{Membro, WitContract};
15299        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15300        c.kind = CaixaKind::Aplicacao;
15301        c.membros = vec![Membro {
15302            caixa: "a".into(),
15303            versao: "^0.1".into(),
15304        }];
15305        c.contratos = vec![WitContract {
15306            de: "a".into(),
15307            para: "a".into(),
15308            wit: "wasi:http/proxy".into(),
15309            endpoint: Some("/x".into()),
15310            subject: None,
15311            slot: None,
15312        }];
15313        c.entrada = entrada;
15314        c
15315    }
15316
15317    #[test]
15318    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
15319        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
15320        // composite optional-composite-reference-shape pin:
15321        // [`Caixa::entrada`] must return the `:entrada` typed
15322        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
15323        // reference over the same backing storage the raw
15324        // `self.entrada.as_ref()` field access borrows from,
15325        // byte-equal across every representative fixture in the
15326        // accept-set — the author-omitted `None` shape (the
15327        // "cluster-internal Aplicacao" partition every downstream
15328        // Gateway-API emitter treats as "emit no listener + no
15329        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
15330        // (empty `paths` — the resolved-paths fallback the peer
15331        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
15332        // onto the substrate catch-all), and a fully-populated
15333        // multi-path-with-non-default-port fixture (the canonical
15334        // shape a public HTTP Aplicacao carries).
15335        //
15336        // Pins against a future silent detour that returned a fresh-
15337        // cloned [`crate::aplicacao::Entrada`] copy (which would
15338        // type-check via the `Clone` impl but silently break every
15339        // downstream caller that relied on the reference sharing the
15340        // composite's backing identity), a reference to an operator-
15341        // resolved overlay (the future per-cluster
15342        // `:entrada-overrides` slot — its resolution must land at
15343        // exactly this accessor body, not silently divert the raw
15344        // slot away from the peer [`Caixa::declared_mesh_slots`]
15345        // enumerator's presence probe), or an axis-shuffled projection
15346        // (a future detour that swapped `host` and `para` through the
15347        // accessor would silently split the paired
15348        // [`Caixa::aplicacao_view`] seed's forward input from the
15349        // sibling M3 gateway-artifact emitter's projection input).
15350        //
15351        // Fifth and final outer top-level [`Caixa`]
15352        // `Option<&Composite>`-return composite-reference accessor pin
15353        // on the substrate primitive — peer of the sibling
15354        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15355        // (b2bd9d7),
15356        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
15357        // (35d8b52),
15358        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
15359        // (5d23d29), and
15360        // `placement_returns_placement_option_ref_verbatim_across_permutations`
15361        // (4fb8074) opening tetrad pins on the outer top-level
15362        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
15363        // here to the third and final M3 mesh-slot axis so the closed
15364        // outer `Option<&Composite>` sub-family carries the same
15365        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
15366        // accessor discipline across all five arms.
15367        use crate::aplicacao::Entrada;
15368        let fixtures: Vec<Option<Entrada>> = vec![
15369            None,
15370            Some(Entrada {
15371                host: "checkout.quero.cloud".into(),
15372                para: "gateway".into(),
15373                paths: Vec::new(),
15374                port: crate::DEFAULT_SERVICO_PORT,
15375            }),
15376            Some(Entrada {
15377                host: "api.pleme.io".into(),
15378                para: "public-api".into(),
15379                paths: vec!["/v1".into(), "/v2".into()],
15380                port: 8080,
15381            }),
15382        ];
15383        for entrada in fixtures {
15384            let c = caixa_aplicacao_with_entrada(entrada.clone());
15385            assert_eq!(
15386                c.entrada(),
15387                entrada.as_ref(),
15388                "Caixa::entrada must return :entrada verbatim (got \
15389                 {:?}, expected {:?})",
15390                c.entrada(),
15391                entrada.as_ref(),
15392            );
15393            match (c.entrada(), c.entrada.as_ref()) {
15394                (Some(a), Some(b)) => assert!(
15395                    std::ptr::eq(a, b),
15396                    "Caixa::entrada accessor and self.entrada.as_ref() \
15397                     field access must borrow the same backing storage \
15398                     — the accessor is the substrate-primitive typed \
15399                     dispatch every downstream Aplicacao-external- \
15400                     gateway composite consumer must route through, and \
15401                     a reference-identity split would silently break \
15402                     every consumer that relied on the borrow sharing \
15403                     the composite's storage",
15404                ),
15405                (None, None) => {}
15406                _ => panic!(
15407                    "Caixa::entrada presence bit must byte-equal \
15408                     self.entrada.is_some() — a presence-bit drift \
15409                     would silently split the paired \
15410                     Caixa::aplicacao_view Aplicacao-composition seed's \
15411                     traversal head from the peer \
15412                     Caixa::declared_mesh_slots M3 declared-slot \
15413                     enumerator's presence probe",
15414                ),
15415            }
15416            assert_eq!(
15417                c.entrada().is_some(),
15418                c.entrada.is_some(),
15419                "Caixa::entrada().is_some() must byte-equal \
15420                 self.entrada.is_some() — a presence-bit drift would \
15421                 silently split every downstream Option<&Entrada> \
15422                 consumer's partition on the cluster-internal arm",
15423            );
15424        }
15425    }
15426
15427    #[test]
15428    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
15429        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
15430        // presence-probe arm must key off [`Caixa::entrada`], not the
15431        // raw `self.entrada.is_some()` field-probe. Structurally: a
15432        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
15433        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
15434        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
15435        // presence bit is `Some`, so the M3 kind-coherence gate must
15436        // surface the slot as "declared" even when every per-axis
15437        // scalar defers to the substrate catch-all / default port),
15438        // and a `Caixa { entrada: None, .. }` must NOT push the label
15439        // (the "author omitted the slot entirely" partition). The pair
15440        // jointly pins the accessor + declared-slot enumerator
15441        // composition: any future silent detour that had the accessor
15442        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
15443        // `.filter(|e| !e.paths.is_empty())` projection) would silently
15444        // absorb the "declared but empty-paths" arm at the accessor
15445        // boundary and the
15446        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
15447        // coherence gate would silently accept a struct-literal
15448        // `Caixa` carrying the drift.
15449        //
15450        // Peer of the sibling
15451        // `declared_servico_slots_limits_arm_routes_through_accessor`
15452        // (b2bd9d7),
15453        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15454        // (35d8b52),
15455        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
15456        // (5d23d29), and
15457        // `declared_mesh_slots_placement_arm_routes_through_accessor`
15458        // (4fb8074) composition pins on the sibling `:limits` /
15459        // `:behavior` / `:politicas` / `:placement` outer-
15460        // `Option<&Composite>` arms — same "the enumerator gate must
15461        // route through the substrate-primitive typed dispatch"
15462        // discipline extended onto the third and final M3 mesh-slot
15463        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
15464        // carries the routing invariant on every M3 mesh-slot arm.
15465        use crate::aplicacao::Entrada;
15466        let c = caixa_aplicacao_with_entrada(Some(Entrada {
15467            host: "checkout.quero.cloud".into(),
15468            para: "gateway".into(),
15469            paths: Vec::new(),
15470            port: crate::DEFAULT_SERVICO_PORT,
15471        }));
15472        let slots = c.declared_mesh_slots();
15473        assert!(
15474            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
15475            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
15476             `:entrada` is Some (even for empty-paths / default-port) \
15477             — the accessor and the enumerator gate must route through \
15478             the same substrate-primitive typed dispatch on the outer \
15479             :entrada presence bit (got slots={slots:?})",
15480        );
15481        let c = caixa_aplicacao_with_entrada(None);
15482        let slots = c.declared_mesh_slots();
15483        assert!(
15484            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
15485            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
15486             when `:entrada` is None — the author-omitted arm must \
15487             route through the accessor's None-return unchanged (got \
15488             slots={slots:?})",
15489        );
15490    }
15491
15492    #[test]
15493    fn aplicacao_view_entrada_arm_folds_through_accessor() {
15494        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
15495        // Aplicacao-composition seed must fold through
15496        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
15497        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
15498        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
15499        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
15500        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
15501        // equals the outer composite's authored value (the fold must
15502        // project the authored composite verbatim), and a `Caixa {
15503        // entrada: None, kind: Aplicacao, .. }` must surface an
15504        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
15505        // "author omitted the slot entirely" arm folds through the
15506        // accessor's `Option::cloned` onto the same `None` presence
15507        // bit — unlike the peer `:politicas` / `:placement` arms
15508        // `:entrada` has no cluster-default fold, the omitted arm
15509        // stays omitted). The pair jointly pins the accessor +
15510        // Aplicacao-composition seed composition: any future silent
15511        // detour that had the accessor divert the raw slot away from
15512        // the seed's fold (an operator-resolved overlay's forward arm
15513        // silently differing from the raw slot's forward arm) would
15514        // silently split the build-time gateway-artifact emission gate
15515        // from the caixa-mesh renderer's Aplicacao-view input at the
15516        // composition boundary.
15517        use crate::aplicacao::Entrada;
15518        let authored = Entrada {
15519            host: "api.pleme.io".into(),
15520            para: "public-api".into(),
15521            paths: vec!["/v1".into()],
15522            port: 8080,
15523        };
15524        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
15525        let view = c.aplicacao_view().unwrap();
15526        assert_eq!(
15527            view.entrada(),
15528            Some(&authored),
15529            "Caixa::aplicacao_view must fold the authored :entrada \
15530             composite through the accessor verbatim onto the \
15531             projected AplicacaoSpec — a future silent detour at the \
15532             seed's fold arm would surface here as a projected- \
15533             composite drift (got {:?})",
15534            view.entrada(),
15535        );
15536        let c = caixa_aplicacao_with_entrada(None);
15537        let view = c.aplicacao_view().unwrap();
15538        assert!(
15539            view.entrada().is_none(),
15540            "Caixa::aplicacao_view must fold None through the \
15541             accessor's Option::cloned onto None — the author- \
15542             omitted arm must route through the accessor's None-return \
15543             unchanged (got {:?})",
15544            view.entrada(),
15545        );
15546    }
15547
15548    #[test]
15549    fn entrada_projects_option_ref_by_borrow() {
15550        // The by-borrow pin: [`Caixa::entrada`] returns
15551        // `Option<&Entrada>` by borrow — the returned reference
15552        // borrows the underlying `Option<Entrada>` storage of the
15553        // `:entrada` slot and the accessor must not clone the backing
15554        // composite on every call. Peer of the sibling
15555        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
15556        // `behavior_projects_option_ref_by_borrow` (35d8b52),
15557        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
15558        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
15559        // borrow pins on the outer top-level [`Caixa`]
15560        // `Option<&Composite>`-return sub-family — extended here to
15561        // the fifth and final axis of the same sub-family, closing
15562        // the discipline: the accessor's returned reference must
15563        // borrow from `&self` (the returned reference's lifetime is
15564        // tied to `&self`), and calling the accessor twice on the
15565        // same [`Caixa`] must yield references that are pointer-equal
15566        // (the underlying byte-buffer is the storage `Entrada`'s
15567        // allocation, not a fresh copy) as well as value-equal
15568        // (idempotent, no side effects on `&self`).
15569        //
15570        // Pins against a future silent detour that returned an owned
15571        // `Entrada` (which would type-check via the `Clone` impl but
15572        // silently clone on every call), a `&Entrada` panic-return on
15573        // the `None` arm (which would collapse the load-bearing
15574        // `Option` presence-bit into a runtime panic), or a one-arm-
15575        // only accessor that returned a saturating composite on some
15576        // sentinel input.
15577        use crate::aplicacao::Entrada;
15578        for entrada in [
15579            Some(Entrada {
15580                host: "checkout.quero.cloud".into(),
15581                para: "gateway".into(),
15582                paths: Vec::new(),
15583                port: crate::DEFAULT_SERVICO_PORT,
15584            }),
15585            Some(Entrada {
15586                host: "api.pleme.io".into(),
15587                para: "public-api".into(),
15588                paths: vec!["/v1".into(), "/v2".into()],
15589                port: 8080,
15590            }),
15591        ] {
15592            let c = caixa_aplicacao_with_entrada(entrada.clone());
15593            let first = c.entrada().unwrap();
15594            let second = c.entrada().unwrap();
15595            assert_eq!(
15596                first, second,
15597                "Caixa::entrada must be idempotent — two successive \
15598                 calls on the same &self must return the same &Entrada",
15599            );
15600            assert!(
15601                std::ptr::eq(first, second),
15602                "Caixa::entrada must borrow the underlying \
15603                 Option<Entrada> storage — two successive calls must \
15604                 return references with the same backing pointer (a \
15605                 fresh Entrada clone would change the pointer on every \
15606                 call)",
15607            );
15608            assert_eq!(
15609                Some(first),
15610                entrada.as_ref(),
15611                "Caixa::entrada must return :entrada verbatim by \
15612                 borrow — got {first:?}, expected {:?}",
15613                entrada.as_ref(),
15614            );
15615        }
15616        let c = caixa_aplicacao_with_entrada(None);
15617        assert!(
15618            c.entrada().is_none(),
15619            "Caixa::entrada must return None when :entrada is absent \
15620             — the author-omitted arm must project through the \
15621             accessor's Option::None unchanged",
15622        );
15623    }
15624
15625    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
15626
15627    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
15628        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15629        c.estrategia = estrategia;
15630        c
15631    }
15632
15633    #[test]
15634    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
15635        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
15636        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
15637        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
15638        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
15639        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
15640        // over the same discriminant the raw `self.estrategia` field
15641        // access carries, byte-equal across every representative fixture
15642        // in the accept-set — the author-omitted `None` shape (the
15643        // "defer to [`RestartStrategy::default`] through the
15644        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
15645        // every non-`Supervisor`-kind `defcaixa` carries by
15646        // `#[serde(default)]`), and each of the four closed-set variants
15647        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
15648        // / [`RestartStrategy::RestForOne`] /
15649        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
15650        // partitions on.
15651        //
15652        // Pins against a future silent detour that re-derived the
15653        // strategy from a peer axis (an accidental fallback to
15654        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
15655        // collapse that read the outer `:children` list-length axis into
15656        // the strategy discriminator at the accessor boundary), a
15657        // stale-derive detour that substituted [`RestartStrategy::default`]
15658        // when the outer `Option` held `None` (which would silently
15659        // collapse the load-bearing "author explicitly declared
15660        // `:estrategia OneForOne`" vs "author omitted the slot and
15661        // inherited the default" partition the [`Self::declared_supervisor_slots`]
15662        // presence-probe reads — the enumerator gate would still push
15663        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
15664        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
15665        // kind-coherence gate's traversal head from the
15666        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
15667        // composition head), a reference to an operator-resolved overlay
15668        // (the future per-cluster `:estrategia-overrides` slot — its
15669        // resolution must land at exactly this accessor body, not
15670        // silently divert the raw slot away from a second consumer), or
15671        // an axis-remap projection (a future detour that mapped
15672        // `OneForAll` through the accessor onto `OneForOne` would
15673        // silently split every downstream sibling-restart-strategy
15674        // consumer's per-arm fan-out).
15675        //
15676        // First outer top-level [`Caixa`] `Option<Copy>`-return
15677        // supervisor-tree-slot flat-spread accessor pin on the substrate
15678        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
15679        // projection pattern the sibling per-`Caixa` `:max-restarts` /
15680        // `:restart-window` future outer-scalar pins fold on. Peer of
15681        // the inner-altitude
15682        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
15683        // (eafb619) pin on the post-composition [`SupervisorSpec`]
15684        // altitude — same "the substrate-primitive accessor must byte-
15685        // equal the raw field access verbatim across every author-
15686        // declared value" discipline extended onto the pre-composition
15687        // outer author-surface [`Caixa`] altitude. Peer of the closed
15688        // outer-`Caixa` `Option<&Composite>` composite-reference family
15689        // the sibling `limits` / `behavior` / `politicas` / `placement` /
15690        // `entrada`
15691        // `..._returns_..._option_ref_verbatim_across_permutations` pins
15692        // already carry on the outer `Option<&Composite>` altitude.
15693        use crate::supervisor::RestartStrategy;
15694        let fixtures: Vec<Option<RestartStrategy>> = vec![
15695            None,
15696            Some(RestartStrategy::OneForOne),
15697            Some(RestartStrategy::OneForAll),
15698            Some(RestartStrategy::RestForOne),
15699            Some(RestartStrategy::SimpleOneForOne),
15700        ];
15701        for estrategia in fixtures {
15702            let c = caixa_with_estrategia(estrategia);
15703            assert_eq!(
15704                c.estrategia(),
15705                estrategia,
15706                "Caixa::estrategia must return :estrategia verbatim (got \
15707                 {:?}, expected {:?})",
15708                c.estrategia(),
15709                estrategia,
15710            );
15711            assert_eq!(
15712                c.estrategia(),
15713                c.estrategia,
15714                "Caixa::estrategia accessor and self.estrategia field \
15715                 access must byte-equal — the accessor is the substrate-\
15716                 primitive typed dispatch every downstream supervisor-\
15717                 tree flat-spread consumer must route through, and a \
15718                 discriminant split would silently break every consumer \
15719                 that relied on the accessor sharing the field's own \
15720                 Option<Copy> shape",
15721            );
15722            assert_eq!(
15723                c.estrategia().is_some(),
15724                c.estrategia.is_some(),
15725                "Caixa::estrategia().is_some() must byte-equal \
15726                 self.estrategia.is_some() — a presence-bit drift would \
15727                 silently split the paired Caixa::declared_supervisor_slots \
15728                 presence-probe arm from the Caixa::supervisor_view \
15729                 unwrap_or_default() fold's composition input",
15730            );
15731        }
15732    }
15733
15734    #[test]
15735    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
15736        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15737        // `:estrategia` presence-probe arm must key off
15738        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
15739        // field-probe. Structurally: every `Caixa { estrategia:
15740        // Some(RestartStrategy::_), .. }` variant must push
15741        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
15742        // (the presence bit is `Some` for every closed-set variant, so
15743        // the M2 supervisor-tree kind-coherence gate must surface the
15744        // slot as "declared" regardless of which variant the author
15745        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
15746        // the label (the "author omitted the slot entirely, deferring
15747        // to [`RestartStrategy::default`] through the supervisor_view
15748        // fold" partition). The pair jointly pins the accessor +
15749        // declared-slot enumerator composition: any future silent detour
15750        // that had the accessor collapse `Some(RestartStrategy::default())`
15751        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
15752        // projection) would silently absorb the "declared but default-
15753        // valued" arm at the accessor boundary and the
15754        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
15755        // coherence gate would silently accept a struct-literal `Caixa`
15756        // carrying the drift.
15757        //
15758        // Peer of the sibling per-`Caixa`
15759        // `declared_servico_slots_limits_arm_routes_through_accessor`
15760        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
15761        // `Option<&LimitsSpec>` composition axis — same "the enumerator
15762        // gate must route through the substrate-primitive typed
15763        // dispatch" discipline extended onto the flat-spread M2
15764        // supervisor-tree `Option<RestartStrategy>`-composition surface,
15765        // opening the outer-`Caixa` supervisor-tree-slot arm of the
15766        // composition-pin family.
15767        use crate::supervisor::RestartStrategy;
15768        for estrategia in [
15769            RestartStrategy::OneForOne,
15770            RestartStrategy::OneForAll,
15771            RestartStrategy::RestForOne,
15772            RestartStrategy::SimpleOneForOne,
15773        ] {
15774            let c = caixa_with_estrategia(Some(estrategia));
15775            let slots = c.declared_supervisor_slots();
15776            assert!(
15777                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
15778                "declared_supervisor_slots must push \
15779                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
15780                 Some({estrategia:?}) — the accessor and the enumerator \
15781                 gate must route through the same substrate-primitive \
15782                 typed dispatch on the outer :estrategia presence bit \
15783                 (got slots={slots:?})",
15784            );
15785        }
15786        let c = caixa_with_estrategia(None);
15787        let slots = c.declared_supervisor_slots();
15788        assert!(
15789            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
15790            "declared_supervisor_slots must NOT push \
15791             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
15792             — the author-omitted arm must route through the accessor's \
15793             None-return unchanged (got slots={slots:?})",
15794        );
15795    }
15796
15797    #[test]
15798    fn supervisor_view_estrategia_arm_routes_through_accessor() {
15799        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
15800        // [`SupervisorSpec`] construction arm must key off
15801        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
15802        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
15803        // for every `:kind Supervisor` `Caixa` carrying an author-
15804        // declared `Some(RestartStrategy::_)` variant, the composed
15805        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
15806        // outer accessor's declared variant unchanged; and for a
15807        // `:kind Supervisor` `Caixa` carrying `None`, the composed
15808        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
15809        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
15810        // arm the flat-spread `unwrap_or_default()` fold projects to on
15811        // the author-omitted arm — this is the *composition* between the
15812        // outer `Option<RestartStrategy>` accessor's presence-bit
15813        // surface and the inner post-composition non-`Option`
15814        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
15815        // pins the accessor + supervisor_view composition: any future
15816        // silent detour that had the accessor promote `None` to
15817        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
15818        // projection) would silently collapse the two arms into one at
15819        // the accessor boundary and the [`Self::declared_supervisor_slots`]
15820        // presence probe would silently drift from the composition site.
15821        //
15822        // Peer of the sibling M2 supervisor-slot post-composition
15823        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
15824        // pin on the [`SupervisorSpec::validate`] altitude — this pin
15825        // extends that inner-altitude accessor-routing discipline onto
15826        // the pre-composition outer author-surface [`Caixa`] altitude,
15827        // pinning the composition edge between the flat-spread outer
15828        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
15829        // `RestartStrategy` axes.
15830        use crate::CaixaKind;
15831        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
15832        for estrategia in [
15833            RestartStrategy::OneForOne,
15834            RestartStrategy::OneForAll,
15835            RestartStrategy::RestForOne,
15836            RestartStrategy::SimpleOneForOne,
15837        ] {
15838            let mut c = caixa_with_estrategia(Some(estrategia));
15839            c.kind = CaixaKind::Supervisor;
15840            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
15841            // shape partition through the [`gen_platform::IsVariant`]
15842            // derive-generated
15843            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
15844            // than the raw `matches!(estrategia, RestartStrategy::
15845            // SimpleOneForOne)` open-coded pattern-match — same closed-
15846            // set-typed-enum arm-discriminator dispatch discipline the
15847            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
15848            // convergence (915a934) extended onto its two paired positive
15849            // / negated `matches!` sites and the peer
15850            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
15851            // predicate convergence (766ec63) extended onto the M3 mesh-
15852            // slot per-`:placement` distribution-strategy discriminator
15853            // axis. See the sibling `supervisor::tests::
15854            // round_trip_all_strategies` and
15855            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
15856            // fixtures — the three sites (all test-only,
15857            // acknowledged in 915a934's Prior-commits footnote as the
15858            // outstanding follow-up) now consult one typed dispatch on
15859            // the substrate primitive.
15860            c.children = if estrategia.is_simple_one_for_one() {
15861                Vec::new()
15862            } else {
15863                vec![ChildSpec {
15864                    caixa: "worker".into(),
15865                    versao: "^0.1".into(),
15866                    restart: RestartPolicy::Permanent,
15867                }]
15868            };
15869            let view = c.supervisor_view().expect(
15870                "supervisor_view must materialize a SupervisorSpec for a \
15871                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
15872            );
15873            assert_eq!(
15874                view.estrategia(),
15875                c.estrategia().unwrap(),
15876                "supervisor_view must carry the outer Caixa::estrategia() \
15877                 declared variant onto the composed SupervisorSpec.estrategia \
15878                 field verbatim on the Some arm (got {:?}, expected {:?})",
15879                view.estrategia(),
15880                c.estrategia().unwrap(),
15881            );
15882        }
15883        // The author-omitted arm: outer `None` → composed
15884        // `RestartStrategy::default()` through the flat-spread
15885        // `unwrap_or_default()` fold.
15886        let mut c = caixa_with_estrategia(None);
15887        c.kind = CaixaKind::Supervisor;
15888        // Populate children so the sibling supervisor slots are coherent
15889        // for the [`Self::supervisor_view`] projection; the `:estrategia`
15890        // arm still defers to [`RestartStrategy::default`] on the
15891        // author-omitted arm even when the sibling slots carry values.
15892        c.children = vec![ChildSpec {
15893            caixa: "worker".into(),
15894            versao: "^0.1".into(),
15895            restart: RestartPolicy::Permanent,
15896        }];
15897        let view = c.supervisor_view().expect(
15898            "supervisor_view must materialize a SupervisorSpec for a \
15899             :kind Supervisor Caixa carrying a None `:estrategia` slot",
15900        );
15901        assert_eq!(
15902            view.estrategia(),
15903            RestartStrategy::default(),
15904            "supervisor_view must project the outer Caixa::estrategia() \
15905             None arm onto RestartStrategy::default() through the flat-\
15906             spread unwrap_or_default() fold (got {:?}, expected {:?})",
15907            view.estrategia(),
15908            RestartStrategy::default(),
15909        );
15910        assert!(
15911            c.estrategia().is_none(),
15912            "Caixa::estrategia() must remain None on the author-omitted \
15913             arm — the supervisor_view fold must not mutate the outer \
15914             flat-spread presence bit",
15915        );
15916    }
15917
15918    #[test]
15919    fn estrategia_projects_option_by_copy() {
15920        // The by-`Copy` pin: [`Caixa::estrategia`] returns
15921        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
15922        // the accessor does not borrow `&self` past the call (no
15923        // lifetime on the return type), and calling the accessor twice
15924        // on the same [`Caixa`] must yield discriminant-equal values
15925        // (idempotent, no side effects on `&self`). Peer of the sibling
15926        // outer-`Caixa` `Option<&Composite>` by-borrow
15927        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
15928        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
15929        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
15930        // `placement_projects_option_ref_by_borrow` (4fb8074) /
15931        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
15932        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
15933        // extended here to the outer-`Caixa` `Option<Copy>`-return
15934        // flat-spread axis. The `Copy` discipline replaces the pointer-
15935        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
15936        // `Copy` discriminant is definitionally the same discriminant, so
15937        // the axis reduces to discriminant equality).
15938        //
15939        // Pins against a future silent detour that returned a fresh
15940        // `Option<&RestartStrategy>` (which would type-check but silently
15941        // introduce a borrow of `&self` past the call, collapsing the
15942        // load-bearing "no lifetime on the return type" `Copy` projection
15943        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
15944        // read side effect that flipped the outer discriminant on
15945        // successive calls, or an axis-remap projection that returned a
15946        // different variant than the field storage.
15947        use crate::supervisor::RestartStrategy;
15948        for estrategia in [
15949            Some(RestartStrategy::OneForOne),
15950            Some(RestartStrategy::OneForAll),
15951            Some(RestartStrategy::RestForOne),
15952            Some(RestartStrategy::SimpleOneForOne),
15953        ] {
15954            let c = caixa_with_estrategia(estrategia);
15955            let first = c.estrategia();
15956            let second = c.estrategia();
15957            assert_eq!(
15958                first, second,
15959                "Caixa::estrategia must be idempotent — two successive \
15960                 calls on the same &self must return the same \
15961                 Option<RestartStrategy>",
15962            );
15963            assert_eq!(
15964                first, estrategia,
15965                "Caixa::estrategia must return :estrategia verbatim by \
15966                 Copy — got {first:?}, expected {estrategia:?}",
15967            );
15968        }
15969        let c = caixa_with_estrategia(None);
15970        assert!(
15971            c.estrategia().is_none(),
15972            "Caixa::estrategia must return None when :estrategia is \
15973             absent — the author-omitted arm must project through the \
15974             accessor's Option::None unchanged",
15975        );
15976    }
15977
15978    // ── Caixa::max_restarts / Caixa::restart_window —
15979    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
15980    //    (Option<u32> / Option<&str>) folding on the ed04d3c
15981    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
15982
15983    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
15984        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15985        c.max_restarts = max_restarts;
15986        c
15987    }
15988
15989    fn caixa_supervisor_with_max_restarts_and_window(
15990        max_restarts: Option<u32>,
15991        restart_window: Option<&str>,
15992    ) -> Caixa {
15993        use crate::CaixaKind;
15994        use crate::supervisor::{ChildSpec, RestartPolicy};
15995        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
15996        c.kind = CaixaKind::Supervisor;
15997        c.max_restarts = max_restarts;
15998        c.restart_window = restart_window.map(str::to_string);
15999        c.children = vec![ChildSpec {
16000            caixa: "worker".into(),
16001            versao: "^0.1".into(),
16002            restart: RestartPolicy::Permanent,
16003        }];
16004        c
16005    }
16006
16007    #[test]
16008    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
16009        // Value-shape pin: [`Caixa::max_restarts`] returns the
16010        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
16011        // from the typed slot's own storage, byte-equal across the
16012        // author-omitted `None` arm (the "defer to the
16013        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
16014        // `{intensity, 5, 60}` default" partition every
16015        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
16016        // and each of the representative fixtures in the accept-set —
16017        // `0` (the zero-floor arm the peer
16018        // [`crate::supervisor::SupervisorSpec::validate`]
16019        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
16020        // the post-composition altitude — the accessor must ship the
16021        // raw slot verbatim so struct-literal fixtures continue to
16022        // expose the zero at the accessor boundary), the OTP-canonical
16023        // `5` default (`{intensity, 5, 60}` worker-supervisor from
16024        // Learn You Some Erlang), `1000` (the
16025        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
16026        // upper-bound gate accepts on the boundary), `u32::MAX` (a
16027        // past-the-cap sentinel that the substrate-primitive accessor
16028        // must still ship verbatim). Second outer top-level
16029        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
16030        // pin — folds on the sibling
16031        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
16032        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
16033        // onto the sibling `Option<u32>` restart-budget-count arm.
16034        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
16035        for max_restarts in fixtures {
16036            let c = caixa_with_max_restarts(max_restarts);
16037            assert_eq!(
16038                c.max_restarts(),
16039                max_restarts,
16040                "Caixa::max_restarts must return :max-restarts verbatim \
16041                 (got {:?}, expected {max_restarts:?})",
16042                c.max_restarts(),
16043            );
16044            assert_eq!(
16045                c.max_restarts(),
16046                c.max_restarts,
16047                "Caixa::max_restarts accessor and self.max_restarts \
16048                 field access must byte-equal — a presence-bit or count \
16049                 drift would silently split the paired \
16050                 Caixa::declared_supervisor_slots presence-probe arm \
16051                 from the Caixa::supervisor_view unwrap_or(5) fold's \
16052                 composition input",
16053            );
16054        }
16055    }
16056
16057    #[test]
16058    fn max_restarts_projects_option_by_copy() {
16059        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
16060        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
16061        // borrow `&self` past the call (no lifetime on the return type),
16062        // and calling the accessor twice on the same [`Caixa`] must
16063        // yield equal values (idempotent, no side effects). Peer of the
16064        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
16065        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
16066        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
16067            let c = caixa_with_max_restarts(max_restarts);
16068            let first = c.max_restarts();
16069            let second = c.max_restarts();
16070            assert_eq!(
16071                first, second,
16072                "Caixa::max_restarts must be idempotent — two successive \
16073                 calls on the same &self must return the same Option<u32>",
16074            );
16075            assert_eq!(
16076                first, max_restarts,
16077                "Caixa::max_restarts must return :max-restarts verbatim \
16078                 by Copy — got {first:?}, expected {max_restarts:?}",
16079            );
16080        }
16081    }
16082
16083    #[test]
16084    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
16085        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16086        // `:max-restarts` presence-probe arm must key off
16087        // [`Caixa::max_restarts`], not the raw
16088        // `self.max_restarts.is_some()` field-probe. Structurally: every
16089        // `Caixa { max_restarts: Some(_), .. }` variant must push
16090        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
16091        // list (the presence bit is `Some` for every representative
16092        // count, so the M2 kind-coherence gate must surface the slot as
16093        // "declared"), and a `Caixa { max_restarts: None, .. }` must
16094        // NOT push the label. Peer of the sibling
16095        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
16096        // (ed04d3c) composition pin — same routing-through-accessor
16097        // discipline extended onto the sibling flat-spread `Option<u32>`
16098        // arm.
16099        for max_restarts in [0u32, 5, 1000, u32::MAX] {
16100            let c = caixa_with_max_restarts(Some(max_restarts));
16101            let slots = c.declared_supervisor_slots();
16102            assert!(
16103                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
16104                "declared_supervisor_slots must push \
16105                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
16106                 is Some({max_restarts}) — the accessor and the \
16107                 enumerator gate must route through the same \
16108                 substrate-primitive typed dispatch on the outer \
16109                 :max-restarts presence bit (got slots={slots:?})",
16110            );
16111        }
16112        let c = caixa_with_max_restarts(None);
16113        let slots = c.declared_supervisor_slots();
16114        assert!(
16115            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
16116            "declared_supervisor_slots must NOT push \
16117             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
16118             None — the author-omitted arm must route through the \
16119             accessor's None-return unchanged (got slots={slots:?})",
16120        );
16121    }
16122
16123    #[test]
16124    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
16125        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
16126        // [`SupervisorSpec`] construction arm must key off
16127        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
16128        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
16129        // every `:kind Supervisor` `Caixa` carrying an author-declared
16130        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
16131        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
16132        // carrying `None`, the composed [`SupervisorSpec`]'s
16133        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
16134        // of the sibling
16135        // `supervisor_view_estrategia_arm_routes_through_accessor`
16136        // (ed04d3c) composition pin.
16137        for max_restarts in [1u32, 5, 1000] {
16138            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
16139            let view = c.supervisor_view().expect(
16140                "supervisor_view must materialize a SupervisorSpec for a \
16141                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
16142            );
16143            assert_eq!(
16144                view.max_restarts(),
16145                max_restarts,
16146                "supervisor_view must carry the outer \
16147                 Caixa::max_restarts() Some arm onto the composed \
16148                 SupervisorSpec.max_restarts field verbatim (got {}, \
16149                 expected {max_restarts})",
16150                view.max_restarts(),
16151            );
16152        }
16153        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
16154        let view = c.supervisor_view().expect(
16155            "supervisor_view must materialize a SupervisorSpec for a \
16156             :kind Supervisor Caixa carrying a None :max-restarts",
16157        );
16158        assert_eq!(
16159            view.max_restarts(),
16160            5,
16161            "supervisor_view must project the outer \
16162             Caixa::max_restarts() None arm onto the OTP-canonical \
16163             {{intensity, 5, 60}} default (5) through the flat-spread \
16164             unwrap_or(5) fold (got {})",
16165            view.max_restarts(),
16166        );
16167        assert!(
16168            c.max_restarts().is_none(),
16169            "Caixa::max_restarts() must remain None on the author-\
16170             omitted arm — the supervisor_view fold must not mutate \
16171             the outer flat-spread presence bit",
16172        );
16173    }
16174
16175    #[test]
16176    fn supervisor_view_estrategia_fallback_routes_through_lifted_default() {
16177        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
16178        // `:estrategia` arm must degrade onto the substrate-canonical
16179        // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
16180        // `pub const` — the Erlang/OTP-canonical `one_for_one` strategy
16181        // half of Learn You Some Erlang's `{one_for_one, intensity, 5, 60}`
16182        // worker-supervisor default — rather than the transitively-
16183        // derived [`crate::supervisor::RestartStrategy::default`] route
16184        // the prior `.unwrap_or_default()` fold reached for. Prior to the
16185        // lift the composition site carried `.unwrap_or_default()` with
16186        // no compile-time link back to the shared OTP-canonical strategy
16187        // default that the paired [`crate::supervisor::Default for
16188        // RestartStrategy`] impl and the [`crate::supervisor::Default for
16189        // SupervisorSpec`] impl's struct-literal `estrategia` field both
16190        // (now) route through the same lifted constant — so a future
16191        // rebrand of the OTP-canonical strategy default (an OTP
16192        // `rest_for_one` widening once the substrate discovers startup-
16193        // order-coupled child cohorts as the more common worker-
16194        // supervisor shape, a per-cluster overlay the operator pins
16195        // through the MESH-COMPOSITION §III.2 supervision-canary
16196        // `:estrategia-overrides` roadmap slot) would have had to migrate
16197        // the paired `MaxIntensity` + `Period` halves through the lifted
16198        // constants and the `one_for_one` half through a
16199        // `RestartStrategy::default()` route in lockstep or a
16200        // `:kind Supervisor` caixa carrying an author-omitted
16201        // `:estrategia` slot would silently resolve to a `SupervisorSpec`
16202        // whose `estrategia` disagreed with the paired
16203        // `SupervisorSpec::default()` view. Byte-parity against the
16204        // lifted constant closes the split. Peer of the sibling
16205        // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
16206        // composition pin on the paired `MaxIntensity` half + the
16207        // [`crate::supervisor::restart_strategy_default_routes_through_lifted_default`]
16208        // + [`crate::supervisor::supervisor_spec_default_estrategia_routes_through_lifted_default`]
16209        // pins on the sibling entry points onto the shared substrate
16210        // constant.
16211        use crate::CaixaKind;
16212        use crate::supervisor::{ChildSpec, RestartPolicy};
16213        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
16214        c.kind = CaixaKind::Supervisor;
16215        c.estrategia = None;
16216        c.children = vec![ChildSpec {
16217            caixa: "worker".into(),
16218            versao: "^0.1".into(),
16219            restart: RestartPolicy::Permanent,
16220        }];
16221        let view = c.supervisor_view().expect(
16222            "supervisor_view must materialize a SupervisorSpec for a \
16223             :kind Supervisor Caixa carrying a None :estrategia",
16224        );
16225        assert_eq!(
16226            view.estrategia(),
16227            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
16228            "supervisor_view must degrade the outer \
16229             Caixa::estrategia() None arm onto the lifted \
16230             SUPERVISOR_ESTRATEGIA_DEFAULT typed pub const (got {:?}, \
16231             expected {:?})",
16232            view.estrategia(),
16233            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
16234        );
16235    }
16236
16237    #[test]
16238    fn supervisor_view_max_restarts_fallback_routes_through_lifted_default() {
16239        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
16240        // `:max-restarts` arm must degrade onto the substrate-canonical
16241        // [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`] typed
16242        // `pub const` — the Erlang/OTP-canonical `{intensity, 5, 60}`
16243        // `MaxIntensity` default — rather than a raw `5` literal. Prior
16244        // to the lift the composition site carried an inline
16245        // `.unwrap_or(5)` with no compile-time link back to the shared
16246        // OTP-canonical default that the serde-side
16247        // `#[serde(default = "default_max_restarts")]` wire-format arm
16248        // and the [`Default for crate::supervisor::SupervisorSpec`]
16249        // struct-literal default arm both key off — so a future rebrand
16250        // of the OTP-canonical default (Elixir's `Supervisor` `3`
16251        // default, a per-cluster overlay the operator pins through the
16252        // MESH-COMPOSITION §III.2 supervision-canary
16253        // `:supervisor :max-restarts-overrides` roadmap slot) would
16254        // have had to be threaded through both the serde-side helper
16255        // and this view-construction arm in lockstep or a `:kind
16256        // Supervisor` caixa carrying `:max-restarts ()` would silently
16257        // resolve to a `SupervisorSpec` whose `max_restarts` disagreed
16258        // with the same fixture's serde-side `SupervisorSpec` view (an
16259        // author-omitted slot round-tripping through
16260        // `SupervisorSpec::default()` to the lifted constant, then
16261        // splitting to a stale literal past `supervisor_view`).
16262        // Byte-parity against the lifted constant closes the split.
16263        // Peer of the sibling
16264        // [`crate::supervisor::default_max_restarts_helper_routes_through_lifted_default`]
16265        // + [`crate::supervisor::supervisor_spec_default_max_restarts_routes_through_lifted_default`]
16266        // composition pins that close the same routing on the two
16267        // sibling entry points onto the shared substrate constant.
16268        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
16269        let view = c.supervisor_view().expect(
16270            "supervisor_view must materialize a SupervisorSpec for a \
16271             :kind Supervisor Caixa carrying a None :max-restarts",
16272        );
16273        assert_eq!(
16274            view.max_restarts(),
16275            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
16276            "supervisor_view must degrade the outer \
16277             Caixa::max_restarts() None arm onto the lifted \
16278             SUPERVISOR_MAX_RESTARTS_DEFAULT typed pub const (got {}, \
16279             expected {})",
16280            view.max_restarts(),
16281            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
16282        );
16283    }
16284
16285    #[test]
16286    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
16287        // Value-shape pin: [`Caixa::restart_window`] returns the
16288        // `:restart-window` typed `Option<String>` verbatim as an
16289        // `Option<&str>`, borrowed from the typed slot's own storage,
16290        // byte-equal across the author-omitted `None` arm and each of
16291        // the representative fixtures in the accept-set — the canonical
16292        // `"60s"` from `{intensity, 5, 60}`, the sibling
16293        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
16294        // / `"0s"`) the shared codec's positive-set sweep pin covers,
16295        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
16296        // seconds drift the sibling [`Self::validate_restart_window`]
16297        // gate refuses; the accessor must ship the raw slot verbatim
16298        // so struct-literal fixtures continue to expose the drift at
16299        // the accessor boundary). Third outer top-level [`Caixa`]
16300        // supervisor-tree flat-spread pin — extends the sub-family onto
16301        // the sibling `Option<&str>` raw-duration-string arm.
16302        for window in [
16303            None,
16304            Some("60s"),
16305            Some("5m"),
16306            Some("1h"),
16307            Some("500ms"),
16308            Some("1.5s"),
16309            Some(""),
16310        ] {
16311            let c = caixa_with_restart_window(window);
16312            assert_eq!(
16313                c.restart_window(),
16314                window,
16315                "Caixa::restart_window must return :restart-window \
16316                 verbatim as Option<&str> (got {:?}, expected {window:?})",
16317                c.restart_window(),
16318            );
16319            assert_eq!(
16320                c.restart_window(),
16321                c.restart_window.as_deref(),
16322                "Caixa::restart_window accessor and \
16323                 self.restart_window.as_deref() field access must \
16324                 byte-equal — a byte-level drift would silently split \
16325                 the paired Caixa::declared_supervisor_slots \
16326                 presence-probe arm from the \
16327                 Caixa::validate_restart_window shared-codec gate and \
16328                 the Caixa::supervisor_view soft-swallowing fold",
16329            );
16330        }
16331    }
16332
16333    #[test]
16334    fn restart_window_projects_slice_by_borrow() {
16335        // The by-borrow pin: [`Caixa::restart_window`] returns
16336        // `Option<&str>` by borrow — the returned string slice borrows
16337        // the underlying `Option<String>` storage of the `:restart-window`
16338        // slot and the accessor must not clone on every call. Peer of
16339        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
16340        // by-borrow pins on the universal-axis scalar family
16341        // (`licenca_projects_option_ref_by_borrow` /
16342        // `descricao_projects_option_ref_by_borrow` and siblings) —
16343        // extended onto the M2 supervisor-tree flat-spread
16344        // `Option<&str>` raw-duration-string axis.
16345        for window in [None, Some("60s"), Some("5m"), Some("")] {
16346            let c = caixa_with_restart_window(window);
16347            let first = c.restart_window();
16348            let second = c.restart_window();
16349            assert_eq!(
16350                first, second,
16351                "Caixa::restart_window must be idempotent — two \
16352                 successive calls on the same &self must return the \
16353                 same Option<&str>",
16354            );
16355            if let (Some(a), Some(b)) = (first, second) {
16356                assert_eq!(
16357                    a.as_ptr(),
16358                    b.as_ptr(),
16359                    "Caixa::restart_window must borrow the underlying \
16360                     String storage — two successive Some-arm calls must \
16361                     return slices with the same backing pointer (a fresh \
16362                     String clone would change the pointer on every call)",
16363                );
16364            }
16365            assert_eq!(
16366                first, window,
16367                "Caixa::restart_window must return :restart-window \
16368                 verbatim by borrow — got {first:?}, expected {window:?}",
16369            );
16370        }
16371    }
16372
16373    #[test]
16374    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
16375        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16376        // `:restart-window` presence-probe arm must key off
16377        // [`Caixa::restart_window`], not the raw
16378        // `self.restart_window.is_some()` field-probe. Structurally:
16379        // every `Caixa { restart_window: Some(_), .. }` must push
16380        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
16381        // list, and a `Caixa { restart_window: None, .. }` must NOT
16382        // push the label. Peer of the sibling
16383        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
16384        // routing pin.
16385        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
16386            let c = caixa_with_restart_window(Some(window));
16387            let slots = c.declared_supervisor_slots();
16388            assert!(
16389                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
16390                "declared_supervisor_slots must push \
16391                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
16392                 `:restart-window` is Some({window:?}) — the accessor \
16393                 and the enumerator gate must route through the same \
16394                 substrate-primitive typed dispatch on the outer \
16395                 :restart-window presence bit (got slots={slots:?})",
16396            );
16397        }
16398        let c = caixa_with_restart_window(None);
16399        let slots = c.declared_supervisor_slots();
16400        assert!(
16401            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
16402            "declared_supervisor_slots must NOT push \
16403             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
16404             is None — the author-omitted arm must route through the \
16405             accessor's None-return unchanged (got slots={slots:?})",
16406        );
16407    }
16408
16409    #[test]
16410    fn validate_restart_window_arm_routes_through_accessor() {
16411        // Composition pin: [`Caixa::validate_restart_window`]'s
16412        // shared-codec fold arm must key off [`Caixa::restart_window`],
16413        // not the raw `self.restart_window.as_deref()` field-projection.
16414        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
16415        // express no reset" canonical shape); (2) a canonical `Some`
16416        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
16417        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
16418        // .. })` carrying the offending raw string verbatim. The three
16419        // arms jointly pin that the validator's raw-string binding is
16420        // the accessor's return, not a peer projection — any future
16421        // silent detour that had the accessor collapse `Some("")` to
16422        // `None` would silently absorb the empty-after-trim refusal
16423        // case at the accessor boundary.
16424        caixa_with_restart_window(None)
16425            .validate_restart_window()
16426            .expect("None :restart-window must validate through the accessor");
16427        caixa_with_restart_window(Some("60s"))
16428            .validate_restart_window()
16429            .expect("canonical :restart-window \"60s\" must validate through the accessor");
16430        let err = caixa_with_restart_window(Some("1.5s"))
16431            .validate_restart_window()
16432            .expect_err("fractional-seconds :restart-window must fail through the accessor");
16433        assert!(
16434            matches!(
16435                err,
16436                ManifestError::RestartWindowMalformed { ref restart_window, .. }
16437                    if restart_window == "1.5s"
16438            ),
16439            "validator must carry the offending raw string verbatim \
16440             from the accessor's borrowed &str (got {err:?})",
16441        );
16442    }
16443
16444    #[test]
16445    fn supervisor_view_restart_window_arm_routes_through_accessor() {
16446        // Composition pin: [`Caixa::supervisor_view`]'s
16447        // per-`:restart-window` [`SupervisorSpec`] construction arm
16448        // must key off [`Caixa::restart_window`]'s soft-swallowing
16449        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
16450        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
16451        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
16452        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
16453        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
16454        // (the shared codec's canonical parse); (3) codec-rejected
16455        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
16456        // (the soft-swallow preserving the view's best-effort shape).
16457        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
16458        let view = c.supervisor_view().expect("Supervisor kind has a view");
16459        assert_eq!(
16460            view.restart_window(),
16461            None,
16462            "supervisor_view must project outer None :restart-window \
16463             onto None on the composed SupervisorSpec (never-reset \
16464             sentinel) through the accessor's None-return unchanged",
16465        );
16466
16467        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
16468        let view = c.supervisor_view().expect("Supervisor kind has a view");
16469        assert_eq!(
16470            view.restart_window(),
16471            Some(std::time::Duration::from_secs(60)),
16472            "supervisor_view must fold outer Some(\"60s\") through the \
16473             shared duration_codec into Duration::from_secs(60) on the \
16474             composed SupervisorSpec (accessor's Some(&str) → codec \
16475             parse → Some(Duration))",
16476        );
16477
16478        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
16479        let view = c.supervisor_view().expect("Supervisor kind has a view");
16480        assert_eq!(
16481            view.restart_window(),
16482            None,
16483            "supervisor_view must soft-swallow the shared-codec parse \
16484             failure to None (the view's best-effort shape the sibling \
16485             manifest-level validate_restart_window surfaces as \
16486             RestartWindowMalformed); the accessor's raw-string return \
16487             is the single input every downstream consumer keys off",
16488        );
16489    }
16490
16491    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
16492
16493    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
16494        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16495        c.upgrade_from = upgrade_from;
16496        c
16497    }
16498
16499    #[test]
16500    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
16501        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
16502        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
16503        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
16504        // typed `Vec<UpgradeFromEntry>` verbatim as a
16505        // `&[UpgradeFromEntry]` slice-view over the same backing
16506        // buffer the raw `self.upgrade_from.as_slice()` field access
16507        // borrows from, element-equal across every representative
16508        // fixture in the accept-set — `[]` (the "no hot-upgrade path
16509        // declared" arm every `defcaixa` without an `:upgrade-from`
16510        // block carries; `#[serde(default)]` folds an omitted slot
16511        // onto `Vec::new()`), a canonical single-entry `Restart`
16512        // fixture (the shape most Servicos carry — a single prior
16513        // version with the fallback strategy), a canonical multi-
16514        // entry list carrying every typed instruction variant
16515        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
16516        // `Restart`), and a past-the-guard sentinel — a duplicate-
16517        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
16518        // ([`crate::upgrade::validate_upgrade_from`] rejects through
16519        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
16520        // ship the raw slot verbatim so struct-literal fixtures
16521        // continue to expose the duplicate at the accessor boundary).
16522        //
16523        // Pins against a future silent detour that returned an owned
16524        // `Vec<UpgradeFromEntry>` (which would type-check but silently
16525        // clone on every accessor call, breaking the zero-cost
16526        // projection every peer sibling slice accessor carries), a
16527        // `[dup, dup] → [dup]` dedup collapse (which would silently
16528        // absorb the `DuplicateFrom` refusal case at the accessor
16529        // boundary and the [`crate::StandardLayout::verify`] cross-
16530        // entry gate would silently accept a struct-literal `Caixa`
16531        // carrying the drift), a reference to an operator-resolved
16532        // overlay (the future per-cluster `:upgrade-overrides` slot
16533        // — its resolution must land at exactly this accessor body,
16534        // not silently divert the raw slot away from a second
16535        // consumer), or an axis-shuffled projection (a future detour
16536        // that reordered entries through the accessor would silently
16537        // split the paired [`crate::StandardLayout::verify`] per-
16538        // `:upgrade-from` shape gate's traversal input from the peer
16539        // [`crate::render::servico_m2_overlay`] emitter's projection
16540        // input, since the operator's hot-upgrade dispatch matches
16541        // per-`:from` and axis reordering would silently split the
16542        // per-entry script-path existence probe's iteration order
16543        // from the M2 overlay emitter's serialized-entry order).
16544        //
16545        // First outer top-level [`Caixa`] `&[Composite]`-return
16546        // slice accessor pin on the substrate primitive for M2 / M3
16547        // typed-slot vec-carry axes — opens the outer-`Caixa`
16548        // `&[Composite]` composite-slice projection pattern the
16549        // sibling `:children` [`crate::supervisor::ChildSpec`] /
16550        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
16551        // [`crate::aplicacao::WitContract`] future outer-composite-
16552        // slice pins fold on. Peer of the closed outer-`Caixa`
16553        // scalar `Option<&Composite>` composite-reference family the
16554        // sibling `limits` / `behavior` / `politicas` / `placement`
16555        // / `entrada` `..._returns_..._option_ref_verbatim_across_
16556        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
16557        // the "byte-equal, borrow-shared" outer-accessor discipline
16558        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
16559        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16560        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
16561            vec![],
16562            vec![UpgradeFromEntry {
16563                from: "0.0.1".into(),
16564                instructions: vec![UpgradeInstruction::Restart],
16565            }],
16566            vec![
16567                UpgradeFromEntry {
16568                    from: "0.0.1".into(),
16569                    instructions: vec![
16570                        UpgradeInstruction::LoadModule {
16571                            module: "demo".into(),
16572                        },
16573                        UpgradeInstruction::SoftPurge {
16574                            module: "demo".into(),
16575                        },
16576                    ],
16577                },
16578                UpgradeFromEntry {
16579                    from: "0.0.2".into(),
16580                    instructions: vec![
16581                        UpgradeInstruction::StateChange {
16582                            script: "servicos/upgrade.lisp".into(),
16583                        },
16584                        UpgradeInstruction::Purge {
16585                            module: "demo".into(),
16586                        },
16587                        UpgradeInstruction::Restart,
16588                    ],
16589                },
16590            ],
16591            vec![
16592                UpgradeFromEntry {
16593                    from: "0.1.0".into(),
16594                    instructions: vec![UpgradeInstruction::Restart],
16595                },
16596                UpgradeFromEntry {
16597                    from: "0.1.0".into(),
16598                    instructions: vec![UpgradeInstruction::Restart],
16599                },
16600            ],
16601        ];
16602        for upgrade_from in fixtures {
16603            let c = caixa_with_upgrade_from(upgrade_from.clone());
16604            assert_eq!(
16605                c.upgrade_from(),
16606                upgrade_from.as_slice(),
16607                "Caixa::upgrade_from must return :upgrade-from \
16608                 verbatim (got {:?}, expected {upgrade_from:?})",
16609                c.upgrade_from(),
16610            );
16611            assert_eq!(
16612                c.upgrade_from(),
16613                c.upgrade_from.as_slice(),
16614                "Caixa::upgrade_from must element-equal the raw \
16615                 `self.upgrade_from.as_slice()` field access across \
16616                 every value in the Vec<UpgradeFromEntry> accept-set",
16617            );
16618            assert_eq!(
16619                c.upgrade_from().is_empty(),
16620                c.upgrade_from.is_empty(),
16621                "Caixa::upgrade_from().is_empty() must byte-equal \
16622                 self.upgrade_from.is_empty() — a presence-bit drift \
16623                 would silently split the paired \
16624                 Caixa::declared_servico_slots M2 declared-slot \
16625                 enumerator's presence probe from the peer \
16626                 crate::render::servico_m2_overlay M2 overlay \
16627                 emitter's presence gate",
16628            );
16629        }
16630    }
16631
16632    #[test]
16633    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
16634        // Composition pin: [`Caixa::declared_servico_slots`]'s
16635        // `:upgrade-from` presence-probe arm must key off
16636        // [`Caixa::upgrade_from`], not the raw
16637        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
16638        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
16639        // instructions: vec![Restart] }], .. }` must push
16640        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
16641        // (the presence bit is non-empty, so the M2 kind-coherence
16642        // gate must surface the slot as "declared"), and a `Caixa {
16643        // upgrade_from: vec![], .. }` must NOT push the label (the
16644        // "author omitted the slot entirely" arm — the empty-slice
16645        // partition the serde-default folds onto). The pair jointly
16646        // pins the accessor + declared-slot enumerator composition:
16647        // any future silent detour that had the accessor collapse
16648        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
16649        // is_empty())` projection) would silently absorb the
16650        // "declared but degenerate" arm at the accessor boundary and
16651        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
16652        // coherence gate would silently accept a struct-literal
16653        // `Caixa` carrying the drift.
16654        //
16655        // Peer of the sibling
16656        // `declared_servico_slots_limits_arm_routes_through_accessor`
16657        // (b2bd9d7) and
16658        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16659        // (35d8b52) composition pins on the sibling `:limits` /
16660        // `:behavior` outer-`Option<&Composite>` arms — same "the
16661        // enumerator gate must route through the substrate-primitive
16662        // typed dispatch" discipline extended onto the third M2
16663        // Servico-runtime slot axis, closing the enumerator's routing
16664        // invariant on every M2 arm.
16665        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16666        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
16667            from: "0.0.1".into(),
16668            instructions: vec![UpgradeInstruction::Restart],
16669        }]);
16670        let slots = c.declared_servico_slots();
16671        assert!(
16672            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
16673            "declared_servico_slots must push \
16674             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
16675             non-empty — the accessor and the enumerator gate must \
16676             route through the same substrate-primitive typed \
16677             dispatch on the outer :upgrade-from presence bit (got \
16678             slots={slots:?})",
16679        );
16680        let c = caixa_with_upgrade_from(vec![]);
16681        let slots = c.declared_servico_slots();
16682        assert!(
16683            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
16684            "declared_servico_slots must NOT push \
16685             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
16686             empty — the author-omitted arm must route through the \
16687             accessor's empty-slice return unchanged (got \
16688             slots={slots:?})",
16689        );
16690    }
16691
16692    #[test]
16693    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
16694        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16695        // per-`:upgrade-from` M2 overlay emit arm must key off
16696        // [`Caixa::upgrade_from`], not the raw
16697        // `!caixa.upgrade_from.is_empty()` presence gate + the
16698        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
16699        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
16700        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
16701        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
16702        // sequence in the overlay (the emitter fans onto the serde
16703        // slice-serialization), and a `Caixa { upgrade_from: vec![],
16704        // .. }` must omit the key entirely (the empty-slice
16705        // partition — the `!.is_empty()` outer gate elides the key
16706        // when the author omitted the slot). The pair jointly pins
16707        // the accessor + M2 overlay emitter composition: any future
16708        // silent detour that had the accessor return a fresh-cloned
16709        // `Vec<UpgradeFromEntry>` copy would silently break the
16710        // reference-identity pin the peer per-entry
16711        // `serde_yaml::to_value(caixa.upgrade_from())` projection
16712        // reads from — the projection would clone once per accessor
16713        // call instead of borrowing the storage buffer verbatim.
16714        //
16715        // Peer of the sibling
16716        // `servico_m2_overlay_limits_arm_routes_through_accessor`
16717        // (b2bd9d7) and
16718        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
16719        // (35d8b52) composition pins on the sibling `:limits` /
16720        // `:behavior` outer-`Option<&Composite>` arms — same "the
16721        // M2 overlay emitter must route through the substrate-
16722        // primitive typed dispatch" discipline extended onto the
16723        // third M2 Servico-runtime slot axis, closing the overlay
16724        // emitter's routing invariant on every M2 arm.
16725        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
16726        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16727        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
16728            from: "0.0.1".into(),
16729            instructions: vec![UpgradeInstruction::Restart],
16730        }]);
16731        let overlay = servico_m2_overlay(&c).unwrap();
16732        assert!(
16733            overlay.contains_key(M2_KEY_UPGRADE_FROM),
16734            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
16735             `:upgrade-from` is non-empty — the accessor and the M2 \
16736             overlay emitter must route through the same substrate- \
16737             primitive typed dispatch on the outer :upgrade-from \
16738             slice (got overlay={overlay:?})",
16739        );
16740        let c = caixa_with_upgrade_from(vec![]);
16741        let overlay = servico_m2_overlay(&c).unwrap();
16742        assert!(
16743            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
16744            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
16745             `:upgrade-from` is empty — the empty-slice partition \
16746             must route through the accessor's empty-slice return \
16747             unchanged (got overlay={overlay:?})",
16748        );
16749    }
16750
16751    #[test]
16752    fn upgrade_from_projects_slice_by_borrow() {
16753        // The by-borrow pin: [`Caixa::upgrade_from`] returns
16754        // `&[UpgradeFromEntry]` by borrow — the returned slice
16755        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
16756        // the `:upgrade-from` slot and the accessor must not clone
16757        // the backing `Vec` on every call. Peer of the sibling
16758        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
16759        // (`autores_projects_slice_by_borrow` b5d813f,
16760        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16761        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16762        // `exe_projects_slice_by_borrow` 65d9527,
16763        // `servicos_projects_slice_by_borrow` 611f78b,
16764        // `deps_projects_slice_by_borrow` ad34b4e,
16765        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
16766        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
16767        // axes — extended here to the first outer-`Caixa`
16768        // composite-element `&[Composite]` axis: the accessor's
16769        // returned slice must borrow from `&self` (the returned
16770        // reference's lifetime is tied to `&self`), and calling the
16771        // accessor twice on the same [`Caixa`] must yield slices
16772        // that are pointer-equal (the underlying byte-buffer is the
16773        // storage `Vec`'s allocation, not a fresh copy) as well as
16774        // value-equal (idempotent, no side effects on `&self`).
16775        //
16776        // Pins against a future silent detour that returned an owned
16777        // `Vec<UpgradeFromEntry>` (which would type-check but
16778        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
16779        // return (which would leak the backing `Vec`'s
16780        // grow/push/reserve surface no downstream consumer reaches
16781        // for), or a one-arm-only accessor that returned a
16782        // saturating value on some sentinel input.
16783        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16784        for upgrade_from in [
16785            vec![],
16786            vec![UpgradeFromEntry {
16787                from: "0.0.1".into(),
16788                instructions: vec![UpgradeInstruction::Restart],
16789            }],
16790            vec![
16791                UpgradeFromEntry {
16792                    from: "0.0.1".into(),
16793                    instructions: vec![UpgradeInstruction::Restart],
16794                },
16795                UpgradeFromEntry {
16796                    from: "0.0.2".into(),
16797                    instructions: vec![UpgradeInstruction::SoftPurge {
16798                        module: "demo".into(),
16799                    }],
16800                },
16801            ],
16802        ] {
16803            let c = caixa_with_upgrade_from(upgrade_from.clone());
16804            let first = c.upgrade_from();
16805            let second = c.upgrade_from();
16806            assert_eq!(
16807                first, second,
16808                "Caixa::upgrade_from must be idempotent — two \
16809                 successive calls on the same &self must return the \
16810                 same &[UpgradeFromEntry]",
16811            );
16812            assert_eq!(
16813                first.as_ptr(),
16814                second.as_ptr(),
16815                "Caixa::upgrade_from must borrow the underlying \
16816                 Vec<UpgradeFromEntry> storage — two successive calls \
16817                 must return slices with the same backing pointer (a \
16818                 fresh Vec<UpgradeFromEntry> clone would change the \
16819                 pointer on every call)",
16820            );
16821            assert_eq!(
16822                first,
16823                upgrade_from.as_slice(),
16824                "Caixa::upgrade_from must return :upgrade-from \
16825                 verbatim by borrow — got {first:?}, expected \
16826                 {upgrade_from:?}",
16827            );
16828        }
16829    }
16830
16831    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
16832
16833    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
16834        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16835        c.children = children;
16836        c
16837    }
16838
16839    #[test]
16840    fn children_returns_children_slice_verbatim_across_permutations() {
16841        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
16842        // outer-composite `&[ChildSpec]`-return slice-shape pin:
16843        // [`Caixa::children`] must return the `:children` typed
16844        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
16845        // the same backing buffer the raw `self.children.as_slice()`
16846        // field access borrows from, element-equal across every
16847        // representative fixture in the accept-set — `[]` (the "no
16848        // static children declared" arm every non-`Supervisor`-kind
16849        // `defcaixa` carries by `#[serde(default)]` and every
16850        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
16851        // a canonical single-child `Permanent` fixture (the shape
16852        // most `OneForOne` supervisors carry — a single long-running
16853        // worker child), a canonical multi-child list carrying every
16854        // typed restart-policy variant (`Permanent` / `Transient` /
16855        // `Temporary`), and a past-the-guard sentinel — a duplicate
16856        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
16857        // ([`crate::SupervisorSpec::validate`] rejects through
16858        // `DuplicateChildNome { nome: "w" }` but the accessor must
16859        // ship the raw slot verbatim so struct-literal fixtures
16860        // continue to expose the duplicate at the accessor boundary).
16861        //
16862        // Pins against a future silent detour that returned an owned
16863        // `Vec<ChildSpec>` (which would type-check but silently clone
16864        // on every accessor call, breaking the zero-cost projection
16865        // every peer sibling slice accessor carries), a `[dup, dup] →
16866        // [dup]` dedup collapse (which would silently absorb the
16867        // `DuplicateChildNome` refusal case at the accessor boundary
16868        // and the [`crate::StandardLayout::verify`] cross-child gate
16869        // would silently accept a struct-literal `Caixa` carrying the
16870        // drift), a reference to an operator-resolved overlay (the
16871        // future per-cluster `:children-overrides` slot — its
16872        // resolution must land at exactly this accessor body, not
16873        // silently divert the raw slot away from a second consumer),
16874        // or an axis-shuffled projection (a future detour that
16875        // reordered children through the accessor would silently
16876        // split the paired [`crate::StandardLayout::verify`] per-
16877        // supervisor gate's traversal input from the peer
16878        // [`Self::supervisor_view`] fold-in path's clone-order input,
16879        // since the OTP `RestForOne` restart strategy dispatches on
16880        // declared child order and axis reordering would silently
16881        // split the operator's per-cluster restart-fan-out order
16882        // from the caixa.lisp source-order).
16883        //
16884        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
16885        // accessor pin on the substrate primitive for M2 / M3 typed-
16886        // slot vec-carry axes — folds on the outer-`Caixa`
16887        // `&[Composite]` composite-slice sub-family the sibling
16888        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16889        // (2a1f907) pin opened, peer at the outer altitude of the
16890        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
16891        // (bc92bce) accessor on the same OTP-supervisor static-child-
16892        // list axis.
16893        use crate::supervisor::{ChildSpec, RestartPolicy};
16894        let fixtures: Vec<Vec<ChildSpec>> = vec![
16895            vec![],
16896            vec![ChildSpec {
16897                caixa: "worker".into(),
16898                versao: "^0.1".into(),
16899                restart: RestartPolicy::Permanent,
16900            }],
16901            vec![
16902                ChildSpec {
16903                    caixa: "worker-a".into(),
16904                    versao: "^0.1".into(),
16905                    restart: RestartPolicy::Permanent,
16906                },
16907                ChildSpec {
16908                    caixa: "worker-b".into(),
16909                    versao: "^0.1".into(),
16910                    restart: RestartPolicy::Transient,
16911                },
16912                ChildSpec {
16913                    caixa: "worker-c".into(),
16914                    versao: "^0.1".into(),
16915                    restart: RestartPolicy::Temporary,
16916                },
16917            ],
16918            vec![
16919                ChildSpec {
16920                    caixa: "w".into(),
16921                    versao: "^0.1".into(),
16922                    restart: RestartPolicy::Permanent,
16923                },
16924                ChildSpec {
16925                    caixa: "w".into(),
16926                    versao: "^0.1".into(),
16927                    restart: RestartPolicy::Permanent,
16928                },
16929            ],
16930        ];
16931        for children in fixtures {
16932            let c = caixa_with_children(children.clone());
16933            assert_eq!(
16934                c.children(),
16935                children.as_slice(),
16936                "Caixa::children must return :children verbatim \
16937                 (got {:?}, expected {children:?})",
16938                c.children(),
16939            );
16940            assert_eq!(
16941                c.children(),
16942                c.children.as_slice(),
16943                "Caixa::children must element-equal the raw \
16944                 `self.children.as_slice()` field access across \
16945                 every value in the Vec<ChildSpec> accept-set",
16946            );
16947            assert_eq!(
16948                c.children().is_empty(),
16949                c.children.is_empty(),
16950                "Caixa::children().is_empty() must byte-equal \
16951                 self.children.is_empty() — a presence-bit drift \
16952                 would silently split the paired \
16953                 Caixa::declared_supervisor_slots supervisor-tree \
16954                 declared-slot enumerator's presence probe from the \
16955                 peer Caixa::supervisor_view typed-view composer's \
16956                 fold-in path",
16957            );
16958        }
16959    }
16960
16961    #[test]
16962    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
16963        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16964        // `:children` presence-probe arm must key off
16965        // [`Caixa::children`], not the raw
16966        // `!self.children.is_empty()` field-probe. Structurally: a
16967        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
16968        // "^0.1", restart: Permanent }], .. }` must push
16969        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
16970        // (the presence bit is non-empty, so the supervisor-tree
16971        // kind-coherence gate must surface the slot as "declared"),
16972        // and a `Caixa { children: vec![], .. }` must NOT push the
16973        // label (the "author omitted the slot entirely" arm — the
16974        // empty-slice partition the serde-default folds onto). The
16975        // pair jointly pins the accessor + declared-slot enumerator
16976        // composition: any future silent detour that had the accessor
16977        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
16978        // "__reserved__")` projection) would silently absorb the
16979        // "declared but degenerate" arm at the accessor boundary and
16980        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
16981        // kind-coherence gate would silently accept a struct-literal
16982        // `Caixa` carrying the drift.
16983        //
16984        // Peer of the sibling
16985        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16986        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
16987        // same "the enumerator gate must route through the substrate-
16988        // primitive typed dispatch" discipline extended onto the
16989        // supervisor-tree `:children` composite-slice arm.
16990        use crate::supervisor::{ChildSpec, RestartPolicy};
16991        let c = caixa_with_children(vec![ChildSpec {
16992            caixa: "w".into(),
16993            versao: "^0.1".into(),
16994            restart: RestartPolicy::Permanent,
16995        }]);
16996        let slots = c.declared_supervisor_slots();
16997        assert!(
16998            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
16999            "declared_supervisor_slots must push \
17000             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
17001             non-empty — the accessor and the enumerator gate must \
17002             route through the same substrate-primitive typed \
17003             dispatch on the outer :children presence bit (got \
17004             slots={slots:?})",
17005        );
17006        let c = caixa_with_children(vec![]);
17007        let slots = c.declared_supervisor_slots();
17008        assert!(
17009            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
17010            "declared_supervisor_slots must NOT push \
17011             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
17012             empty — the author-omitted arm must route through the \
17013             accessor's empty-slice return unchanged (got \
17014             slots={slots:?})",
17015        );
17016    }
17017
17018    #[test]
17019    fn supervisor_view_children_arm_routes_through_accessor() {
17020        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
17021        // fold-in arm must key off [`Caixa::children`], not the raw
17022        // `self.children.clone()` field-clone. Structurally: a `Caixa {
17023        // kind: Supervisor, estrategia: Some(OneForOne), children:
17024        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
17025        // per-child list through the accessor into the typed
17026        // [`SupervisorSpec`] view's `children` field verbatim — every
17027        // entry the accessor surfaces must land in the view's
17028        // `children` slot in the same order. The pair jointly pins the
17029        // accessor + view-composer composition: any future silent
17030        // detour that had the accessor return a fresh-cloned
17031        // `Vec<ChildSpec>` copy would silently break the reference-
17032        // identity pin the peer `supervisor_view` fold-in path reads
17033        // from — the fold would clone once more per accessor call
17034        // instead of borrowing the storage buffer verbatim once.
17035        //
17036        // Peer of the sibling
17037        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
17038        // family) composition pin on the peer kind-gate arm — same
17039        // "the view composer must route through the substrate-
17040        // primitive typed dispatch" discipline extended onto the
17041        // per-`:children` fold-in arm, closing the supervisor-view
17042        // composer's routing invariant on the composite-slice input.
17043        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17044        let mut c = caixa_with_children(vec![
17045            ChildSpec {
17046                caixa: "worker-a".into(),
17047                versao: "^0.1".into(),
17048                restart: RestartPolicy::Permanent,
17049            },
17050            ChildSpec {
17051                caixa: "worker-b".into(),
17052                versao: "^0.1".into(),
17053                restart: RestartPolicy::Transient,
17054            },
17055        ]);
17056        c.kind = crate::CaixaKind::Supervisor;
17057        c.estrategia = Some(RestartStrategy::OneForOne);
17058        let view = c
17059            .supervisor_view()
17060            .expect("Supervisor kind must produce a supervisor_view");
17061        assert_eq!(
17062            view.children(),
17063            c.children(),
17064            "supervisor_view must fold Caixa::children verbatim into \
17065             SupervisorSpec::children — the accessor and the view \
17066             composer must route through the same substrate-primitive \
17067             typed dispatch on the outer :children slice (got view \
17068             children={:?}, expected {:?})",
17069            view.children(),
17070            c.children(),
17071        );
17072    }
17073
17074    #[test]
17075    fn children_projects_slice_by_borrow() {
17076        // The by-borrow pin: [`Caixa::children`] returns
17077        // `&[ChildSpec]` by borrow — the returned slice borrows the
17078        // underlying `Vec<ChildSpec>` storage of the `:children` slot
17079        // and the accessor must not clone the backing `Vec` on every
17080        // call. Peer of the sibling outer top-level [`Caixa`]
17081        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
17082        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
17083        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17084        // `exe_projects_slice_by_borrow` 65d9527,
17085        // `servicos_projects_slice_by_borrow` 611f78b,
17086        // `deps_projects_slice_by_borrow` ad34b4e,
17087        // `deps_dev_projects_slice_by_borrow` f7fd81e,
17088        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
17089        // sibling outer top-level [`Caixa`] scalar-element and
17090        // composite-element `&[T]` axes — folds on the outer-`Caixa`
17091        // composite-element `&[Composite]` axis: the accessor's
17092        // returned slice must borrow from `&self` (the returned
17093        // reference's lifetime is tied to `&self`), and calling the
17094        // accessor twice on the same [`Caixa`] must yield slices
17095        // that are pointer-equal (the underlying byte-buffer is the
17096        // storage `Vec`'s allocation, not a fresh copy) as well as
17097        // value-equal (idempotent, no side effects on `&self`).
17098        //
17099        // Pins against a future silent detour that returned an owned
17100        // `Vec<ChildSpec>` (which would type-check but silently clone
17101        // on every call), a `&Vec<ChildSpec>` return (which would leak
17102        // the backing `Vec`'s grow/push/reserve surface no downstream
17103        // consumer reaches for), or a one-arm-only accessor that
17104        // returned a saturating value on some sentinel input.
17105        use crate::supervisor::{ChildSpec, RestartPolicy};
17106        for children in [
17107            vec![],
17108            vec![ChildSpec {
17109                caixa: "w".into(),
17110                versao: "^0.1".into(),
17111                restart: RestartPolicy::Permanent,
17112            }],
17113            vec![
17114                ChildSpec {
17115                    caixa: "worker-a".into(),
17116                    versao: "^0.1".into(),
17117                    restart: RestartPolicy::Permanent,
17118                },
17119                ChildSpec {
17120                    caixa: "worker-b".into(),
17121                    versao: "^0.1".into(),
17122                    restart: RestartPolicy::Transient,
17123                },
17124            ],
17125        ] {
17126            let c = caixa_with_children(children.clone());
17127            let first = c.children();
17128            let second = c.children();
17129            assert_eq!(
17130                first, second,
17131                "Caixa::children must be idempotent — two successive \
17132                 calls on the same &self must return the same \
17133                 &[ChildSpec]",
17134            );
17135            assert_eq!(
17136                first.as_ptr(),
17137                second.as_ptr(),
17138                "Caixa::children must borrow the underlying \
17139                 Vec<ChildSpec> storage — two successive calls must \
17140                 return slices with the same backing pointer (a fresh \
17141                 Vec<ChildSpec> clone would change the pointer on \
17142                 every call)",
17143            );
17144            assert_eq!(
17145                first,
17146                children.as_slice(),
17147                "Caixa::children must return :children verbatim by \
17148                 borrow — got {first:?}, expected {children:?}",
17149            );
17150        }
17151    }
17152
17153    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
17154
17155    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
17156        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17157        c.kind = CaixaKind::Aplicacao;
17158        c.membros = membros;
17159        c
17160    }
17161
17162    #[test]
17163    fn membros_returns_membros_slice_verbatim_across_permutations() {
17164        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
17165        // composite `&[Membro]`-return slice-shape pin:
17166        // [`Caixa::membros`] must return the `:membros` typed
17167        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
17168        // same backing buffer the raw `self.membros.as_slice()` field
17169        // access borrows from, element-equal across every
17170        // representative fixture in the accept-set — `[]` (the "no
17171        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
17172        // carries by `#[serde(default)]` and every partially-authored
17173        // Aplicacao carries before the
17174        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
17175        // canonical single-member fixture (the shape a minimal
17176        // Aplicacao carries — one Servico wrapping one contained
17177        // computation), a canonical multi-member list carrying three
17178        // distinct entries (the canonical checkout-shape Aplicacao —
17179        // cart / pricing / auth — every canonical example carries), and
17180        // a past-the-guard sentinel — a duplicate `:caixa`
17181        // `[("cart", ...), ("cart", ...)]` entry pair
17182        // ([`crate::AplicacaoSpec::validate`] rejects through
17183        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
17184        // the raw slot verbatim so struct-literal fixtures continue to
17185        // expose the duplicate at the accessor boundary).
17186        //
17187        // Pins against a future silent detour that returned an owned
17188        // `Vec<Membro>` (which would type-check but silently clone on
17189        // every accessor call, breaking the zero-cost projection every
17190        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
17191        // dedup collapse (which would silently absorb the
17192        // `DuplicateMembro` refusal case at the accessor boundary and
17193        // the [`crate::StandardLayout::verify`] cross-member gate would
17194        // silently accept a struct-literal `Caixa` carrying the drift),
17195        // a reference to an operator-resolved overlay (the future per-
17196        // cluster `:membros-overrides` slot — its resolution must land
17197        // at exactly this accessor body, not silently divert the raw
17198        // slot away from a second consumer), or an axis-shuffled
17199        // projection (a future detour that reordered members through
17200        // the accessor would silently split the paired
17201        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
17202        // traversal input from the peer [`Self::aplicacao_view`] fold-
17203        // in path's clone-order input, since the canonical `:contratos`
17204        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
17205        // read the member set through the same slice).
17206        //
17207        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
17208        // accessor pin on the substrate primitive for M2 / M3 typed-
17209        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
17210        // arm of the `&[Composite]` composite-slice sub-family the
17211        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
17212        // (2a1f907) and
17213        // `children_returns_children_slice_verbatim_across_permutations`
17214        // (c17b51e) pins opened, peer at the outer altitude of the
17215        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
17216        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
17217        // list axis.
17218        use crate::aplicacao::Membro;
17219        let fixtures: Vec<Vec<Membro>> = vec![
17220            vec![],
17221            vec![Membro {
17222                caixa: "cart".into(),
17223                versao: "^0.1".into(),
17224            }],
17225            vec![
17226                Membro {
17227                    caixa: "cart".into(),
17228                    versao: "^0.1".into(),
17229                },
17230                Membro {
17231                    caixa: "pricing".into(),
17232                    versao: "^0.2".into(),
17233                },
17234                Membro {
17235                    caixa: "auth".into(),
17236                    versao: "^1.0".into(),
17237                },
17238            ],
17239            vec![
17240                Membro {
17241                    caixa: "cart".into(),
17242                    versao: "^0.1".into(),
17243                },
17244                Membro {
17245                    caixa: "cart".into(),
17246                    versao: "^0.1".into(),
17247                },
17248            ],
17249        ];
17250        for membros in fixtures {
17251            let c = caixa_aplicacao_with_membros(membros.clone());
17252            assert_eq!(
17253                c.membros(),
17254                membros.as_slice(),
17255                "Caixa::membros must return :membros verbatim \
17256                 (got {:?}, expected {membros:?})",
17257                c.membros(),
17258            );
17259            assert_eq!(
17260                c.membros(),
17261                c.membros.as_slice(),
17262                "Caixa::membros must element-equal the raw \
17263                 `self.membros.as_slice()` field access across every \
17264                 value in the Vec<Membro> accept-set",
17265            );
17266            assert_eq!(
17267                c.membros().is_empty(),
17268                c.membros.is_empty(),
17269                "Caixa::membros().is_empty() must byte-equal \
17270                 self.membros.is_empty() — a presence-bit drift would \
17271                 silently split the paired Caixa::declared_mesh_slots \
17272                 mesh declared-slot enumerator's presence probe from \
17273                 the peer Caixa::aplicacao_view typed-view composer's \
17274                 fold-in path",
17275            );
17276        }
17277    }
17278
17279    #[test]
17280    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
17281        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
17282        // presence-probe arm must key off [`Caixa::membros`], not the
17283        // raw `!self.membros.is_empty()` field-probe. Structurally: a
17284        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
17285        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
17286        // declared-slot list (the presence bit is non-empty, so the
17287        // mesh kind-coherence gate must surface the slot as
17288        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
17289        // push the label (the "author omitted the slot entirely" arm
17290        // — the empty-slice partition the serde-default folds onto).
17291        // The pair jointly pins the accessor + declared-slot
17292        // enumerator composition: any future silent detour that had
17293        // the accessor collapse `[Membro { .. }]` to `[]` (a
17294        // `.filter(|m| m.nome() != "__reserved__")` projection) would
17295        // silently absorb the "declared but degenerate" arm at the
17296        // accessor boundary and the
17297        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17298        // coherence gate would silently accept a struct-literal
17299        // `Caixa` carrying the drift.
17300        //
17301        // Peer of the sibling
17302        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
17303        // (2a1f907) and
17304        // `declared_supervisor_slots_children_arm_routes_through_accessor`
17305        // (c17b51e) composition pins on the M2 `:upgrade-from` /
17306        // `:children` composite-slice arms — same "the enumerator gate
17307        // must route through the substrate-primitive typed dispatch"
17308        // discipline extended onto the M3 `:membros` composite-slice
17309        // arm, opening the M3 arm of the declared-slot enumerator's
17310        // routing invariant.
17311        use crate::aplicacao::Membro;
17312        let c = caixa_aplicacao_with_membros(vec![Membro {
17313            caixa: "cart".into(),
17314            versao: "^0.1".into(),
17315        }]);
17316        let slots = c.declared_mesh_slots();
17317        assert!(
17318            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
17319            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
17320             `:membros` is non-empty — the accessor and the enumerator \
17321             gate must route through the same substrate-primitive \
17322             typed dispatch on the outer :membros presence bit (got \
17323             slots={slots:?})",
17324        );
17325        let c = caixa_aplicacao_with_membros(vec![]);
17326        let slots = c.declared_mesh_slots();
17327        assert!(
17328            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
17329            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
17330             when `:membros` is empty — the author-omitted arm must \
17331             route through the accessor's empty-slice return unchanged \
17332             (got slots={slots:?})",
17333        );
17334    }
17335
17336    #[test]
17337    fn aplicacao_view_membros_arm_routes_through_accessor() {
17338        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
17339        // fold-in arm must key off [`Caixa::membros`], not the raw
17340        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
17341        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
17342        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
17343        // member list through the accessor into the typed
17344        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
17345        // every entry the accessor surfaces must land in the view's
17346        // `membros` slot in the same order. The pair jointly pins the
17347        // accessor + view-composer composition: any future silent
17348        // detour that had the accessor return a fresh-cloned
17349        // `Vec<Membro>` copy would silently break the reference-
17350        // identity pin the peer `aplicacao_view` fold-in path reads
17351        // from — the fold would clone once more per accessor call
17352        // instead of borrowing the storage buffer verbatim once.
17353        //
17354        // Peer of the sibling
17355        // `aplicacao_view_politicas_arm_folds_through_accessor`
17356        // (5d23d29) /
17357        // `aplicacao_view_placement_arm_folds_through_accessor`
17358        // (4fb8074) /
17359        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
17360        // composition pins on the M3 `:politicas` / `:placement` /
17361        // `:entrada` outer-`Option<&Composite>` arms — extended here to
17362        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
17363        // closing the aplicacao-view composer's routing invariant on
17364        // the composite-slice input.
17365        use crate::aplicacao::Membro;
17366        let c = caixa_aplicacao_with_membros(vec![
17367            Membro {
17368                caixa: "cart".into(),
17369                versao: "^0.1".into(),
17370            },
17371            Membro {
17372                caixa: "pricing".into(),
17373                versao: "^0.2".into(),
17374            },
17375        ]);
17376        let view = c
17377            .aplicacao_view()
17378            .expect("Aplicacao kind must produce an aplicacao_view");
17379        assert_eq!(
17380            view.membros(),
17381            c.membros(),
17382            "aplicacao_view must fold Caixa::membros verbatim into \
17383             AplicacaoSpec::membros — the accessor and the view \
17384             composer must route through the same substrate-primitive \
17385             typed dispatch on the outer :membros slice (got view \
17386             membros={:?}, expected {:?})",
17387            view.membros(),
17388            c.membros(),
17389        );
17390    }
17391
17392    #[test]
17393    fn membros_projects_slice_by_borrow() {
17394        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
17395        // borrow — the returned slice borrows the underlying
17396        // `Vec<Membro>` storage of the `:membros` slot and the
17397        // accessor must not clone the backing `Vec` on every call.
17398        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
17399        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
17400        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
17401        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17402        // `exe_projects_slice_by_borrow` 65d9527,
17403        // `servicos_projects_slice_by_borrow` 611f78b,
17404        // `deps_projects_slice_by_borrow` ad34b4e,
17405        // `deps_dev_projects_slice_by_borrow` f7fd81e,
17406        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
17407        // `children_projects_slice_by_borrow` c17b51e) on the sibling
17408        // outer top-level [`Caixa`] scalar-element and composite-
17409        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
17410        // slot composite-element `&[Composite]` axis: the accessor's
17411        // returned slice must borrow from `&self` (the returned
17412        // reference's lifetime is tied to `&self`), and calling the
17413        // accessor twice on the same [`Caixa`] must yield slices that
17414        // are pointer-equal (the underlying byte-buffer is the storage
17415        // `Vec`'s allocation, not a fresh copy) as well as value-equal
17416        // (idempotent, no side effects on `&self`).
17417        //
17418        // Pins against a future silent detour that returned an owned
17419        // `Vec<Membro>` (which would type-check but silently clone on
17420        // every call), a `&Vec<Membro>` return (which would leak the
17421        // backing `Vec`'s grow/push/reserve surface no downstream
17422        // consumer reaches for), or a one-arm-only accessor that
17423        // returned a saturating value on some sentinel input.
17424        use crate::aplicacao::Membro;
17425        for membros in [
17426            vec![],
17427            vec![Membro {
17428                caixa: "cart".into(),
17429                versao: "^0.1".into(),
17430            }],
17431            vec![
17432                Membro {
17433                    caixa: "cart".into(),
17434                    versao: "^0.1".into(),
17435                },
17436                Membro {
17437                    caixa: "pricing".into(),
17438                    versao: "^0.2".into(),
17439                },
17440            ],
17441        ] {
17442            let c = caixa_aplicacao_with_membros(membros.clone());
17443            let first = c.membros();
17444            let second = c.membros();
17445            assert_eq!(
17446                first, second,
17447                "Caixa::membros must be idempotent — two successive \
17448                 calls on the same &self must return the same &[Membro]",
17449            );
17450            assert_eq!(
17451                first.as_ptr(),
17452                second.as_ptr(),
17453                "Caixa::membros must borrow the underlying Vec<Membro> \
17454                 storage — two successive calls must return slices with \
17455                 the same backing pointer (a fresh Vec<Membro> clone \
17456                 would change the pointer on every call)",
17457            );
17458            assert_eq!(
17459                first,
17460                membros.as_slice(),
17461                "Caixa::membros must return :membros verbatim by borrow \
17462                 — got {first:?}, expected {membros:?}",
17463            );
17464        }
17465    }
17466
17467    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
17468
17469    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
17470        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17471        c.kind = CaixaKind::Aplicacao;
17472        c.contratos = contratos;
17473        c
17474    }
17475
17476    fn contrato_http_for_test(
17477        de: &str,
17478        para: &str,
17479        endpoint: &str,
17480    ) -> crate::aplicacao::WitContract {
17481        crate::aplicacao::WitContract {
17482            de: de.into(),
17483            para: para.into(),
17484            wit: "wasi:http/proxy".into(),
17485            endpoint: Some(endpoint.into()),
17486            subject: None,
17487            slot: None,
17488        }
17489    }
17490
17491    #[test]
17492    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
17493        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
17494        // composite `&[WitContract]`-return slice-shape pin:
17495        // [`Caixa::contratos`] must return the `:contratos` typed
17496        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
17497        // over the same backing buffer the raw
17498        // `self.contratos.as_slice()` field access borrows from,
17499        // element-equal across every representative fixture in the
17500        // accept-set — `[]` (the "no contracts declared" arm every
17501        // non-`Aplicacao`-kind `defcaixa` carries by
17502        // `#[serde(default)]` and every leaf-Aplicacao with a single
17503        // member carries), a canonical single-edge fixture (the
17504        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
17505        // edge), and a canonical multi-edge fixture with three distinct
17506        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
17507        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
17508        //
17509        // Pins against a future silent detour that returned an owned
17510        // `Vec<WitContract>` (which would type-check but silently clone
17511        // on every accessor call, breaking the zero-cost projection
17512        // every peer sibling slice accessor carries), an axis-shuffled
17513        // projection (a future detour that reordered edges through the
17514        // accessor would silently split the paired
17515        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
17516        // traversal input from the peer [`Self::aplicacao_view`] fold-
17517        // in path's clone-order input, since every canonical
17518        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
17519        // seed dispatch reads the edge set through the same slice),
17520        // or a reference to an operator-resolved overlay (the future
17521        // per-cluster `:contratos-overrides` slot — its resolution
17522        // must land at exactly this accessor body, not silently divert
17523        // the raw slot away from a second consumer).
17524        //
17525        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
17526        // accessor pin on the substrate primitive for M2 / M3 typed-
17527        // slot vec-carry axes — closes the outer-`Caixa`
17528        // `&[Composite]` composite-slice sub-family the sibling M2
17529        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
17530        // (2a1f907) and
17531        // `children_returns_children_slice_verbatim_across_permutations`
17532        // (c17b51e) pins opened and the M3
17533        // `membros_returns_membros_slice_verbatim_across_permutations`
17534        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
17535        // slot arm of the composite-slice sub-family. Peer at the outer
17536        // altitude of the closed inner-
17537        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
17538        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
17539        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
17540            vec![],
17541            vec![contrato_http_for_test("cart", "catalog", "/items")],
17542            vec![
17543                contrato_http_for_test("cart", "catalog", "/items"),
17544                contrato_http_for_test("cart", "pricing", "/price"),
17545                contrato_http_for_test("cart", "auth", "/whoami"),
17546            ],
17547        ];
17548        for contratos in fixtures {
17549            let c = caixa_aplicacao_with_contratos(contratos.clone());
17550            assert_eq!(
17551                c.contratos(),
17552                contratos.as_slice(),
17553                "Caixa::contratos must return :contratos verbatim \
17554                 (got {:?}, expected {contratos:?})",
17555                c.contratos(),
17556            );
17557            assert_eq!(
17558                c.contratos(),
17559                c.contratos.as_slice(),
17560                "Caixa::contratos must element-equal the raw \
17561                 `self.contratos.as_slice()` field access across every \
17562                 value in the Vec<WitContract> accept-set",
17563            );
17564            assert_eq!(
17565                c.contratos().is_empty(),
17566                c.contratos.is_empty(),
17567                "Caixa::contratos().is_empty() must byte-equal \
17568                 self.contratos.is_empty() — a presence-bit drift would \
17569                 silently split the paired Caixa::declared_mesh_slots \
17570                 mesh declared-slot enumerator's presence probe from \
17571                 the peer Caixa::aplicacao_view typed-view composer's \
17572                 fold-in path",
17573            );
17574        }
17575    }
17576
17577    #[test]
17578    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
17579        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
17580        // presence-probe arm must key off [`Caixa::contratos`], not the
17581        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
17582        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
17583        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
17584        // presence bit is non-empty, so the mesh kind-coherence gate
17585        // must surface the slot as "declared"), and a `Caixa {
17586        // contratos: vec![], .. }` must NOT push the label (the "author
17587        // omitted the slot entirely" arm — the empty-slice partition
17588        // the serde-default folds onto). The pair jointly pins the
17589        // accessor + declared-slot enumerator composition: any future
17590        // silent detour that had the accessor collapse
17591        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
17592        // "__reserved__")` projection) would silently absorb the
17593        // "declared but degenerate" arm at the accessor boundary and
17594        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17595        // coherence gate would silently accept a struct-literal
17596        // `Caixa` carrying the drift.
17597        //
17598        // Peer of the sibling
17599        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
17600        // (2a1f907),
17601        // `declared_supervisor_slots_children_arm_routes_through_accessor`
17602        // (c17b51e), and
17603        // `declared_mesh_slots_membros_arm_routes_through_accessor`
17604        // (0f26987) composition pins on the M2 `:upgrade-from` /
17605        // `:children` / M3 `:membros` composite-slice arms — same "the
17606        // enumerator gate must route through the substrate-primitive
17607        // typed dispatch" discipline extended onto the M3 `:contratos`
17608        // composite-slice arm, closing the M3 mesh-slot arm of the
17609        // declared-slot enumerator's routing invariant on the
17610        // composite-slice inputs.
17611        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
17612            "cart", "catalog", "/items",
17613        )]);
17614        let slots = c.declared_mesh_slots();
17615        assert!(
17616            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
17617            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
17618             `:contratos` is non-empty — the accessor and the enumerator \
17619             gate must route through the same substrate-primitive \
17620             typed dispatch on the outer :contratos presence bit (got \
17621             slots={slots:?})",
17622        );
17623        let c = caixa_aplicacao_with_contratos(vec![]);
17624        let slots = c.declared_mesh_slots();
17625        assert!(
17626            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
17627            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
17628             when `:contratos` is empty — the author-omitted arm must \
17629             route through the accessor's empty-slice return unchanged \
17630             (got slots={slots:?})",
17631        );
17632    }
17633
17634    #[test]
17635    fn aplicacao_view_contratos_arm_routes_through_accessor() {
17636        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
17637        // fold-in arm must key off [`Caixa::contratos`], not the raw
17638        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
17639        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
17640        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
17641        // per-edge list through the accessor into the typed
17642        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
17643        // every entry the accessor surfaces must land in the view's
17644        // `contratos` slot in the same order. The pair jointly pins
17645        // the accessor + view-composer composition: a future silent
17646        // detour that had the accessor shuffle or drop an edge would
17647        // silently split the paired declared-slot enumerator's
17648        // presence bit from the typed-view composer's edge-list, a
17649        // two-consumer split at the enumerator and the view composer
17650        // far from the source `caixa.lisp`.
17651        //
17652        // Peer of the sibling
17653        // `aplicacao_view_membros_arm_routes_through_accessor`
17654        // (0f26987) composition pin on the M3 `:membros` outer-
17655        // `&[Composite]` composite-slice arm, closing the aplicacao-
17656        // view composer's routing invariant on the composite-slice
17657        // inputs at the outer altitude.
17658        let c = caixa_aplicacao_with_contratos(vec![
17659            contrato_http_for_test("cart", "catalog", "/items"),
17660            contrato_http_for_test("cart", "pricing", "/price"),
17661        ]);
17662        let view = c
17663            .aplicacao_view()
17664            .expect("Aplicacao kind must produce an aplicacao_view");
17665        assert_eq!(
17666            view.contratos(),
17667            c.contratos(),
17668            "aplicacao_view must fold Caixa::contratos verbatim into \
17669             AplicacaoSpec::contratos — the accessor and the view \
17670             composer must route through the same substrate-primitive \
17671             typed dispatch on the outer :contratos slice (got view \
17672             contratos={:?}, expected {:?})",
17673            view.contratos(),
17674            c.contratos(),
17675        );
17676    }
17677
17678    #[test]
17679    fn contratos_projects_slice_by_borrow() {
17680        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
17681        // by borrow — the returned slice borrows the underlying
17682        // `Vec<WitContract>` storage of the `:contratos` slot and the
17683        // accessor must not clone the backing `Vec` on every call.
17684        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
17685        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
17686        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
17687        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17688        // `exe_projects_slice_by_borrow` 65d9527,
17689        // `servicos_projects_slice_by_borrow` 611f78b,
17690        // `deps_projects_slice_by_borrow` ad34b4e,
17691        // `deps_dev_projects_slice_by_borrow` f7fd81e,
17692        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
17693        // `children_projects_slice_by_borrow` c17b51e,
17694        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
17695        // outer top-level [`Caixa`] scalar-element and composite-
17696        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
17697        // composite-element `&[Composite]` axis on the by-borrow pin:
17698        // the accessor's returned slice must borrow from `&self` (the
17699        // returned reference's lifetime is tied to `&self`), and
17700        // calling the accessor twice on the same [`Caixa`] must yield
17701        // slices that are pointer-equal (the underlying byte-buffer is
17702        // the storage `Vec`'s allocation, not a fresh copy) as well as
17703        // value-equal (idempotent, no side effects on `&self`).
17704        //
17705        // Pins against a future silent detour that returned an owned
17706        // `Vec<WitContract>` (which would type-check but silently clone
17707        // on every call), a `&Vec<WitContract>` return (which would
17708        // leak the backing `Vec`'s grow/push/reserve surface no
17709        // downstream consumer reaches for), or a one-arm-only accessor
17710        // that returned a saturating value on some sentinel input.
17711        for contratos in [
17712            vec![],
17713            vec![contrato_http_for_test("cart", "catalog", "/items")],
17714            vec![
17715                contrato_http_for_test("cart", "catalog", "/items"),
17716                contrato_http_for_test("cart", "pricing", "/price"),
17717            ],
17718        ] {
17719            let c = caixa_aplicacao_with_contratos(contratos.clone());
17720            let first = c.contratos();
17721            let second = c.contratos();
17722            assert_eq!(
17723                first, second,
17724                "Caixa::contratos must be idempotent — two successive \
17725                 calls on the same &self must return the same \
17726                 &[WitContract]",
17727            );
17728            assert_eq!(
17729                first.as_ptr(),
17730                second.as_ptr(),
17731                "Caixa::contratos must borrow the underlying \
17732                 Vec<WitContract> storage — two successive calls must \
17733                 return slices with the same backing pointer (a fresh \
17734                 Vec<WitContract> clone would change the pointer on \
17735                 every call)",
17736            );
17737            assert_eq!(
17738                first,
17739                contratos.as_slice(),
17740                "Caixa::contratos must return :contratos verbatim by \
17741                 borrow — got {first:?}, expected {contratos:?}",
17742            );
17743        }
17744    }
17745
17746    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
17747
17748    #[test]
17749    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
17750        // Load-bearing invariant: every multi-word top-level [`Caixa`]
17751        // serde-derived JSON key routes through a lifted `&'static str`
17752        // const. The Rust field names are `snake_case`
17753        // (`deps_dev` / `upgrade_from` / `max_restarts` /
17754        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
17755        // "camelCase")]` derive attribute maps each to the camelCase
17756        // byte-string the [`Caixa::to_lisp`] round-trip's
17757        // `serde_json::to_value(self)` step lands under before
17758        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
17759        // to the kebab-case `:deps-dev` / `:upgrade-from` /
17760        // `:max-restarts` / `:restart-window` author surface. Serialize
17761        // a fully-populated [`Caixa`] and pin that each canonical
17762        // byte-sequence appears verbatim in the JSON — a future
17763        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
17764        // verbatim-field-name flip at the derive attribute (any of
17765        // which would silently break every [`Caixa::to_lisp`]
17766        // round-trip and the future M4 operator-side manifest ingest's
17767        // `Value::get(<key>)` navigation) surfaces here as a build-time
17768        // test failure at `manifest.rs`, not as an apply-time
17769        // `.get(<stale-canonical-const>)` returning `None` far from the
17770        // derive-attr drift's commit. Same discipline the sibling
17771        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
17772        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
17773        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
17774        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
17775        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
17776        // [`UpgradeFromEntry`] per-entry axes — extended here to the
17777        // enclosing M0 [`Caixa`] top-level axis so the last of the four
17778        // multi-word top-level [`Caixa`] serde-derived JSON keys
17779        // (`depsDev`) joins the substrate's "one canonical byte-string
17780        // per typed serialized-key axis" discipline.
17781        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17782        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17783        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17784        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
17785        c.upgrade_from = vec![UpgradeFromEntry {
17786            from: "0.0.1".into(),
17787            instructions: vec![UpgradeInstruction::Restart],
17788        }];
17789        c.estrategia = Some(RestartStrategy::OneForOne);
17790        c.max_restarts = Some(3);
17791        c.restart_window = Some("60s".into());
17792        c.children = vec![ChildSpec {
17793            caixa: "child".into(),
17794            versao: "^0.1".into(),
17795            restart: RestartPolicy::Permanent,
17796        }];
17797        let json = serde_json::to_string(&c).unwrap();
17798        for key in [
17799            crate::render::CAIXA_KEY_DEPS_DEV,
17800            crate::render::M2_KEY_UPGRADE_FROM,
17801            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
17802            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
17803        ] {
17804            let quoted = format!("\"{key}\"");
17805            assert!(
17806                json.contains(&quoted),
17807                "serialized Caixa must carry the lifted top-level \
17808                 multi-word byte-sequence {quoted} verbatim in the JSON \
17809                 emission (got: {json})",
17810            );
17811        }
17812    }
17813
17814    #[test]
17815    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
17816        // Cross-axis drift-detection pin: a future collapse of the four
17817        // canonical [`Caixa`] top-level multi-word byte-strings onto the
17818        // same value (e.g. an accidental copy-paste flip of
17819        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
17820        // `"upgradeFrom"`) would silently reroute every downstream
17821        // `Value::get(<key>)` probe on one axis onto the sibling axis's
17822        // top-level entry and pass every propagation-probe test that
17823        // expected only the stale axis's value. Peer of the sibling
17824        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
17825        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
17826        let all = [
17827            crate::render::CAIXA_KEY_DEPS_DEV,
17828            crate::render::M2_KEY_UPGRADE_FROM,
17829            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
17830            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
17831        ];
17832        for (i, a) in all.iter().enumerate() {
17833            for b in all.iter().skip(i + 1) {
17834                assert_ne!(
17835                    a, b,
17836                    "Caixa top-level multi-word key consts must be \
17837                     pairwise-distinct canonical byte-sequences — got \
17838                     `{a}` == `{b}`",
17839                );
17840            }
17841        }
17842    }
17843
17844    #[test]
17845    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
17846        // Shape-pin: every [`Caixa`] top-level multi-word key const must
17847        // be a lowerCamelCase byte-sequence (no `snake_case`
17848        // underscores, no `kebab-case` hyphens, no leading colon, no
17849        // `PascalCase` leading capital, no whitespace / dots) — the
17850        // canonical shape the `#[serde(rename_all = "camelCase")]`
17851        // derive produces on [`Caixa`]. A future flip to a
17852        // non-camelCase attribute at the derive surfaces both here
17853        // (this test fails on the stale-constant shape) and at
17854        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
17855        // (that test fails on the mismatch between const and derive).
17856        // Peer with `membro_key_consts_are_lower_camel_case_shape`
17857        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
17858        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
17859        for key in [
17860            crate::render::CAIXA_KEY_DEPS_DEV,
17861            crate::render::M2_KEY_UPGRADE_FROM,
17862            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
17863            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
17864        ] {
17865            assert!(
17866                !key.is_empty(),
17867                "Caixa top-level multi-word key const must be non-empty \
17868                 (got {key:?})"
17869            );
17870            let first = key.chars().next().unwrap();
17871            assert!(
17872                first.is_ascii_lowercase(),
17873                "Caixa top-level multi-word key const must lead with an \
17874                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
17875            );
17876            assert!(
17877                key.chars().all(|c| c.is_ascii_alphanumeric()),
17878                "Caixa top-level multi-word key const must be \
17879                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
17880                 whitespace (got {key:?})",
17881            );
17882        }
17883    }
17884
17885    #[test]
17886    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
17887        // Scalar-value pin: the byte-string the
17888        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
17889        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
17890        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
17891        // → `depsTest` matching a hypothetical per-test-target
17892        // vocabulary flip) lands as an edit to exactly one const AND
17893        // one derive attribute — the sibling
17894        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
17895        // pin already ties the const to the derive attribute, so a
17896        // rebrand that touches only one side of the pair fails at
17897        // caixa-core build time. Same "scalar-value pin per const"
17898        // discipline the sibling
17899        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
17900        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
17901        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
17902        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
17903    }
17904
17905    #[test]
17906    fn caixa_key_deps_pins_canonical_byte_string() {
17907        // Scalar-value pin: the byte-string the
17908        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
17909        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
17910        // on the two-list dep-graph serialized-key axis — the sibling
17911        // pin covers the multi-word `deps_dev → depsDev` camelCase
17912        // arm, this pin covers the single-word `deps → deps` no-op arm
17913        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
17914        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
17915        // axis and the emitted JSON key equals the source-side field
17916        // name byte-for-byte). A future [`crate::Caixa::deps`] field
17917        // rename (`deps` → `dependencies` matching Cargo's verbatim
17918        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
17919        // hypothetical per-runtime-target vocabulary flip) OR an added
17920        // `#[serde(rename = "…")]` explicit override lands as an edit
17921        // to exactly one const AND one derive-attr / field name — the
17922        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
17923        // pin ties the const to the emitted JSON key, so a rebrand
17924        // that touches only one side of the pair fails at caixa-core
17925        // build time.
17926        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
17927    }
17928
17929    #[test]
17930    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
17931        // Load-bearing invariant on the single-word `deps` top-level
17932        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
17933        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
17934        // `serde_json::to_value(self)` step emits. Serialize a
17935        // populated [`Caixa`] whose `:deps` slot carries at least one
17936        // entry (the `#[serde(default)]` attribute on the field emits
17937        // an empty `[]` even without members, but a non-empty vec
17938        // additionally covers the codec's per-`Dep`-entry emission
17939        // path) and pin that `"deps"` appears verbatim in the JSON
17940        // emission — a future accidental `rename_all = "snake_case"` /
17941        // `"kebab-case"` flip at the derive attribute (or an added
17942        // `#[serde(rename = "…")]` explicit override on the field, or
17943        // a Rust field rename) would break every [`Caixa::to_lisp`]
17944        // round-trip and the future M4 operator-side manifest ingest's
17945        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
17946        // build-time test failure at `manifest.rs`, not as an
17947        // apply-time `.get(<stale-canonical-const>)` returning `None`
17948        // far from the drift's commit. Peer of the sibling
17949        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
17950        // multi-word pin on the same M0 [`Caixa`] top-level
17951        // serialized-key axis, extended here to the single-word arm
17952        // the multi-word test's `rename_all = "camelCase"` sweep can't
17953        // reach (single-word `deps → deps` is a no-op the multi-word
17954        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
17955        // `\"restartWindow\"` byte-scan can never observe).
17956        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17957        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
17958        let json = serde_json::to_string(&c).unwrap();
17959        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
17960        assert!(
17961            json.contains(&quoted),
17962            "serialized Caixa must carry the lifted top-level `deps` \
17963             byte-sequence {quoted} verbatim in the JSON emission (got: \
17964             {json})",
17965        );
17966    }
17967
17968    #[test]
17969    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
17970        // Cross-axis drift-detection pin on the two-list dep-graph
17971        // renderer-side wire-key axis: a future collapse of the
17972        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
17973        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
17974        // same value (e.g. an accidental copy-paste flip of
17975        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
17976        // reroute every downstream `Value::get(<key>)` probe on one
17977        // axis onto the sibling axis's dep-list and pass every
17978        // propagation-probe test that expected only the stale axis's
17979        // value — a dev-only dep would land in the runtime closure at
17980        // publish time, or a runtime dep would be excluded from the
17981        // published lacre. Peer of the sibling four-way distinct pin
17982        // on the top-level multi-word tetrad
17983        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
17984        // and the two-way pin on the sibling
17985        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
17986        // author-facing arm (4da6fba's test), extended here to the
17987        // renderer-side wire-key arm of the same two-list dep-graph
17988        // axis so both halves of the "one canonical byte-string per
17989        // typed axis per (author, wire)" grid carry the same
17990        // distinct-ness discipline.
17991        assert_ne!(
17992            crate::render::CAIXA_KEY_DEPS,
17993            crate::render::CAIXA_KEY_DEPS_DEV,
17994            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
17995             canonical byte-sequences on the two-list dep-graph \
17996             renderer-side wire-key axis"
17997        );
17998    }
17999
18000    // ── DepList / Caixa::push_dep pin ────────────────────────────────
18001    //
18002    // The compounding pin: the two-arm closed-set typed enum
18003    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
18004    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
18005    // consumer of the top-level manifest's dep-mutation surface reads
18006    // through, and the typed dispatch [`Caixa::push_dep`] on the
18007    // substrate primitive folds the "select list → check within-list
18008    // dup → push" cascade onto one method call. Prior to this landing
18009    // the two axes lived across two `&'static str` constants
18010    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
18011    // set type carrying the pair; the `feira add` mutation site's
18012    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
18013    // caixa.deps }` dispatch expressed no compile-time link back to
18014    // the substrate primitive, and a future third dep-list axis would
18015    // have silently split at every open-coded mutation site.
18016
18017    #[test]
18018    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
18019        // Every arm returns the same `&'static str` the substrate's
18020        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
18021        // constants carry. A future rebrand on either constant reaches
18022        // the enum through one edit; a regression to inline literals
18023        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
18024        // quotes from the wire-format constants every consumer routes
18025        // through and this pin flags it at build time.
18026        assert_eq!(
18027            crate::dep::DepList::Prod.as_str(),
18028            crate::render::DEP_AUTHOR_KEY_DEPS
18029        );
18030        assert_eq!(
18031            crate::dep::DepList::Dev.as_str(),
18032            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
18033        );
18034    }
18035
18036    #[test]
18037    fn dep_list_display_routes_through_as_str() {
18038        // Same as-str-through-Display convergence discipline the
18039        // sibling closed-set typed enums carry — a `format!("{list}")`
18040        // call must land byte-for-byte on the accessor's return so a
18041        // future consumer that formats the enum for a diagnostic line
18042        // reaches the same wire-format constant the wire-format
18043        // producers do.
18044        assert_eq!(
18045            format!("{}", crate::dep::DepList::Prod),
18046            crate::dep::DepList::Prod.as_str()
18047        );
18048        assert_eq!(
18049            format!("{}", crate::dep::DepList::Dev),
18050            crate::dep::DepList::Dev.as_str()
18051        );
18052    }
18053
18054    #[test]
18055    fn dep_list_all_enumerates_every_variant_once() {
18056        // Exhaustive-iteration pin — every arm appears exactly once in
18057        // `ALL`, matching the closed set the compiler enforces on the
18058        // sibling `match self` arms. A future variant addition that
18059        // extends only one method's match without extending `ALL`
18060        // would silently drop the new arm from every consumer that
18061        // iterates the slice.
18062        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
18063        assert!(variants.contains(&crate::dep::DepList::Prod));
18064        assert!(variants.contains(&crate::dep::DepList::Dev));
18065        assert_eq!(variants.len(), 2);
18066    }
18067
18068    #[test]
18069    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
18070        // Reverse projection on the two-list dep-graph axis: the
18071        // author-surface wire tag the sibling `as_str` emitter walks
18072        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
18073        // `Some(DepList::Prod)`. A regression that hand-rolled the
18074        // per-arm match without routing through the lifted
18075        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
18076        // future wire-tag rebrand and this pin flags it at build time.
18077        assert_eq!(
18078            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
18079            Some(crate::dep::DepList::Prod)
18080        );
18081    }
18082
18083    #[test]
18084    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
18085        // Peer of the `Prod`-arm pin on the dev-only axis: the
18086        // author-surface wire tag the sibling `as_str` emitter walks
18087        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
18088        // back to `Some(DepList::Dev)`. Same drift-detection posture
18089        // as the peer arm — the sibling method `match` arms are
18090        // compiler-checked exhaustive so a future variant addition
18091        // trips at build time.
18092        assert_eq!(
18093            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
18094            Some(crate::dep::DepList::Dev)
18095        );
18096    }
18097
18098    #[test]
18099    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
18100        // Every input outside the closed-set arm-string set the
18101        // sibling `as_str` emitter walks lands on the terminal `None`
18102        // fallback — no silent-accept surface. Sweeps a set of
18103        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
18104        // rebrand candidates, foreign wire tags, empty string) so a
18105        // future variant addition that widened one wire form without
18106        // extending the emitter's arm-set would trip the sibling
18107        // round-trip pin below rather than silently accepting the new
18108        // form here.
18109        for candidate in [
18110            "",
18111            "deps",
18112            "deps-dev",
18113            ":deps ",
18114            ":Deps",
18115            ":DEPS",
18116            ":build-dep",
18117            ":tool-dep",
18118            "prod",
18119            "dev",
18120        ] {
18121            assert_eq!(
18122                crate::dep::DepList::from_wire(candidate),
18123                None,
18124                "from_wire({candidate:?}) must return None; every input outside \
18125                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
18126                 the sibling as_str emitter walks lands on the terminal fallback",
18127            );
18128        }
18129    }
18130
18131    #[test]
18132    fn dep_list_round_trips_through_as_str_and_from_wire() {
18133        // Load-bearing round-trip pin: every arm the `ALL` iteration
18134        // exposes survives the `as_str` → `from_wire` composition
18135        // byte-for-byte. Same discipline the sibling closed-set enums
18136        // carry — `CaixaKind` /
18137        // `RestartStrategy` / `RestartPolicy` /
18138        // `PlacementStrategy` — extended onto the two-list dep-graph
18139        // axis. A future variant addition that extends `ALL` +
18140        // `as_str` without extending `from_wire` (or vice versa)
18141        // trips at build time on this iteration because the compiler
18142        // enforces exhaustiveness on the sibling `match self` arms.
18143        for &list in crate::dep::DepList::ALL {
18144            assert_eq!(
18145                crate::dep::DepList::from_wire(list.as_str()),
18146                Some(list),
18147                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
18148                 a silent split between the forward emitter and the reverse parser \
18149                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
18150            );
18151        }
18152    }
18153
18154    #[test]
18155    fn push_dep_routes_to_deps_slot_on_prod_arm() {
18156        // The `Prod` arm dispatches to the runtime-closure `:deps`
18157        // slot every downstream lacre-pipeline consumer resolves at
18158        // build time. A future arm that regressed to inline `&mut
18159        // self.deps_dev` on the `Prod` path would silently reroute
18160        // every runtime dep into the dev-only closure at publish time
18161        // — this pin refuses that regression.
18162        let src = Caixa::template("host");
18163        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18164        let before_deps = caixa.deps().len();
18165        let before_deps_dev = caixa.deps_dev().len();
18166        let dep = Dep {
18167            nome: "caixa-teia".to_string(),
18168            versao: "^0.1".to_string(),
18169            fonte: None,
18170            opcional: false,
18171            caracteristicas: Vec::new(),
18172        };
18173        caixa
18174            .push_dep(crate::dep::DepList::Prod, dep)
18175            .expect("first push into :deps succeeds");
18176        assert_eq!(caixa.deps().len(), before_deps + 1);
18177        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
18178        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
18179    }
18180
18181    #[test]
18182    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
18183        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
18184        // must dispatch to the dev-only-closure `:deps-dev` slot every
18185        // downstream test-facing artifact resolver reads. A future
18186        // regression that inverted the two arms would silently route
18187        // every dev-only dep into the runtime closure at publish time
18188        // and this pin catches it before the drift ships.
18189        let src = Caixa::template("host");
18190        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18191        let dep = Dep {
18192            nome: "tatara-check".to_string(),
18193            versao: "*".to_string(),
18194            fonte: None,
18195            opcional: false,
18196            caracteristicas: Vec::new(),
18197        };
18198        caixa
18199            .push_dep(crate::dep::DepList::Dev, dep)
18200            .expect("first push into :deps-dev succeeds");
18201        assert!(caixa.deps().is_empty());
18202        assert_eq!(caixa.deps_dev().len(), 1);
18203        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
18204    }
18205
18206    #[test]
18207    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
18208        // Within-list dup check routes through the canonical
18209        // [`DepError::DuplicateNome`] carrier — the substrate's typed
18210        // diagnostic for the same axis [`Caixa::validate_deps`]'s
18211        // parse-time [`crate::render::insert_first_seen`] walk raises
18212        // on. Prior to the lift the mutation site's inline
18213        // `bail!("dep '{}' already declared", …)` string-diagnostic
18214        // path expressed no through-line back to the typed error;
18215        // routing every dep-list refusal through one carrier means an
18216        // author reading a `feira add` refusal and a `feira build`
18217        // refusal reaches for the same corrective surface without
18218        // switching diagnostic idioms.
18219        let src = Caixa::template("host");
18220        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18221        let dep = Dep {
18222            nome: "caixa-teia".to_string(),
18223            versao: "^0.1".to_string(),
18224            fonte: None,
18225            opcional: false,
18226            caracteristicas: Vec::new(),
18227        };
18228        caixa
18229            .push_dep(crate::dep::DepList::Prod, dep.clone())
18230            .expect("first push succeeds");
18231        let dup = Dep {
18232            nome: "caixa-teia".to_string(),
18233            versao: "^0.2".to_string(),
18234            fonte: None,
18235            opcional: false,
18236            caracteristicas: Vec::new(),
18237        };
18238        let err = caixa
18239            .push_dep(crate::dep::DepList::Prod, dup)
18240            .expect_err("second push with same :nome refuses");
18241        assert_eq!(
18242            err,
18243            DepError::DuplicateNome {
18244                nome: "caixa-teia".to_string(),
18245                list: crate::render::DEP_AUTHOR_KEY_DEPS,
18246            }
18247        );
18248        // The refused mutation must not corrupt the target list —
18249        // exactly one entry lives past the refusal, matching the
18250        // canonical single-source-of-truth invariant `Caixa::deps()`
18251        // carries.
18252        assert_eq!(caixa.deps().len(), 1);
18253    }
18254
18255    #[test]
18256    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
18257        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
18258        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
18259        // `list` payload so a future author reading the refusal grep's
18260        // for the correct `:deps-dev` block in their `caixa.lisp`,
18261        // not the sibling `:deps` block the runtime closure resolves.
18262        let src = Caixa::template("host");
18263        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18264        let dep = Dep {
18265            nome: "tatara-check".to_string(),
18266            versao: "*".to_string(),
18267            fonte: None,
18268            opcional: false,
18269            caracteristicas: Vec::new(),
18270        };
18271        caixa
18272            .push_dep(crate::dep::DepList::Dev, dep.clone())
18273            .expect("first push succeeds");
18274        let err = caixa
18275            .push_dep(crate::dep::DepList::Dev, dep)
18276            .expect_err("second push with same :nome refuses");
18277        assert!(matches!(
18278            err,
18279            DepError::DuplicateNome {
18280                ref nome,
18281                list,
18282            } if nome == "tatara-check"
18283                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
18284        ));
18285    }
18286
18287    #[test]
18288    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
18289        // The within-list dup check is scoped to the target arm — a
18290        // caixa may legitimately carry the same `:nome` under both
18291        // `:deps` and `:deps-dev` (though the substrate's peer
18292        // [`crate::Caixa::validate_deps`] walk still refuses the
18293        // shape at parse time; the mutation-site refusal is scoped to
18294        // the mutation-site's list to match the peer parse-time
18295        // per-list [`crate::render::insert_first_seen`] discipline).
18296        // The two arms hold independent seen-sets.
18297        let src = Caixa::template("host");
18298        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18299        let dep_prod = Dep {
18300            nome: "shared".to_string(),
18301            versao: "^0.1".to_string(),
18302            fonte: None,
18303            opcional: false,
18304            caracteristicas: Vec::new(),
18305        };
18306        let dep_dev = Dep {
18307            nome: "shared".to_string(),
18308            versao: "*".to_string(),
18309            fonte: None,
18310            opcional: false,
18311            caracteristicas: Vec::new(),
18312        };
18313        caixa
18314            .push_dep(crate::dep::DepList::Prod, dep_prod)
18315            .expect("push into :deps succeeds");
18316        caixa
18317            .push_dep(crate::dep::DepList::Dev, dep_dev)
18318            .expect("push same :nome into :deps-dev succeeds");
18319        assert_eq!(caixa.deps().len(), 1);
18320        assert_eq!(caixa.deps_dev().len(), 1);
18321    }
18322
18323    #[test]
18324    fn deps_of_prod_returns_the_deps_slot_verbatim() {
18325        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
18326        // accessor must project onto the runtime-closure `:deps` slot —
18327        // element-equal and length-equal to the sibling per-slot
18328        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
18329        // A future arm that regressed to `self.deps_dev()` on the `Prod`
18330        // path would silently reroute every downstream typed-dispatch
18331        // walker (the [`Caixa::validate_deps`] per-list
18332        // [`crate::render::insert_first_seen`] dedup walk, any future
18333        // per-axis-parametrised consumer) into the sibling dev-only
18334        // closure and this pin refuses that regression.
18335        let src = Caixa::template("host");
18336        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18337        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
18338        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
18339        let dep = Dep {
18340            nome: "caixa-teia".to_string(),
18341            versao: "^0.1".to_string(),
18342            fonte: None,
18343            opcional: false,
18344            caracteristicas: Vec::new(),
18345        };
18346        caixa
18347            .push_dep(crate::dep::DepList::Prod, dep.clone())
18348            .expect("push into :deps succeeds");
18349        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
18350        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
18351        assert_eq!(
18352            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
18353            "caixa-teia"
18354        );
18355    }
18356
18357    #[test]
18358    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
18359        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
18360        // [`Caixa::deps_of`] must project onto the dev-only-closure
18361        // `:deps-dev` slot, element-equal and length-equal to the
18362        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
18363        // future regression that inverted the two arms would silently
18364        // route every dev-list walker onto the runtime closure and this
18365        // pin catches it before the drift ships.
18366        let src = Caixa::template("host");
18367        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18368        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
18369        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
18370        let dep = Dep {
18371            nome: "tatara-check".to_string(),
18372            versao: "*".to_string(),
18373            fonte: None,
18374            opcional: false,
18375            caracteristicas: Vec::new(),
18376        };
18377        caixa
18378            .push_dep(crate::dep::DepList::Dev, dep)
18379            .expect("push into :deps-dev succeeds");
18380        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
18381        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
18382        assert_eq!(
18383            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
18384            "tatara-check"
18385        );
18386    }
18387
18388    #[test]
18389    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
18390        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
18391        // [`Caixa::deps_of`] must land on the same two-slot partition the
18392        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
18393        // expose — the canonical dispatch a future per-axis-parametrised
18394        // walker (a future `feira app graph` per-list dep summary, a
18395        // future M4 per-cluster dev-closure-audit overlay the CR
18396        // materializer resolves per-CR) reads through. Prior to the
18397        // lift the two-block iteration lived open-coded at every walker,
18398        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
18399        // §I) would have had to grow a third block at every consumer.
18400        // A regression that dropped the `Dev` arm from `ALL` would flip
18401        // the collected pairs to `[(":deps", &[])]` alone and this pin
18402        // refuses that shape.
18403        let src = Caixa::template("host");
18404        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18405        let prod_dep = Dep {
18406            nome: "caixa-teia".to_string(),
18407            versao: "^0.1".to_string(),
18408            fonte: None,
18409            opcional: false,
18410            caracteristicas: Vec::new(),
18411        };
18412        let dev_dep = Dep {
18413            nome: "tatara-check".to_string(),
18414            versao: "*".to_string(),
18415            fonte: None,
18416            opcional: false,
18417            caracteristicas: Vec::new(),
18418        };
18419        caixa
18420            .push_dep(crate::dep::DepList::Prod, prod_dep)
18421            .expect("push into :deps succeeds");
18422        caixa
18423            .push_dep(crate::dep::DepList::Dev, dev_dep)
18424            .expect("push into :deps-dev succeeds");
18425        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
18426            .iter()
18427            .map(|&list| {
18428                let slice = caixa.deps_of(list);
18429                (list.as_str(), slice.len(), slice[0].nome())
18430            })
18431            .collect();
18432        assert_eq!(
18433            collected,
18434            vec![
18435                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
18436                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
18437            ]
18438        );
18439    }
18440
18441    #[test]
18442    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
18443        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
18444        // must route its per-list [`crate::render::insert_first_seen`]
18445        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
18446        // rather than the pre-lift open-coded two-block iteration over
18447        // `self.deps()` + `self.deps_dev()`. A regression that dropped
18448        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
18449        // stop refusing within-list dups on the sibling arm; a
18450        // regression that flipped the arm-to-list-key mapping
18451        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
18452        // diagnostic surface. Both drifts surface here through a paired
18453        // duplicate-name refusal per arm plus an offending-list-key
18454        // check on the emitted [`DepError::DuplicateNome`] carrier.
18455        for &list in crate::dep::DepList::ALL {
18456            let src = Caixa::template("host");
18457            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18458            let dup = Dep {
18459                nome: "twin".to_string(),
18460                versao: "^0.1".to_string(),
18461                fonte: None,
18462                opcional: false,
18463                caracteristicas: Vec::new(),
18464            };
18465            match list {
18466                crate::dep::DepList::Prod => {
18467                    caixa.deps.push(dup.clone());
18468                    caixa.deps.push(dup);
18469                }
18470                crate::dep::DepList::Dev => {
18471                    caixa.deps_dev.push(dup.clone());
18472                    caixa.deps_dev.push(dup);
18473                }
18474            }
18475            let err = caixa
18476                .validate_deps()
18477                .expect_err("within-list duplicate :nome must refuse");
18478            assert_eq!(
18479                err,
18480                DepError::DuplicateNome {
18481                    nome: "twin".to_string(),
18482                    list: list.as_str(),
18483                },
18484                "validate_deps on {list} arm must emit \
18485                 DepError::DuplicateNome carrying the arm's own \
18486                 as_str() diagnostic — the arm-to-list-key mapping \
18487                 flowed through DepList::ALL + Caixa::deps_of"
18488            );
18489        }
18490    }
18491
18492    #[test]
18493    fn caixa_licenca_default_pins_canonical_mit_byte() {
18494        // Bridge-arm pin: [`CAIXA_LICENCA_DEFAULT`] resolves to the
18495        // canonical SPDX-`"MIT"` byte today, the same license expression
18496        // every peer substrate-side consumer of the author-omitted
18497        // `:licenca` slot ([`caixa-helm`]'s `build_readme` fallback arm at
18498        // `caixa-helm/src/lib.rs`, the future M4
18499        // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR
18500        // `Chart.yaml annotations["artifacthub.io/license"]` emitter this
18501        // crate's [`Caixa::validate_licenca`] docstring roadmap already
18502        // names as the second consumer) fills into its per-consumer
18503        // README/annotation emit site. Pin the literal here (peer with the
18504        // [`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`] /
18505        // [`crate::version::DEFAULT_GIT_REMOTE`] /
18506        // [`crate::version::DEFAULT_PLEME_GIT_ORG`] canonical-literal pins
18507        // on the sibling lifted-constant surfaces) so a future
18508        // substrate-side license-fallback rebrand surfaces here as a
18509        // coordinated edit-point: the sibling caixa-helm
18510        // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
18511        // pinning test already pins the equality at the renderer-emit
18512        // axis; this pin closes the second coordinate of the pair by
18513        // anchoring the lifted constant's current byte to the canonical
18514        // CAIXA-SDLC §I license scaffold's documented shape.
18515        assert_eq!(CAIXA_LICENCA_DEFAULT, "MIT");
18516    }
18517}