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
277impl Caixa {
278    /// Parse a `caixa.lisp` source string to a typed `Caixa`.
279    ///
280    /// Classifies the dialect **before** parsing. A `(defcaixa …)` of another
281    /// declaration is [`LeituraError::DialetoEstrangeiro`], naming what it is
282    /// and who reads it, instead of an unknown-keyword rejection that reads as
283    /// "your manifest is broken".
284    ///
285    /// The ordering is load-bearing. Handing a foreign dialect to the derive
286    /// first and interpreting the failure afterwards would mean guessing from
287    /// an error message, and the guess would be wrong for every file whose
288    /// first unknown slot happens to be one both schemas could plausibly carry.
289    pub fn from_lisp(src: &str) -> Result<Self, LeituraError> {
290        use tatara_lisp::domain::TataraDomain;
291        let forms = tatara_lisp::read(src).map_err(LeituraError::Leitura)?;
292        let first = forms.first().ok_or(crate::dialeto::DialetoError::Vazio)?;
293
294        // Route the foreign-dialect rejection gate through the lifted
295        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
296        // typed predicate rather than the pre-lift hand-rolled three-arm
297        // `match { Pacote => {}, Desconhecido => {}, foreign => Err(…) }`
298        // literal — the `defmolde` declaration-family partition (the two-
299        // arity closure of [`crate::dialeto::CaixaDialeto::Molde`] and
300        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two arms
301        // whose sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
302        // projection already collapses onto `"defmolde"` and whose sibling
303        // [`crate::dialeto::CaixaDialeto::consumidor`] projection already
304        // collapses onto `"pleme-doc-gen"`) resolves through one dispatch
305        // on the substrate primitive. `Pacote` (the tatara-lisp package
306        // manifest this derive can parse) and `Desconhecido` (deliberately
307        // falls through to the derive rather than short-circuiting: a
308        // `(defcaixa …)` matching neither schema is most likely a genuine
309        // package manifest with a typo in `:nome`, and the derive's
310        // diagnostic — which names the offending keyword and suggests the
311        // nearest slot — is far better than anything this classifier
312        // could say) both return `false` from `is_molde_family()` and fall
313        // through to the derive. Only the typed dialect flows into the
314        // error — the three user-facing projections (canonical keyword,
315        // description, consumer) are read at Display time through
316        // [`crate::dialeto::CaixaDialeto`]'s own accessors, so the
317        // variant cannot carry a snapshot that drifts from
318        // [`crate::dialeto::CaixaDialeto::palavra_canonica`] /
319        // `descricao` / `consumidor`. A future fifth dialect the
320        // [`crate::dialeto`] module doc's "third dialect" hazard
321        // actualises that belongs to the `defmolde` family lands one
322        // match arm at [`crate::dialeto::CaixaDialeto::is_molde_family`]
323        // and this gate picks up the new arm by construction — the pre-
324        // lift wildcard `foreign =>` was compile-time-anonymous and would
325        // silently absorb any hypothetical fifth `defcaixa`-family arm as
326        // foreign; routing the partition through the typed predicate
327        // closes both drift surfaces.
328        let dialeto = crate::dialeto::classify_form(first)?;
329        if dialeto.is_molde_family() {
330            return Err(LeituraError::DialetoEstrangeiro { dialeto });
331        }
332
333        Self::compile_from_sexp(first).map_err(LeituraError::Leitura)
334    }
335
336    /// Register `Caixa` with the global tatara-lisp domain registry so
337    /// `defcaixa` is dispatchable from any tatara-lisp binary that seeds
338    /// the registry (e.g. `tatara-check`).
339    ///
340    /// `pending-fallible-register`: upstream `tatara_lisp::domain::register`
341    /// became `-> Result<(), KeywordCollision>` on 2026-07-31, so a second type
342    /// claiming `defcaixa` in one process is refused and named instead of
343    /// silently displacing this one. This workspace pins
344    /// `tatara-lisp = "0.3.3"`, which predates that, so the result cannot be
345    /// checked here yet. Propagate it — `pub fn register() -> Result<(),
346    /// tatara_lisp::KeywordCollision>` — in the same commit that bumps the pin.
347    pub fn register() {
348        tatara_lisp::domain::register::<Self>();
349    }
350
351    /// Substrate-canonical per-`Caixa` `:licenca` SPDX-expression scalar
352    /// accessor every consumer of the top-level manifest's license axis
353    /// keys off — returns the author-declared `:licenca` byte-string
354    /// verbatim as an `Option<&str>`, borrowed from the typed slot's own
355    /// `Option<String>` storage. `None` when the slot is absent (the
356    /// canonical "omit to defer to the caixa-helm renderer's `MIT`
357    /// fallback" shape [`Self::validate_licenca`] documents at
358    /// caixa-core/src/manifest.rs:1560; the peer [`caixa-helm`]
359    /// `build_readme` fold at caixa-helm/src/lib.rs:962 reads this
360    /// predicate too, so an authored-but-unset `:licenca` round-trips to
361    /// a rendered `lareira-<nome>` chart's `README.md` `## License`
362    /// section structurally identical to one that omits the slot).
363    ///
364    /// The `:licenca` slot carries the universal-axis SPDX-expression
365    /// license identifier every kind of caixa emits under (CAIXA-SDLC
366    /// §I — the author-facing surface every `defcaixa` form supplies) —
367    /// the typed slot's `Option<String>` accept-set (empty-string
368    /// rejected through [`ManifestError::LicencaEmpty`], SPDX-alphabet-
369    /// invalid rejected through [`ManifestError::LicencaInvalid`]) maps
370    /// onto the `lareira-<nome>` Helm chart's `README.md` `## License`
371    /// section (caixa-helm/src/lib.rs:962) and (through future
372    /// tightening documented at [`Self::validate_licenca`]) the
373    /// Chart.yaml `annotations["artifacthub.io/license"]` axis every
374    /// registry-facing chart carries. Every downstream consumer that
375    /// reads the license byte-string keys off this scalar (the
376    /// [`Self::validate_licenca`] empty-arm + SPDX-shape gate that
377    /// routes through `self.licenca.as_deref()`, the caixa-helm
378    /// `build_readme` `unwrap_or_else(|| "MIT".into())` fold that keys
379    /// the fallback off the `Option::is_none()` arm, every future
380    /// per-`Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
381    /// acknowledges).
382    ///
383    /// Prior to this lift the `.licenca` field was accessed inline at
384    /// two production sites — [`Self::validate_licenca`]'s
385    /// `self.licenca.as_deref()` empty-and-shape gate binding and the
386    /// caixa-helm `build_readme` `caixa.licenca.clone().unwrap_or_else(||
387    /// "MIT".into())` `README.md` `## License` fold — two open-coded
388    /// field-accesses that expressed no compile-time link back to the
389    /// typed slot. A future extension of the `:licenca` axis to a
390    /// richer author surface — a per-`:licenca` structured SPDX
391    /// expression parser + license-id allowlist (the future tightening
392    /// [`Self::validate_licenca`]'s docstring acknowledges), a
393    /// per-cluster license-default overlay the M4 CR materializer
394    /// resolves per-CR (the "cluster policy pins `Apache-2.0` for every
395    /// unlisted caixa" arm), a promotion of the plain
396    /// `Option<String>` byte-string to a richer `SpdxExpression` enum
397    /// once the SPDX-expression parser lands — would have had to be
398    /// threaded through both open-coded copies in lockstep or the
399    /// validate gate and the caixa-helm emit path would silently
400    /// disagree on which license a given [`Caixa`] resolves to (an
401    /// author's `:licenca "MIT OR Apache-2.0"` would satisfy validate
402    /// while the emit path silently rendered a stale `MIT` fallback,
403    /// or vice versa). Lifting the resolution to a typed method on the
404    /// substrate primitive means every downstream consumer of the
405    /// caixa's per-`Caixa` license surface reaches for exactly one
406    /// typed dispatch — the resolver's accept-set migrates as a unit
407    /// on any future axis addition.
408    ///
409    /// First `Option<&str>`-return top-level [`Caixa`] scalar accessor —
410    /// opens the "outer [`Caixa`] `Option<&str>` scalar" projection
411    /// pattern the sibling per-`Caixa` `:descricao` / `:repositorio` /
412    /// `:edicao` future lifts fold on. Same "one typed dispatch on the
413    /// substrate primitive, thin projections at each consumer"
414    /// discipline the peer per-`:placement` [`crate::aplicacao::Placement::shard_key`]
415    /// (7cd2a28) / [`crate::aplicacao::Placement::affinity`] (74ec2d3)
416    /// / per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
417    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
418    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
419    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
420    /// typed-slot atom axes, extended here to the outer top-level
421    /// `Caixa` universal-axis surface. Named `licenca()` to match the
422    /// storage field's name; the accessor's identity maps onto the
423    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
424    /// carries.
425    #[must_use]
426    pub fn licenca(&self) -> Option<&str> {
427        self.licenca.as_deref()
428    }
429
430    /// Substrate-canonical per-`Caixa` `:repositorio` git-repo-URL scalar
431    /// accessor every consumer of the top-level manifest's homepage /
432    /// source-of-truth axis keys off — returns the author-declared
433    /// `:repositorio` byte-string verbatim as an `Option<&str>`, borrowed
434    /// from the typed slot's own `Option<String>` storage. `None` when
435    /// the slot is absent (the canonical "omit to defer to the renderer's
436    /// per-target placeholder" shape — [`caixa-helm`]'s `ChartYaml.home`
437    /// carries the `Option<String>` through verbatim so an author-omitted
438    /// `:repositorio` renders a `Chart.yaml` without a `home:` field
439    /// (`skip_serializing_if = "Option::is_none"`), while [`caixa-flux`]'s
440    /// `ClusterBundleOpts::for_caixa` folds the omitted slot through a
441    /// `format!("https://github.com/{DEFAULT_PLEME_GIT_ORG}/{nome}")`
442    /// fallback derived from `caixa.nome`).
443    ///
444    /// The `:repositorio` slot carries the universal-axis git-repo-URL
445    /// homepage identifier every kind of caixa emits under (CAIXA-SDLC
446    /// §I — the author-facing surface every `defcaixa` form supplies) —
447    /// the typed slot's `Option<String>` accept-set (empty-string
448    /// rejected through [`ManifestError::RepositorioEmpty`], git-repo-URL-
449    /// shape-invalid rejected through [`ManifestError::RepositorioInvalid`]
450    /// past the shared [`crate::render::is_git_repo_url`] predicate the
451    /// peer per-`:deps :fonte :repo` axis also routes through) maps onto
452    /// four load-bearing downstream consumers:
453    ///
454    ///   - [`Self::validate_repositorio`]'s empty-arm + shape-predicate
455    ///     gate binding at caixa-core/src/manifest.rs:1456 — the
456    ///     universal-axis identity gate wired at caixa-build time.
457    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.home` fold at
458    ///     caixa-helm/src/lib.rs:840 — the rendered `lareira-<nome>`
459    ///     Helm chart's `Chart.yaml` `home:` field, which every registry
460    ///     that ingests the chart (ArtifactHub, chartmuseum,
461    ///     `helm search repo`) surfaces as the chart's canonical source-
462    ///     of-truth link.
463    ///   - [`caixa-helm`]'s `build_readme` `## Source` fold at
464    ///     caixa-helm/src/lib.rs:957 — the rendered `lareira-<nome>`
465    ///     chart's `README.md` header link back to the source repo,
466    ///     which every author who inspects the rendered chart bundle
467    ///     lands at.
468    ///   - [`caixa-flux`]'s `ClusterBundleOpts::for_caixa`
469    ///     `GitRepository.spec.url` fold at caixa-flux/src/lib.rs:2006 —
470    ///     the rendered `GitRepository` CR's `spec.url` field, which
471    ///     FluxCD's `source-controller` polls to reconcile the caixa's
472    ///     manifest bundle from git.
473    ///
474    /// Prior to this lift the `.repositorio` field was accessed inline
475    /// at four production sites — [`Self::validate_repositorio`]'s
476    /// `self.repositorio.as_deref()` empty-and-shape gate binding, the
477    /// caixa-helm `build_chart_yaml` `caixa.repositorio.clone()`
478    /// `Chart.yaml` `home:` field fold, the caixa-helm `build_readme`
479    /// `caixa.repositorio.clone().unwrap_or_else(|| caixa.nome.clone())`
480    /// `README.md` `## Source` fold, and the caixa-flux
481    /// `ClusterBundleOpts::for_caixa`
482    /// `caixa.repositorio.clone().unwrap_or_else(|| format!(...))`
483    /// `GitRepository.spec.url` fold — four open-coded field-accesses
484    /// that expressed no compile-time link back to the typed slot. A
485    /// future extension of the `:repositorio` axis to a richer author
486    /// surface — a per-`:repositorio` structured
487    /// [`crate::render::GitRepoUrl`]-shaped scheme+host+path parse
488    /// (the future tightening [`Self::validate_repositorio`]'s
489    /// docstring anticipates alongside the peer per-`:deps :fonte
490    /// :repo` axis), a per-cluster repo-mirror overlay the M4 CR
491    /// materializer resolves per-CR (the "cluster policy rewrites
492    /// `github:pleme-io/...` to `git.internal/mirror/pleme-io/...`"
493    /// arm the private-registry story acknowledges), a promotion of
494    /// the plain `Option<String>` byte-string to a richer
495    /// `RepoUrl` enum discriminated on scheme — would have had to be
496    /// threaded through all four open-coded copies in lockstep or the
497    /// validate gate and the three emit paths would silently disagree
498    /// on which URL a given [`Caixa`] resolves to (an author's
499    /// `:repositorio "github:pleme-io/checkout"` would satisfy validate
500    /// while one of the emit paths silently rendered a stale URL, or
501    /// vice versa). Lifting the resolution to a typed method on the
502    /// substrate primitive means every downstream consumer of the
503    /// caixa's per-`Caixa` repo-URL surface reaches for exactly one
504    /// typed dispatch — the resolver's accept-set migrates as a unit on
505    /// any future axis addition.
506    ///
507    /// Second outer top-level [`Caixa`] `Option<&str>`-return scalar
508    /// accessor — sibling of [`Self::licenca`] (6d5bc28), the accessor
509    /// that opened the "outer [`Caixa`] `Option<&str>` scalar"
510    /// projection pattern this lift folds on. Same "one typed dispatch
511    /// on the substrate primitive, thin projections at each consumer"
512    /// discipline the peer per-`:placement`
513    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
514    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
515    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
516    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
517    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
518    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
519    /// typed-slot atom axes, extended here to the second outer top-level
520    /// `Caixa` universal-axis surface. Named `repositorio()` to match
521    /// the storage field's name; the accessor's identity maps onto the
522    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
523    /// carries.
524    #[must_use]
525    pub fn repositorio(&self) -> Option<&str> {
526        self.repositorio.as_deref()
527    }
528
529    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
530    /// chart-description scalar accessor every consumer of the top-level
531    /// manifest's Chart.yaml `description:` axis keys off — returns the
532    /// author-declared `:descricao` byte-string verbatim as an
533    /// `Option<&str>`, borrowed from the typed slot's own
534    /// `Option<String>` storage. `None` when the slot is absent (the
535    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
536    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
537    /// omitted slot through a `format!("Generated chart for caixa Servico
538    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
539    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
540    /// and [`caixa-feira`]'s `render_flake` folds it through a
541    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
542    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
543    ///
544    /// The `:descricao` slot carries the universal-axis free-form-prose
545    /// chart-description identifier every kind of caixa emits under
546    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
547    /// supplies) — the typed slot's `Option<String>` accept-set
548    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
549    /// chart-description-shape-invalid rejected through
550    /// [`ManifestError::DescricaoInvalid`] past the shared
551    /// [`crate::render::is_chart_description_shape`] predicate the peer
552    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
553    /// load-bearing downstream consumers:
554    ///
555    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
556    ///     gate binding — the universal-axis identity gate wired at
557    ///     caixa-build time.
558    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
559    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
560    ///     chart's `Chart.yaml` `description:` field, which
561    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
562    ///     `WARNING [chart.metadata.description]: description is required`
563    ///     when absent) and which every registry that ingests the chart
564    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
565    ///     chart's canonical one-line prose descriptor.
566    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
567    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
568    ///     header directly beneath the `# <chart-name>` title, which
569    ///     every author who inspects the rendered chart bundle lands at.
570    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
571    ///     top-level fold — the emitted `flake.nix`'s `description`
572    ///     field, which every Nix consumer (`nix flake show`,
573    ///     `nix flake metadata`, downstream flake-registry ingestors)
574    ///     surfaces as the flake's canonical descriptor.
575    ///
576    /// Prior to this lift the `.descricao` field was accessed inline at
577    /// four production sites — [`Self::validate_descricao`]'s
578    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
579    /// caixa-helm `build_chart_yaml`
580    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
581    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
582    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
583    /// `README.md` header fold, and the caixa-feira `render_flake`
584    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
585    /// `description = ""` fold — four open-coded field-accesses that
586    /// expressed no compile-time link back to the typed slot. A future
587    /// extension of the `:descricao` axis to a richer author surface —
588    /// a per-`:descricao` locale-tagged multi-language descriptor map
589    /// (the "one caixa, N language-tagged prose descriptions" arm
590    /// author-tooling internationalization anticipates), a
591    /// per-registry-target length-and-shape overlay the M4 CR
592    /// materializer resolves per-CR (the "ArtifactHub caps description
593    /// at 512 bytes but the internal registry caps at 256" arm), a
594    /// promotion of the plain `Option<String>` byte-string to a richer
595    /// `ChartDescription` newtype guaranteeing the
596    /// `is_chart_description_shape` predicate at the type level — would
597    /// have had to be threaded through all four open-coded copies in
598    /// lockstep or the validate gate and the three emit paths would
599    /// silently disagree on which prose string a given [`Caixa`]
600    /// resolves to (an author's
601    /// `:descricao "Checkout flow orchestration."` would satisfy
602    /// validate while one of the emit paths silently rendered a stale
603    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
604    /// resolution to a typed method on the substrate primitive means
605    /// every downstream consumer of the caixa's per-`Caixa`
606    /// chart-description surface reaches for exactly one typed dispatch
607    /// — the resolver's accept-set migrates as a unit on any future
608    /// axis addition.
609    ///
610    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
611    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
612    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
613    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
614    /// lift folds on. Same "one typed dispatch on the substrate
615    /// primitive, thin projections at each consumer" discipline the
616    /// peer per-`:placement`
617    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
618    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
619    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
620    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
621    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
622    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
623    /// typed-slot atom axes, extended here to the third outer top-level
624    /// `Caixa` universal-axis surface. Named `descricao()` to match the
625    /// storage field's name; the accessor's identity maps onto the
626    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
627    /// carries. The one remaining universal `Option<String>` slot
628    /// (`:edicao`) folds on this pattern next.
629    #[must_use]
630    pub fn descricao(&self) -> Option<&str> {
631        self.descricao.as_deref()
632    }
633
634    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
635    /// accessor every consumer of the top-level manifest's tatara-lisp
636    /// edition-selector axis keys off — returns the author-declared
637    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
638    /// the typed slot's own `Option<String>` storage. `None` when the
639    /// slot is absent (the canonical "omit the slot to defer to the
640    /// substrate's default edition" shape every existing
641    /// [`caixa-resolver`] integration test fixture carries via
642    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
643    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
644    /// arm by construction, so an author-omitted `:edicao` round-trips
645    /// to a build without triggering the year-shape predicate).
646    ///
647    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
648    /// decimal-year language-edition identifier every kind of caixa
649    /// emits under (CAIXA-SDLC §I — the author-facing surface every
650    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
651    /// accept-set (empty-string rejected through
652    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
653    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
654    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
655    /// onto one load-bearing downstream consumer today
656    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
657    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
658    /// future edition-aware substrate consumer the CAIXA-SDLC §I
659    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
660    /// selector every edition-aware build step keys off, the future
661    /// per-edition compatibility-flag overlay the M4 CR materializer
662    /// resolves per-CR, the peer [`Caixa::template`] canonical
663    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
664    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
665    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
666    /// carry `edicao: Some("2026".into())` by construction).
667    ///
668    /// Prior to this lift the `.edicao` field was accessed inline at
669    /// one production site — [`Self::validate_edicao`]'s
670    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
671    /// open-coded field-access that expressed no compile-time link
672    /// back to the typed slot. A future extension of the `:edicao`
673    /// axis to a richer author surface — a per-`:edicao` known-
674    /// edition allowlist (the future tightening
675    /// [`Self::validate_edicao`]'s docstring acknowledges past the
676    /// structural year-shape floor, rejecting year-shaped values that
677    /// don't name a tatara-lisp edition the substrate actually
678    /// understands — `"1999"` is year-shaped but no `1999` edition
679    /// exists), a per-edition compatibility-flag overlay the M4 CR
680    /// materializer resolves per-CR (the "edition `"2026"` enables
681    /// macro-surface features the sibling `"2018"` gates behind a
682    /// feature flag" arm the edition-selector story anticipates), a
683    /// promotion of the plain `Option<String>` byte-string to a
684    /// richer `CaixaEdition` enum discriminated on year once a sibling
685    /// edition to `"2026"` lands — would have had to be threaded
686    /// through the open-coded copy in lockstep with every future
687    /// edition-aware consumer, or the validate gate and the future
688    /// edition-aware consumer path would silently disagree on which
689    /// edition a given [`Caixa`] resolves to (an author's
690    /// `:edicao "2026"` would satisfy validate while a future
691    /// edition-aware consumer silently defaulted to a stale edition,
692    /// or vice versa). Lifting the resolution to a typed method on
693    /// the substrate primitive means every downstream consumer of the
694    /// caixa's per-`Caixa` edition surface reaches for exactly one
695    /// typed dispatch — the resolver's accept-set migrates as a unit
696    /// on any future axis addition.
697    ///
698    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
699    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
700    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
701    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
702    /// `Option<&str>` scalar" projection pattern this lift folds on.
703    /// Same "one typed dispatch on the substrate primitive, thin
704    /// projections at each consumer" discipline the peer per-`:placement`
705    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
706    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
707    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
708    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
709    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
710    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
711    /// typed-slot atom axes, extended here to close the outer top-level
712    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
713    /// slot. Named `edicao()` to match the storage field's name; the
714    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
715    /// vocabulary the slot's docstring already carries.
716    #[must_use]
717    pub fn edicao(&self) -> Option<&str> {
718        self.edicao.as_deref()
719    }
720
721    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
722    /// label caixa-identity scalar accessor every consumer of the top-
723    /// level manifest's identity axis keys off — returns the author-
724    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
725    /// the typed slot's own `String` storage. Non-optional (`:nome` is
726    /// a required-axis scalar every `defcaixa` form must supply; the
727    /// [`Self::from_lisp`] derive rejects an omitted / non-string
728    /// `:nome` at parse time, so a `Caixa` past parse definitionally
729    /// carries a non-`None` `:nome`).
730    ///
731    /// The `:nome` slot carries the universal-axis DNS-1123-label
732    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
733    /// the primary identity axis every `defcaixa` form supplies
734    /// alongside `:versao` / `:kind`; the substrate-wide identity every
735    /// other typed surface that names a caixa reaches through — `:deps`
736    /// entries, `:membros` entries, `:children` entries, the
737    /// `lareira-<nome>` Helm chart name every per-Servico renderer
738    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
739    /// renderer emits) — the typed slot's `String` accept-set (empty
740    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
741    /// invalid rejected through [`ManifestError::NomeInvalid`] past
742    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
743    /// the peer name axes each land on, joint-length-with-`lareira-`-
744    /// prefix rejected through
745    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
746    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
747    /// load-bearing downstream consumer the substrate carries — the
748    /// two universal-axis validate gates at caixa-build time
749    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
750    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
751    /// derivation every per-Servico renderer keys off, the caixa-helm
752    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
753    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
754    /// `HTTPRoute` per-Aplicacao name axes at
755    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
756    /// [`crate::pleme_program_selector`] /
757    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
758    /// derivations, and every future substrate renderer that emits an
759    /// artifact keyed by the caixa's identity.
760    ///
761    /// Prior to this lift the `.nome` field was accessed inline at a
762    /// dozen production sites across `caixa-core` (the two universal-
763    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
764    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
765    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
766    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
767    /// entry `name:` fold, the `flux_kustomization_source_subtree`
768    /// per-cluster subpath derivation), and `caixa-mesh` (the
769    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
770    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
771    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
772    /// insert) — a dozen open-coded field-accesses that expressed no
773    /// compile-time link back to the typed slot. A future extension of
774    /// the `:nome` axis to a richer author surface — a per-`:nome`
775    /// structured `CaixaIdentity` newtype that carries the joint-
776    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
777    /// enforces at the type level (rather than as a validate-time
778    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
779    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
780    /// `partner-org/checkout` collision" arm the multi-tenant-registry
781    /// story acknowledges), a promotion of the plain `String` byte-
782    /// string to a richer `CaixaNome` newtype discriminated on
783    /// namespace prefix — would have had to be threaded through every
784    /// open-coded copy in lockstep or the two validate gates and the
785    /// dozen emit paths would silently disagree on which identity a
786    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
787    /// would satisfy validate while one of the emit paths silently
788    /// rendered a drifted other identity, or vice versa). Lifting the
789    /// resolution to a typed method on the substrate primitive means
790    /// every downstream consumer of the caixa's per-`Caixa` identity
791    /// surface reaches for exactly one typed dispatch — the resolver's
792    /// accept-set migrates as a unit on any future axis addition.
793    ///
794    /// First outer top-level [`Caixa`] `&str`-return required-scalar
795    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
796    /// projection pattern the sibling per-`Caixa` `:versao` future lift
797    /// folds on. Sibling in shape to the peer per-`:membros`
798    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
799    /// [`crate::aplicacao::WitContract::source`] /
800    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
801    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
802    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
803    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
804    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
805    /// per-sub-struct required-axis accessors carry on the sibling M3
806    /// mesh-slot-atom scalar-value axes, extended here to open the
807    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
808    /// Named `nome()` to match the storage field's name; the accessor's
809    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
810    /// slot's docstring already carries.
811    #[must_use]
812    pub fn nome(&self) -> &str {
813        &self.nome
814    }
815
816    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
817    /// pinned-version scalar accessor every consumer of the top-level
818    /// manifest's version axis keys off — returns the author-declared
819    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
820    /// typed slot's own `String` storage. Non-optional (`:versao` is a
821    /// required-axis scalar every `defcaixa` form must supply alongside
822    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
823    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
824    /// parse definitionally carries a non-`None` `:versao`).
825    ///
826    /// The `:versao` slot carries the universal-axis SemVer-2
827    /// concrete-version body every kind of caixa emits under
828    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
829    /// supplies alongside `:nome` / `:kind`; the substrate-wide
830    /// pinned-version every downstream artifact-emitting consumer
831    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
832    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
833    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
834    /// prefix composes on top of, the programs.yaml entry's `versao:`
835    /// value the `lareira-fleet-programs` aggregator carries onto each
836    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
837    /// tags every substrate-side `skopeo push` writes, the lacre
838    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
839    /// prior-version references peers in the exact same SemVer-2 shape).
840    /// The typed slot's `String` accept-set (empty rejected through
841    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
842    /// through [`ManifestError::VersaoInvalid`] past
843    /// [`semver::Version::parse`]) maps onto every load-bearing
844    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
845    /// universal-axis validate gate at caixa-build time, the
846    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
847    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
848    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
849    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
850    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
851    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
852    /// tag derivation (`format!("{prefix}{versao}")`), and every future
853    /// substrate renderer that emits an artifact keyed by the caixa's
854    /// pinned version.
855    ///
856    /// Prior to this lift the `.versao` field was accessed inline at a
857    /// dozen production sites across `caixa-core` (the universal-axis
858    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
859    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
860    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
861    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
862    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
863    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
864    /// (the `feira publish` git-tag derivation + the `feira app graph` /
865    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
866    /// field-accesses that expressed no compile-time link back to the
867    /// typed slot. A future extension of the `:versao` axis to a richer
868    /// author surface — a per-`:versao` structured `CaixaVersion` at the
869    /// storage layer (the substrate already carries a `CaixaVersion`
870    /// newtype at [`crate::version::CaixaVersion`], deferred until the
871    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
872    /// a per-registry `:versao` immutability overlay the M4 CR
873    /// materializer enforces per-CR, a promotion of the plain `String`
874    /// byte-string to a richer `PinnedVersao` newtype discriminated on
875    /// SemVer-2 pre-release / build-metadata presence — would have had
876    /// to be threaded through every open-coded copy in lockstep or the
877    /// validate gate and the dozen emit paths would silently disagree
878    /// on which version a given [`Caixa`] resolves to (an author's
879    /// `:versao "0.1.0"` would satisfy validate while one of the emit
880    /// paths silently rendered a drifted other version, or vice versa).
881    /// Lifting the resolution to a typed method on the substrate
882    /// primitive means every downstream consumer of the caixa's
883    /// per-`Caixa` pinned-version surface reaches for exactly one typed
884    /// dispatch — the resolver's accept-set migrates as a unit on any
885    /// future axis addition.
886    ///
887    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
888    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
889    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
890    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
891    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
892    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
893    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
894    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
895    /// on the sibling per-typed-slot version-carrier axes, extended here
896    /// to close the second outer top-level [`Caixa`] required-`&str`-
897    /// carrying axis so the two universal-axis identity-carrying
898    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
899    /// share the same "one typed dispatch per axis" discipline. Named
900    /// `versao()` to match the storage field's name; the accessor's
901    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
902    /// slot's docstring already carries.
903    #[must_use]
904    pub fn versao(&self) -> &str {
905        &self.versao
906    }
907
908    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
909    /// closed-set-enum discriminant accessor every consumer of the top-
910    /// level manifest's kind axis keys off — returns the author-declared
911    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
912    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
913    /// (`:kind` is a required-axis discriminant every `defcaixa` form
914    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
915    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
916    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
917    /// variant).
918    ///
919    /// The `:kind` slot carries the universal-axis closed-set typed-
920    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
921    /// §I — the primary shape gate every renderer / verifier /
922    /// operator branches on; the five variants `Biblioteca` /
923    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
924    /// the caixa surface into disjoint runtime contracts) — the typed
925    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
926    /// values through the derive-macro's symbol-arm gate, exhaustively
927    /// matched at every downstream dispatch site) maps onto every
928    /// load-bearing downstream consumer the substrate carries:
929    ///
930    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
931    ///     predicate — the canonical two-line
932    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
933    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
934    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
935    ///     ComputeUnit` CR materializer) runs at its entry-point,
936    ///     alongside the [`crate::render::KindMismatch`] error carrier's
937    ///     `actual:` field the diagnostic surfaces to name the offending
938    ///     caixa's variant.
939    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
940    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
941    ///     `_view` composers that fold the flat mesh-slot / supervisor-
942    ///     slot columns into their typed sub-spec only when the kind
943    ///     matches (returns `None` otherwise); the future per-Servico
944    ///     M2-view composer (`servico_view`) will follow the same shape.
945    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
946    ///     coherence gate — the `!self.kind.requires_exe()` /
947    ///     `!self.kind.requires_servicos()` predicates that fence
948    ///     each code-surface slot from the wrong owning kind.
949    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
950    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
951    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
952    ///     coherence error carriers (`SupervisorOwnsCode` /
953    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
954    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
955    ///     / `ForeignCodeSlot`) which each name the offending caixa's
956    ///     variant in their `kind:` field.
957    ///
958    /// Prior to this lift the `.kind` field was accessed inline at
959    /// twenty-plus production sites across `caixa-core` (the
960    /// [`crate::render::require_kind`] entry-gate predicate + the
961    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
962    /// composers, the `declared_foreign_code_slots` per-slot kind-
963    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
964    /// kind ↔ code-surface predicates + four error carriers) — a score
965    /// of open-coded field-accesses that expressed no compile-time link
966    /// back to the typed slot. A future extension of the `:kind` axis
967    /// to a richer author surface — a per-`:kind` sub-variant discriminant
968    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
969    /// variant across the wasm-component / legacy-container / native-
970    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
971    /// kind-overlay the M4 CR materializer resolves per-CR (the
972    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
973    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
974    /// enum to a richer `KindWithRuntime` discriminated on the
975    /// component-model world axis — would have had to be threaded
976    /// through every open-coded copy in lockstep or the entry gate,
977    /// the view composers, and the layout invariants would silently
978    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
979    /// the resolution to a typed method on the substrate primitive
980    /// means every downstream consumer of the caixa's per-`Caixa`
981    /// kind surface reaches for exactly one typed dispatch — the
982    /// resolver's accept-set migrates as a unit on any future axis
983    /// addition.
984    ///
985    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
986    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
987    /// required-discriminant" projection pattern. Sibling in shape to
988    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
989    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
990    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
991    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
992    /// on the sibling nested-spec typed-slot discriminator axes,
993    /// extended here to the outer top-level [`Caixa`] universal-axis
994    /// surface. Named `kind()` to match the storage field's name;
995    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
996    /// vocabulary the slot's docstring already carries.
997    #[must_use]
998    pub fn kind(&self) -> CaixaKind {
999        self.kind
1000    }
1001
1002    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
1003    /// maintainer-name-list slice-accessor every consumer of the top-
1004    /// level manifest's maintainer axis keys off — returns the author-
1005    /// declared `:autores` list verbatim as a `&[String]` slice-view over
1006    /// the same backing buffer the raw `self.autores.as_slice()` field
1007    /// access borrows from. Empty-list-carrying (`:autores` is a default-
1008    /// empty axis every `defcaixa` form supplies with an empty `()` when
1009    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
1010    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1011    /// parse definitionally carries a `Vec<String>` slot — possibly
1012    /// empty — and the returned `&[String]` degenerates to an empty
1013    /// slice on that arm without any silent `None` collapse).
1014    ///
1015    /// The `:autores` slot carries the universal-axis maintainer-name
1016    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1017    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1018    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1019    /// every downstream registry-facing artifact emits under) — the
1020    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1021    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1022    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1023    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1024    /// onto every load-bearing downstream consumer the substrate carries
1025    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1026    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1027    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1028    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1029    /// name, email: None }` record, every future per-`Caixa` registry-
1030    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1031    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1032    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1033    /// the future per-cluster author-notification overlay the M4 CR
1034    /// materializer resolves per-CR).
1035    ///
1036    /// Prior to this lift the `.autores` field was accessed inline at
1037    /// two production sites — [`Self::validate_autores`]'s `for autor
1038    /// in &self.autores` walk that gates every entry through
1039    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1040    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1041    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1042    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1043    /// two open-coded field-accesses that expressed no compile-time link
1044    /// back to the typed slot. A future extension of the `:autores` axis
1045    /// to a richer author surface — a per-`:autores` structured
1046    /// `Maintainer { name, email, url }` at the storage layer once the
1047    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1048    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1049    /// enforces per-CR (the "cluster policy demands every author declare
1050    /// an on-file `mailto:` contact" arm), a promotion of the plain
1051    /// `Vec<String>` byte-string list to a richer
1052    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1053    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1054    /// predicate already resolves through — would have had to be
1055    /// threaded through both open-coded copies in lockstep or the
1056    /// validate gate and the caixa-helm emit path would silently
1057    /// disagree on which authors a given [`Caixa`] resolves to (an
1058    /// author's `:autores ("alice" "bob")` would satisfy validate while
1059    /// the caixa-helm emit path silently rendered a drifted other
1060    /// maintainer list, or vice versa). Lifting the resolution to a
1061    /// typed method on the substrate primitive means every downstream
1062    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1063    /// for exactly one typed dispatch — the resolver's accept-set
1064    /// migrates as a unit on any future axis addition.
1065    ///
1066    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1067    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1068    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1069    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1070    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1071    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1072    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1073    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1074    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1075    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1076    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1077    /// per-M3 typed-slot list axes, extended here to the outer top-level
1078    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1079    /// `&Vec<String>`) because every downstream consumer of the author
1080    /// list treats it as a read-only sequence — the slice-view is the
1081    /// narrowest borrow that supports every present + roadmapped consumer
1082    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1083    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1084    /// reaches for (the storage-side `Vec` remains reachable through the
1085    /// `pub autores` field for the mutation-carrying serde round-trip and
1086    /// per-test fixture-mutation paths). Named `autores()` to match the
1087    /// storage field's name; the accessor's identity maps onto the
1088    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1089    /// carries.
1090    #[must_use]
1091    pub fn autores(&self) -> &[String] {
1092        self.autores.as_slice()
1093    }
1094
1095    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1096    /// registry-search-tag-list slice-accessor every consumer of the
1097    /// top-level manifest's topical-tag axis keys off — returns the
1098    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1099    /// slice-view over the same backing buffer the raw
1100    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1101    /// list-carrying (`:etiquetas` is a default-empty axis every
1102    /// `defcaixa` form supplies with an empty `()` when unset; the
1103    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1104    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1105    /// definitionally carries a `Vec<String>` slot — possibly empty —
1106    /// and the returned `&[String]` degenerates to an empty slice on
1107    /// that arm without any silent `None` collapse).
1108    ///
1109    /// The `:etiquetas` slot carries the universal-axis topical-tag
1110    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1111    /// author-facing surface every `defcaixa` form supplies alongside
1112    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1113    /// search-facing axis every downstream registry-facing artifact
1114    /// emits under) — the typed slot's `Vec<String>` accept-set
1115    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1116    /// non-chart-keyword-shape rejected through
1117    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1118    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1119    /// every load-bearing downstream consumer the substrate carries —
1120    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1121    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1122    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1123    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1124    /// `Chart.yaml` `keywords:` array (chained with the
1125    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1126    /// dedup'd through a `BTreeSet` at emit time), every future per-
1127    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1128    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1129    /// annotation, the future per-cluster tag-notification overlay the
1130    /// M4 CR materializer resolves per-CR).
1131    ///
1132    /// Prior to this lift the `.etiquetas` field was accessed inline at
1133    /// two production sites — [`Self::validate_etiquetas`]'s `for
1134    /// etiqueta in &self.etiquetas` walk that gates every entry through
1135    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1136    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1137    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1138    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1139    /// two open-coded field-accesses that expressed no compile-time
1140    /// link back to the typed slot. A future extension of the
1141    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1142    /// structured `ChartKeyword { name, uri, category }` at the storage
1143    /// layer once the substrate absorbs `artifacthub.io/keywords`
1144    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1145    /// CR materializer enforces per-CR (the "cluster policy demands
1146    /// every tag come from a substrate-approved taxonomy" arm), a
1147    /// promotion of the plain `Vec<String>` byte-string list to a
1148    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1149    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1150    /// already resolves through — would have had to be threaded through
1151    /// both open-coded copies in lockstep or the validate gate and the
1152    /// caixa-helm emit path would silently disagree on which tags a
1153    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1154    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1155    /// path silently rendered a drifted other keyword list, or vice
1156    /// versa). Lifting the resolution to a typed method on the
1157    /// substrate primitive means every downstream consumer of the
1158    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1159    /// typed dispatch — the resolver's accept-set migrates as a unit
1160    /// on any future axis addition.
1161    ///
1162    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1163    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1164    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1165    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1166    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1167    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1168    /// fold onto the same pattern in future lifts. Sibling in shape to
1169    /// the peer per-`:supervisor`
1170    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1171    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1172    /// (a6e18d7), per-`:membros`
1173    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1174    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1175    /// (0dcc926), and per-`:upgrade-from :instructions`
1176    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1177    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1178    /// typed-slot list axes, extended here to the outer top-level
1179    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1180    /// `&Vec<String>`) because every downstream consumer of the tag
1181    /// list treats it as a read-only sequence — the slice-view is the
1182    /// narrowest borrow that supports every present + roadmapped
1183    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1184    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1185    /// the typed view reaches for (the storage-side `Vec` remains
1186    /// reachable through the `pub etiquetas` field for the mutation-
1187    /// carrying serde round-trip and per-test fixture-mutation paths).
1188    /// Named `etiquetas()` to match the storage field's name; the
1189    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1190    /// vocabulary the slot's docstring already carries.
1191    #[must_use]
1192    pub fn etiquetas(&self) -> &[String] {
1193        self.etiquetas.as_slice()
1194    }
1195
1196    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1197    /// library-source-path-list slice-accessor every consumer of the
1198    /// top-level manifest's Biblioteca-source axis keys off — returns
1199    /// the author-declared `:bibliotecas` list verbatim as a
1200    /// `&[String]` slice-view over the same backing buffer the raw
1201    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1202    /// list-carrying (`:bibliotecas` is a default-empty axis every
1203    /// `defcaixa` form supplies with an empty `()` when unset; the
1204    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1205    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1206    /// parse definitionally carries a `Vec<String>` slot — possibly
1207    /// empty — and the returned `&[String]` degenerates to an empty
1208    /// slice on that arm without any silent `None` collapse).
1209    ///
1210    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1211    /// entry-path list every `:kind Biblioteca` caixa emits under
1212    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1213    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1214    /// substrate-wide library-carrier axis every downstream
1215    /// authoring-facing consumer keys off) — the typed slot's
1216    /// `Vec<String>` accept-set (empty-per-entry rejected through
1217    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1218    /// non-sandboxed-relative-shape rejected through
1219    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1220    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1221    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1222    /// maps onto every load-bearing downstream consumer the substrate
1223    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1224    /// empty-check + per-entry file-exists loop at
1225    /// caixa-core/src/layout.rs that gates each entry through
1226    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1227    /// [`Self::validate_code_paths`] per-slot shape gate at
1228    /// caixa-core/src/manifest.rs that walks each entry through the
1229    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1230    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1231    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1232    /// declared library file for lexical / structural errors before
1233    /// downstream `importar` resolution, every future per-`Caixa`
1234    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1235    /// (the future `tatara-lispc` compilation entry the docstring at
1236    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1237    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1238    /// the future `caixa-lsp` per-library semantic-token stream the
1239    /// caixa-lsp docstring roadmaps).
1240    ///
1241    /// Prior to this lift the `.bibliotecas` field was accessed inline
1242    /// at three production sites — [`crate::LayoutInvariants`]'s
1243    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1244    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1245    /// declared library path through the on-disk-existence check,
1246    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1247    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1248    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1249    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1250    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1251    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1252    /// coded field-accesses that expressed no compile-time link back
1253    /// to the typed slot. A future extension of the `:bibliotecas`
1254    /// axis to a richer library surface — a per-`:bibliotecas`
1255    /// structured `BibliotecaEntry { path, edition, exports }` at the
1256    /// storage layer once the substrate absorbs the per-library
1257    /// language-edition + explicit-exports tuple the tatara-lisp
1258    /// module-system roadmap acknowledges, a per-registry
1259    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1260    /// per-CR (the "cluster policy demands every biblioteca declare
1261    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1262    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1263    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1264    /// [`crate::render::is_sandboxed_relative_path`] +
1265    /// [`crate::render::is_lisp_extension`] predicates already resolve
1266    /// through — would have had to be threaded through all three
1267    /// open-coded copies in lockstep or the layout gate, the shape
1268    /// validator, and the `feira build` phase-1 parse walk would
1269    /// silently disagree on which library paths a given [`Caixa`]
1270    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1271    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1272    /// silently parsed a drifted other list, or vice versa). Lifting
1273    /// the resolution to a typed method on the substrate primitive
1274    /// means every downstream consumer of the caixa's per-`Caixa`
1275    /// library-source surface reaches for exactly one typed dispatch
1276    /// — the resolver's accept-set migrates as a unit on any future
1277    /// axis addition.
1278    ///
1279    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1280    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1281    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1282    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1283    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1284    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1285    /// `:children` / `:membros` / `:contratos`) fold onto the same
1286    /// pattern in future lifts. Sibling in shape to the peer
1287    /// per-`:supervisor`
1288    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1289    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1290    /// (a6e18d7), per-`:membros`
1291    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1292    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1293    /// (0dcc926), and per-`:upgrade-from :instructions`
1294    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1295    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1296    /// typed-slot list axes, extended here to the outer top-level
1297    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1298    /// `&Vec<String>`) because every downstream consumer of the
1299    /// library-source list treats it as a read-only sequence — the
1300    /// slice-view is the narrowest borrow that supports every
1301    /// present + roadmapped consumer (`.iter()`, `.len()`,
1302    /// `.is_empty()`) without leaking the backing `Vec`'s
1303    /// grow/push/reserve surface no consumer of the typed view
1304    /// reaches for (the storage-side `Vec` remains reachable through
1305    /// the `pub bibliotecas` field for the mutation-carrying serde
1306    /// round-trip and per-test fixture-mutation paths). Named
1307    /// `bibliotecas()` to match the storage field's name; the
1308    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1309    /// vocabulary the slot's docstring already carries.
1310    #[must_use]
1311    pub fn bibliotecas(&self) -> &[String] {
1312        self.bibliotecas.as_slice()
1313    }
1314
1315    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1316    /// nix-built-executable-entry-path-list slice-accessor every consumer
1317    /// of the top-level manifest's Binario-executable axis keys off —
1318    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1319    /// slice-view over the same backing buffer the raw
1320    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1321    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1322    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1323    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1324    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1325    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1326    /// degenerates to an empty slice on that arm without any silent
1327    /// `None` collapse).
1328    ///
1329    /// The `:exe` slot carries the universal-axis nix-built executable
1330    /// entry-path list every `:kind Binario` caixa emits under
1331    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1332    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1333    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1334    /// downstream flake-build-facing consumer keys off) — the typed
1335    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1336    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1337    /// non-sandboxed-relative-shape rejected through
1338    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1339    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1340    /// directory paths rejected past the layout's
1341    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1342    /// onto every load-bearing downstream consumer the substrate carries
1343    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1344    /// per-entry file-exists + `exe/`-directory-fence loop at
1345    /// caixa-core/src/layout.rs that gates each entry through
1346    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1347    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1348    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1349    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1350    /// that fences code-surface slots off from the two no-code kinds,
1351    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1352    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1353    /// fences the `:exe` code surface off from every non-Binario code-
1354    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1355    /// that walks each entry through the sandbox-relative / cross-entry
1356    /// duplicate gates, every future per-`Caixa` executable-facing
1357    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1358    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1359    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1360    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1361    /// future `feira nix` per-executable Binario-target emit path).
1362    ///
1363    /// Prior to this lift the `.exe` field was accessed inline at three
1364    /// production sites — the compound-code-path `has_code =
1365    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1366    /// !caixa.servicos.is_empty()` OR-fold on the
1367    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1368    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1369    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1370    /// gate, the per-entry `for p in &caixa.exe`
1371    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1372    /// [`Self::declared_foreign_code_slots`]'s
1373    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1374    /// open-coded field-accesses that expressed no compile-time link
1375    /// back to the typed slot. A future extension of the `:exe` axis
1376    /// to a richer executable surface — a per-`:exe` structured
1377    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1378    /// layer once the substrate absorbs the per-executable
1379    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1380    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1381    /// the M4 CR materializer enforces per-CR (the "cluster policy
1382    /// demands every Binario declare an explicit `:wrapper`" arm), a
1383    /// promotion of the plain `Vec<String>` byte-string list to a
1384    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1385    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1386    /// fence already resolves through — would have had to be threaded
1387    /// through all four open-coded copies in lockstep or the layout
1388    /// gate, the shape validator, and the `feira nix` emit path would
1389    /// silently disagree on which executable paths a given [`Caixa`]
1390    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1391    /// satisfy layout while `feira nix` silently packaged a drifted
1392    /// other list, or vice versa). Lifting the resolution to a typed
1393    /// method on the substrate primitive means every downstream
1394    /// consumer of the caixa's per-`Caixa` executable-source surface
1395    /// reaches for exactly one typed dispatch — the resolver's accept-
1396    /// set migrates as a unit on any future axis addition.
1397    ///
1398    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1399    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1400    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1401    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1402    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1403    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1404    /// future lift closes onto (per the trio of code-surface list slots
1405    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1406    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1407    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1408    /// last unlifted code-surface slot). Sibling in shape to the peer
1409    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1410    /// (bc92bce), per-`:placement`
1411    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1412    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1413    /// (6c77e36), per-`:contratos`
1414    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1415    /// per-`:upgrade-from :instructions`
1416    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1417    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1418    /// typed-slot list axes, extended here to the outer top-level
1419    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1420    /// `&Vec<String>`) because every downstream consumer of the
1421    /// executable-source list treats it as a read-only sequence — the
1422    /// slice-view is the narrowest borrow that supports every
1423    /// present + roadmapped consumer (`.iter()`, `.len()`,
1424    /// `.is_empty()`) without leaking the backing `Vec`'s
1425    /// grow/push/reserve surface no consumer of the typed view
1426    /// reaches for (the storage-side `Vec` remains reachable through
1427    /// the `pub exe` field for the mutation-carrying serde
1428    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1429    /// to match the storage field's name; the accessor's identity
1430    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1431    /// docstring already carries.
1432    #[must_use]
1433    pub fn exe(&self) -> &[String] {
1434        self.exe.as_slice()
1435    }
1436
1437    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1438    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1439    /// of the top-level manifest's Servico-component axis keys off —
1440    /// returns the author-declared `:servicos` list verbatim as a
1441    /// `&[String]` slice-view over the same backing buffer the raw
1442    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1443    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1444    /// form supplies with an empty `()` when unset; the
1445    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1446    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1447    /// definitionally carries a `Vec<String>` slot — possibly empty —
1448    /// and the returned `&[String]` degenerates to an empty slice on
1449    /// that arm without any silent `None` collapse).
1450    ///
1451    /// The `:servicos` slot carries the universal-axis
1452    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1453    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1454    /// author-facing surface every `defcaixa` form supplies alongside
1455    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1456    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1457    /// Servico-facing renderer keys off) — the typed slot's
1458    /// `Vec<String>` accept-set (empty-per-entry rejected through
1459    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1460    /// non-sandboxed-relative-shape rejected through
1461    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1462    /// extension rejected through
1463    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1464    /// entry duplicate rejected through
1465    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1466    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1467    /// renderer entry-points, out-of-`servicos/`-directory paths
1468    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1469    /// `starts_with` fence) maps onto every load-bearing downstream
1470    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1471    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1472    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1473    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1474    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1475    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1476    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1477    /// that fences code-surface slots off from the two no-code kinds,
1478    /// [`Self::declared_foreign_code_slots`]'s
1479    /// `!self.servicos.is_empty()` arm on the
1480    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1481    /// `:servicos` code surface off from every non-Servico code-running
1482    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1483    /// walks each entry through the sandbox-relative / `.computeunit.
1484    /// yaml`-extension / cross-entry duplicate gates, the
1485    /// [`crate::require_single_servico`] V0 singularity gate every
1486    /// per-Servico renderer entry-point runs through
1487    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1488    /// `feira deploy` per-verb `first_servico_path` walk at
1489    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1490    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1491    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1492    /// per-Servico OCI packager, the future M4
1493    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1494    /// per-Servico OTel collector-config emit).
1495    ///
1496    /// Prior to this lift the `.servicos` field was accessed inline at
1497    /// five production sites — the compound-code-path `has_code =
1498    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1499    /// !caixa.servicos.is_empty()` OR-fold on the
1500    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1501    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1502    /// `caixa.servicos.is_empty()`
1503    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1504    /// per-entry `for p in &caixa.servicos`
1505    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1506    /// [`Self::declared_foreign_code_slots`]'s
1507    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1508    /// and the [`crate::require_single_servico`] V0 count gate's
1509    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1510    /// projection (both the accept-arm predicate and the
1511    /// diagnostic-carrying `ServicoCountMismatch { count }`
1512    /// projection) — five open-coded field-accesses across three
1513    /// crates that expressed no compile-time link back to the typed
1514    /// slot. A future extension of the `:servicos` axis to a richer
1515    /// component surface — a per-`:servicos` structured
1516    /// `ServicoEntry { path, world, capabilities }` at the storage
1517    /// layer once the substrate absorbs the per-component WIT-world +
1518    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1519    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1520    /// materializer enforces per-CR (the "cluster policy demands every
1521    /// Servico declare an explicit `:world`" arm), a promotion of the
1522    /// plain `Vec<String>` byte-string list to a richer
1523    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1524    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1525    /// `starts_with(servicos_dir)` fence and the
1526    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1527    /// already resolve through, a promotion of the V0 singleton
1528    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1529    /// component-model multi-world boundary — would have had to be
1530    /// threaded through all five open-coded copies in lockstep or the
1531    /// layout gate, the shape validator, the V0 count gate, and the
1532    /// `feira chart` / `feira deploy` entry-point walks would silently
1533    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1534    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1535    /// yaml")` would satisfy layout while `feira chart` silently
1536    /// packaged a drifted other list, or vice versa). Lifting the
1537    /// resolution to a typed method on the substrate primitive means
1538    /// every downstream consumer of the caixa's per-`Caixa`
1539    /// ComputeUnit-CR-source surface reaches for exactly one typed
1540    /// dispatch — the resolver's accept-set migrates as a unit on any
1541    /// future axis addition.
1542    ///
1543    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1544    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1545    /// projection pattern [`Self::autores`] (b5d813f) opened,
1546    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1547    /// (8a36c23) closed the universal-axis text-tag family of, and
1548    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1549    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1550    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1551    /// a substrate-canonical slice accessor, the trio of code-surface
1552    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1553    /// tuple carries is complete on the typed dispatch surface (the
1554    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1555    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1556    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1557    /// per-element accessor swap in isolation — a future companion lift
1558    /// promotes the tuple's element type to `&[String]` and threads the
1559    /// triple of typed dispatches through as a unit). Sibling in shape
1560    /// to the peer per-`:supervisor`
1561    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1562    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1563    /// (a6e18d7), per-`:membros`
1564    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1565    /// per-`:contratos`
1566    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1567    /// per-`:upgrade-from :instructions`
1568    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1569    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1570    /// typed-slot list axes, extended here to the outer top-level
1571    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1572    /// `&Vec<String>`) because every downstream consumer of the
1573    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1574    /// the slice-view is the narrowest borrow that supports every
1575    /// present + roadmapped consumer (`.iter()`, `.len()`,
1576    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1577    /// grow/push/reserve surface no consumer of the typed view reaches
1578    /// for (the storage-side `Vec` remains reachable through the
1579    /// `pub servicos` field for the mutation-carrying serde round-trip
1580    /// and per-test fixture-mutation paths, and for the
1581    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1582    /// homogeneous-element-type shape carries the raw field access
1583    /// until the trio-closure lift promotes the tuple as a unit).
1584    /// Named `servicos()` to match the storage field's name; the
1585    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1586    /// vocabulary the slot's docstring already carries.
1587    #[must_use]
1588    pub fn servicos(&self) -> &[String] {
1589        self.servicos.as_slice()
1590    }
1591
1592    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
1593    /// runtime-dependency-declaration-list slice-accessor every consumer
1594    /// of the top-level manifest's runtime-dep-graph axis keys off —
1595    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
1596    /// slice-view over the same backing buffer the raw
1597    /// `self.deps.as_slice()` field access borrows from. Empty-list-
1598    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
1599    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1600    /// derive folds an omitted `:deps` through `#[serde(default)]` to
1601    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1602    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
1603    /// degenerates to an empty slice on that arm without any silent
1604    /// `None` collapse).
1605    ///
1606    /// The `:deps` slot carries the universal-axis runtime dependency
1607    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1608    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1609    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
1610    /// every downstream resolver-facing artifact emits under) — the
1611    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
1612    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
1613    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
1614    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
1615    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
1616    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
1617    /// maps onto every load-bearing downstream consumer the substrate
1618    /// carries — the [`Self::validate_deps`] per-entry
1619    /// [`Dep::validate`] + within-list dedup walk at
1620    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
1621    /// cross-list self-reference gate at caixa-core/src/layout.rs that
1622    /// checks each entry against the caixa's own `:nome`, the
1623    /// caixa-resolver `for dep in &root.deps` closure walk at
1624    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
1625    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
1626    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
1627    /// caixa-crd/src/conversion.rs that materializes each entry into the
1628    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
1629    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1630    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
1631    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
1632    /// closure emit walk the caixa-resolver docstring roadmaps).
1633    ///
1634    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1635    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1636    /// sibling `:deps-dev` future lift closes on. Peer of the closed
1637    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
1638    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
1639    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
1640    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
1641    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
1642    /// pattern onto a novel element-type axis (`Dep` composite vs the
1643    /// prior sibling family's `String` scalar). Sibling in shape to the
1644    /// peer per-`:supervisor`
1645    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1646    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1647    /// (a6e18d7), per-`:membros`
1648    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1649    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1650    /// (0dcc926), and per-`:upgrade-from :instructions`
1651    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1652    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1653    /// typed-slot list axes, extended here to the outer top-level
1654    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
1655    /// (not `&Vec<Dep>`) because every downstream consumer of the
1656    /// runtime-dep list treats it as a read-only sequence — the slice-
1657    /// view is the narrowest borrow that supports every present +
1658    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1659    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1660    /// of the typed view reaches for (the storage-side `Vec` remains
1661    /// reachable through the `pub deps` field for the mutation-carrying
1662    /// serde round-trip and per-test fixture-mutation paths). Named
1663    /// `deps()` to match the storage field's name; the accessor's
1664    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1665    /// slot's docstring already carries.
1666    #[must_use]
1667    pub fn deps(&self) -> &[Dep] {
1668        self.deps.as_slice()
1669    }
1670
1671    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
1672    /// development-only-dependency-declaration-list slice-accessor every
1673    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
1674    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
1675    /// slice-view over the same backing buffer the raw
1676    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
1677    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
1678    /// form supplies with an empty `()` when unset; the
1679    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
1680    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1681    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
1682    /// the returned `&[Dep]` degenerates to an empty slice on that arm
1683    /// without any silent `None` collapse).
1684    ///
1685    /// The `:deps-dev` slot carries the universal-axis dev-only
1686    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
1687    /// the author-facing sibling of `:deps` that every `defcaixa` form
1688    /// supplies to declare tests / lint / bench closures the runtime
1689    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
1690    /// axis every downstream test-facing artifact emits under, matching
1691    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
1692    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
1693    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
1694    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
1695    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
1696    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
1697    /// within-list duplicate `:nome` rejected through
1698    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
1699    /// load-bearing downstream consumer the substrate carries — the
1700    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
1701    /// dedup walk at caixa-core/src/manifest.rs, the
1702    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
1703    /// gate at caixa-core/src/layout.rs that checks each entry against
1704    /// the caixa's own `:nome`, the caixa-resolver
1705    /// `for dep in &root.deps_dev` closure walk at
1706    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
1707    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
1708    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
1709    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
1710    /// overlay the M4 CR materializer resolves per-CR, the future
1711    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
1712    /// roadmaps).
1713    ///
1714    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1715    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1716    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
1717    /// jointly close the two-list dep-graph surface every downstream
1718    /// resolver-facing consumer keys off (runtime `:deps` +
1719    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
1720    /// pair the [`Self::validate_deps`] gate already walks in canonical
1721    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
1722    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
1723    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
1724    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
1725    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
1726    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
1727    /// dev-dep composite-element axis (`Dep` composite, matching the
1728    /// [`Self::deps`] element type). Sibling in shape to the peer
1729    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1730    /// (bc92bce), per-`:placement`
1731    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1732    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1733    /// (6c77e36), per-`:contratos`
1734    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1735    /// per-`:upgrade-from :instructions`
1736    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1737    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1738    /// typed-slot list axes, folded here to the outer top-level
1739    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
1740    /// (not `&Vec<Dep>`) because every downstream consumer of the
1741    /// dev-dep list treats it as a read-only sequence — the slice-view
1742    /// is the narrowest borrow that supports every present +
1743    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1744    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1745    /// of the typed view reaches for (the storage-side `Vec` remains
1746    /// reachable through the `pub deps_dev` field for the mutation-
1747    /// carrying serde round-trip and per-test fixture-mutation paths).
1748    /// Named `deps_dev()` to match the storage field's `snake_case` name;
1749    /// the kebab-case author-surface tag `:deps-dev` is the same axis
1750    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
1751    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1752    /// docstring already carries.
1753    #[must_use]
1754    pub fn deps_dev(&self) -> &[Dep] {
1755        self.deps_dev.as_slice()
1756    }
1757
1758    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
1759    /// every consumer that walks one of the two dep-list axes keyed on a
1760    /// [`crate::dep::DepList`] discriminant reaches for — routes the
1761    /// `(list: DepList) -> &[Dep]` projection through one typed method on
1762    /// the substrate primitive rather than the prior open-coded
1763    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
1764    /// inline dispatch every per-axis walker would otherwise carry.
1765    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
1766    /// `&[Dep]` slice-view over the same backing buffer the sibling
1767    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
1768    /// accessors borrow from, preserving the empty-list-carrying invariant
1769    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
1770    /// are default-empty axes every `defcaixa` form supplies with an empty
1771    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
1772    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
1773    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
1774    /// returned `&[Dep]` degenerates to an empty slice on either arm
1775    /// without any silent `None` collapse).
1776    ///
1777    /// The [`crate::dep::DepList`] closed-set typed enum is the
1778    /// substrate's canonical discriminator for the "runtime-closure
1779    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
1780    /// consumer dispatches on — the compiler-checked exhaustiveness on
1781    /// the enum's `match` arms is the build-time guarantee that no future
1782    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
1783    /// that a future third dep-list axis (a `:deps-build` build-only
1784    /// closure once the substrate grows cross-artifact heterogeneous
1785    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
1786    /// consumer. Prior to this the read side carried two per-slot
1787    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
1788    /// typed dispatch that a per-axis walker could parametrise on, so
1789    /// every per-list walker (the [`Self::validate_deps`] per-list
1790    /// [`crate::render::insert_first_seen`] dedup walk, a future
1791    /// `feira app graph` per-list dep summary, a future M4 per-cluster
1792    /// dev-closure-audit overlay the CR materializer resolves per-CR)
1793    /// open-coded the same two-block "run over `:deps`, then run over
1794    /// `:deps-dev`" pattern — a silent duplication that a future third
1795    /// dep-list axis would have had to grow a third block at every site.
1796    ///
1797    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
1798    /// (359fba5) — closes the two-side dispatch symmetry on the outer
1799    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
1800    /// side, `deps_of` on the read side, both keyed on the same
1801    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
1802    /// the substrate primitive, thin projections at each consumer"
1803    /// discipline the sibling per-slot read accessors ([`Self::nome`]
1804    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
1805    /// the outer-[`Caixa`] typed-dispatch read surface.
1806    #[must_use]
1807    pub fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
1808        match list {
1809            crate::dep::DepList::Prod => self.deps(),
1810            crate::dep::DepList::Dev => self.deps_dev(),
1811        }
1812    }
1813
1814    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
1815    /// consumer that appends to one of the two dep-list axes keys off
1816    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
1817    /// method on the substrate primitive rather than the prior
1818    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
1819    /// else { &mut caixa.deps }` inline dispatch + open-coded
1820    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
1821    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
1822    /// a within-list name collision — the same `list: &'static str`
1823    /// diagnostic shape [`Self::validate_deps`]'s per-list
1824    /// [`crate::render::insert_first_seen`] walk raises on the peer
1825    /// parse-time within-list dedup axis, so a future author reading a
1826    /// `feira add` refusal and a `feira build` refusal reaches for the
1827    /// same corrective surface without switching diagnostic idioms.
1828    ///
1829    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
1830    /// closed-set typed carrier for the "runtime-closure `:deps` vs
1831    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
1832    /// dispatches on — the compiler-checked exhaustiveness on the
1833    /// enum's `match` arms is the build-time guarantee that no future
1834    /// per-list mutation-site regresses to a bare-`bool`-flag
1835    /// (`is_dev: bool`) inline dispatch that a future third
1836    /// dep-list axis (a `:deps-build` build-only closure once the
1837    /// substrate grows cross-artifact heterogeneous dep-graphs, per
1838    /// CAIXA-SDLC §I) would silently split at every consumer.
1839    ///
1840    /// Same "one typed dispatch on the substrate primitive, thin
1841    /// projections at each consumer" discipline the sibling per-slot
1842    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
1843    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
1844    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
1845    /// the substrate's first typed-mutation dispatch on the top-level
1846    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
1847    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
1848    /// diagnostic path routed no through-line back to the typed slot,
1849    /// so a future extension of either dep-list axis to a richer author
1850    /// surface (a per-cluster override the operator pins through a
1851    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
1852    /// roadmap acknowledges, an M4
1853    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
1854    /// admission-webhook that normalized the list at admission time)
1855    /// would have had to be threaded through the `feira add` mutation
1856    /// site in lockstep with every read consumer or one path would
1857    /// silently disagree with the other on which list a given dep lands
1858    /// in. Lifting the resolution rule to a typed method on the
1859    /// substrate primitive means every downstream dep-list-mutating
1860    /// consumer of the top-level manifest reaches for exactly one typed
1861    /// dispatch — the resolver's accept-set migrates as a unit on any
1862    /// future axis addition.
1863    ///
1864    /// # Errors
1865    ///
1866    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
1867    /// when another entry in the same list already carries the same
1868    /// `:nome` — the mutation is refused and the caller can surface the
1869    /// typed diagnostic to the author (the `feira add` verb routes the
1870    /// error through `anyhow::Error::from`, which preserves the
1871    /// canonical `#[error(...)]`-templated diagnostic body).
1872    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
1873        let target = match list {
1874            crate::dep::DepList::Prod => &mut self.deps,
1875            crate::dep::DepList::Dev => &mut self.deps_dev,
1876        };
1877        if target.iter().any(|d| d.nome() == dep.nome()) {
1878            return Err(DepError::DuplicateNome {
1879                nome: dep.nome().to_string(),
1880                list: list.as_str(),
1881            });
1882        }
1883        target.push(dep);
1884        Ok(())
1885    }
1886
1887    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
1888    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
1889    /// composite-reference accessor every consumer of the top-level
1890    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
1891    /// off — returns the author-declared `:limits` typed composite
1892    /// verbatim as an `Option<&LimitsSpec>` reference over the same
1893    /// backing storage the raw `self.limits.as_ref()` field access
1894    /// borrows from, with `None` naming the "no `:limits` block
1895    /// authored — every per-axis Lunatic-sandbox cap defers to the
1896    /// wasm-engine-default arm named on the per-axis
1897    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
1898    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
1899    /// docstrings" partition every downstream Servico-M2-overlay
1900    /// emitter treats as "emit nothing" and the sibling
1901    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
1902    /// treats as "skip the per-axis
1903    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
1904    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
1905    ///
1906    /// The outer `:limits` slot carries the M2 Servico-runtime typed
1907    /// composite — the load-bearing container of every Lunatic-shaped
1908    /// per-process wasm32-sandbox cap axis every long-running wasm
1909    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
1910    /// Lunatic per-process linear-memory / fuel / wall-clock /
1911    /// millicore cap primitives translated onto pleme-io's typed
1912    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
1913    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
1914    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
1915    /// chart both fan on). Every per-`:limits` axis threads through a
1916    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
1917    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
1918    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
1919    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
1920    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
1921    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
1922    /// consumer that reaches for a limits axis first passes through
1923    /// this outer accessor onto the composite and then dispatches
1924    /// onto the per-axis accessor — the two-level dispatch means
1925    /// every per-`:limits` reader now routes through a typed dispatch
1926    /// on the substrate primitive at both altitudes.
1927    ///
1928    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
1929    /// was accessed inline at three production sites — the
1930    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
1931    /// `if let Some(l) = &caixa.limits { … }` traversal head
1932    /// (caixa-core/src/layout.rs:882, which drives the per-axis
1933    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
1934    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
1935    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
1936    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
1937    /// [`LimitsSpec::validate`] fans onto), the
1938    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
1939    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
1940    /// head (caixa-core/src/render.rs:18504, which drives the
1941    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
1942    /// projection every `caixa-helm` / `caixa-flux` Servico values-
1943    /// block emitter fans on), and the
1944    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
1945    /// set enumerator's `self.limits.is_some()` presence probe
1946    /// (caixa-core/src/manifest.rs:1788, which drives the
1947    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
1948    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
1949    /// gate reads) — three open-coded outer-field accesses that
1950    /// expressed no compile-time link back to the typed slot at the
1951    /// [`Caixa`] altitude. A future extension of the `:limits` outer
1952    /// axis to a richer author surface (a multi-`:limits` list the M4
1953    /// CR materializer resolves per-CR at admission time so a Servico
1954    /// can expose a compute-heavy + IO-heavy limits pair, a per-
1955    /// cluster `:limits-overrides` slot the operator pins so a
1956    /// cluster-specific policy can tighten a caixa-declared cap
1957    /// without re-authoring the `caixa.lisp`, a promotion of the
1958    /// plain `Option<LimitsSpec>` to a richer
1959    /// `{static, dynamic}` partition once the wasm-engine's runtime-
1960    /// resolved dynamic-cap surface lands) would have had to be
1961    /// threaded through all three open-coded copies in lockstep or
1962    /// one consumer would silently disagree with the peers on which
1963    /// limits composite a given Caixa resolves to — the layout gate's
1964    /// per-axis bracket-dispatch seed reading the raw slot while the
1965    /// peer `servico_m2_overlay` emitter read an operator-resolved
1966    /// slot would silently split the build-time sandbox-shape gate
1967    /// from the runtime `ComputeUnit` CR emission gate, a three-
1968    /// consumer split at the layout gate, the M2 overlay emitter, and
1969    /// the declared-slot enumerator far from the source `caixa.lisp`
1970    /// with no field naming the limits-drift root cause. Lifting the
1971    /// resolution rule to a typed method on the substrate primitive
1972    /// means every downstream consumer of the caixa's per-`Caixa`
1973    /// Lunatic-sandboxing outer-composite surface reaches for exactly
1974    /// one typed dispatch — the resolver's accept-set migrates as a
1975    /// unit on any future axis addition.
1976    ///
1977    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
1978    /// composite-reference accessor — opens the outer-`Caixa`
1979    /// `Option<&Composite>` composite-reference projection pattern the
1980    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
1981    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
1982    /// [`crate::aplicacao::Placement`] / `:entrada`
1983    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
1984    /// fold on. Peer of the M3 mesh-slot outer-composite family the
1985    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
1986    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
1987    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
1988    /// accessors already close on the outer [`crate::AplicacaoSpec`]
1989    /// altitude — extends that "one typed dispatch on the substrate
1990    /// primitive, thin projections at each consumer" discipline onto
1991    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
1992    /// runtime slot family's outer-composite axis. Returns
1993    /// `Option<&LimitsSpec>` (not the owning composite by copy or
1994    /// clone) because every downstream consumer of the limits
1995    /// composite treats it as a read-only per-axis dispatch source —
1996    /// the reference-view is the narrowest borrow that supports every
1997    /// present + roadmapped consumer (per-axis accessor dispatch,
1998    /// `.is_empty()`-gated overlay projection, presence-probe early
1999    /// return on the "author-omitted `:limits` ⇒ engine-default
2000    /// applies" partition) without cloning the composite through
2001    /// every consumer's fast path. The `Option` half of the return-
2002    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2003    /// engine-default applies" partition (not a default composite the
2004    /// downstream must reject on emptiness) — the accessor projects
2005    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2006    /// reference-return unchanged. Named `limits()` to match the
2007    /// storage field's name verbatim and the tatara-lisp author-
2008    /// surface term (`:limits`) the field's own docstring already
2009    /// carries.
2010    #[must_use]
2011    pub fn limits(&self) -> Option<&LimitsSpec> {
2012        self.limits.as_ref()
2013    }
2014
2015    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2016    /// composite OTP-`gen_server`-shaped callback-table optional-
2017    /// composite-reference accessor every consumer of the top-level
2018    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2019    /// keys off — returns the author-declared `:behavior` typed
2020    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2021    /// the same backing storage the raw `self.behavior.as_ref()` field
2022    /// access borrows from, with `None` naming the "no `:behavior`
2023    /// block authored — every per-callback OTP-shaped hook defers to
2024    /// the wasm-engine's runtime default arm named on the per-axis
2025    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2026    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2027    /// [`BehaviorSpec::on_state_change`] /
2028    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2029    /// partition every downstream Servico-M2-overlay emitter treats as
2030    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2031    /// per-`:behavior` shape gate treats as "skip the per-arm
2032    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2033    /// per-callback on-disk `MissingEntry` existence check".
2034    ///
2035    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2036    /// composite — the load-bearing container of every OTP-shaped
2037    /// per-Servico lifecycle-callback path axis every long-running wasm
2038    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2039    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2040    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2041    /// translated onto pleme-io's typed `:behavior :on-init` /
2042    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2043    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2044    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2045    /// chart both fan on). Every per-`:behavior` axis threads through a
2046    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2047    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2048    /// Every downstream consumer that reaches for a behavior axis
2049    /// first passes through this outer accessor onto the composite
2050    /// and then dispatches onto the per-callback accessor — the
2051    /// two-level dispatch means every per-`:behavior` reader now
2052    /// routes through a typed dispatch on the substrate primitive at
2053    /// both altitudes.
2054    ///
2055    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2056    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2057    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2058    /// keys the "per-version `:state-change` instruction must have a
2059    /// `:on-state-change` callback" precondition off this accessor's
2060    /// composite (the callback-side counterpart to the
2061    /// `:upgrade-from :instructions :state-change :script` refusal at
2062    /// the appup-side). Threading that gate's traversal input through
2063    /// this accessor closes the cross-slot invariant on the substrate
2064    /// primitive, not on the raw field.
2065    ///
2066    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2067    /// composite was accessed inline at four production sites — the
2068    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2069    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2070    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2071    /// `BehaviorError` refusal cascade + the per-callback on-disk
2072    /// [`crate::LayoutError::MissingEntry`] existence check under
2073    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2074    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2075    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2076    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2077    /// drives the `:state-change` ↔ `:on-state-change` precondition
2078    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2079    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2080    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2081    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2082    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2083    /// Servico values-block emitter fans on), and the
2084    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2085    /// set enumerator's `self.behavior.is_some()` presence probe
2086    /// (caixa-core/src/manifest.rs:1919, which drives the
2087    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2088    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2089    /// gate reads) — four open-coded outer-field accesses that
2090    /// expressed no compile-time link back to the typed slot at the
2091    /// [`Caixa`] altitude. A future extension of the `:behavior`
2092    /// outer axis to a richer author surface (a per-callback overlay
2093    /// resolver the operator materializes at admission time so a
2094    /// cluster-specific policy can inject a per-callback tracing
2095    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2096    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2097    /// dynamic}` partition once a runtime-resolved behavior-swap
2098    /// surface lands, the M4 per-callback middleware chain the
2099    /// caixa-operator's per-Servico admission webhook keys off) would
2100    /// have had to be threaded through all four open-coded copies in
2101    /// lockstep or one consumer would silently disagree with the
2102    /// peers on which behavior composite a given Caixa resolves to —
2103    /// the layout gate's per-callback existence-check seed reading
2104    /// the raw slot while the peer `servico_m2_overlay` emitter read
2105    /// an operator-resolved slot would silently split the build-time
2106    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2107    /// gate from the cross-slot `:state-change` composition gate from
2108    /// the M2 declared-slot enumerator, a four-consumer split far
2109    /// from the source `caixa.lisp` with no field naming the
2110    /// behavior-drift root cause. Lifting the resolution rule to a
2111    /// typed method on the substrate primitive means every downstream
2112    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2113    /// composite surface reaches for exactly one typed dispatch — the
2114    /// resolver's accept-set migrates as a unit on any future axis
2115    /// addition.
2116    ///
2117    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2118    /// composite-reference accessor — sibling to the opening
2119    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2120    /// `Option<&Composite>` composite-reference sub-family, extends
2121    /// the "one typed dispatch on the substrate primitive, thin
2122    /// projections at each consumer" discipline onto the second of
2123    /// the three M2 Servico-runtime slots. The remaining
2124    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2125    /// altitude — the M3 mesh-slot family (`:politicas`,
2126    /// `:placement`, `:entrada` — already closed on the inner
2127    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2128    /// d32111c) — remain the future sibling lifts on the outer
2129    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2130    /// the owning composite by copy or clone) because every
2131    /// downstream consumer of the behavior composite treats it as a
2132    /// read-only per-callback dispatch source — the reference-view is
2133    /// the narrowest borrow that supports every present + roadmapped
2134    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2135    /// overlay projection, presence-probe early return on the
2136    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2137    /// partition, cross-slot `:state-change` composition input)
2138    /// without cloning the composite through every consumer's fast
2139    /// path. The `Option` half of the return-type preserves the
2140    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2141    /// applies" partition (not a default composite the downstream
2142    /// must reject on emptiness) — the accessor projects the raw
2143    /// `Option<BehaviorSpec>` slot's presence bit through the
2144    /// reference-return unchanged. Named `behavior()` to match the
2145    /// storage field's name verbatim and the tatara-lisp author-
2146    /// surface term (`:behavior`) the field's own docstring already
2147    /// carries.
2148    #[must_use]
2149    pub fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2150        self.behavior.as_ref()
2151    }
2152
2153    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2154    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2155    /// reference accessor every consumer of the top-level manifest's
2156    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2157    /// reader keys off — returns the author-declared `:politicas` typed
2158    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2159    /// same backing storage the raw `self.politicas.as_ref()` field
2160    /// access borrows from, with `None` naming the "no `:politicas`
2161    /// block authored — every per-axis mesh-policy scalar defers to the
2162    /// cluster-default arm named on the per-axis
2163    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2164    /// [`crate::aplicacao::MeshPolicy::retries`] /
2165    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2166    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2167    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2168    /// docstrings" partition every downstream caixa-mesh /
2169    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2170    /// "emit no per-`:politicas` overlay" and the sibling
2171    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2172    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2173    /// arm.
2174    ///
2175    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2176    /// Aplicacao typed composite — the load-bearing container of every
2177    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2178    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2179    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2180    /// composite; §V — the "no infinite blocking" per-call deadline +
2181    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2182    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2183    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2184    /// threads through a lifted per-slot accessor on the
2185    /// [`crate::aplicacao::MeshPolicy`] type: the
2186    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2187    /// mTLS-enforcement toggle, the
2188    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2189    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2190    /// (7073d0f) Gateway-API per-call deadline, the
2191    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2192    /// Envoy-outlier-detection composite. Every downstream consumer
2193    /// that reaches for a mesh-policy axis first passes through this
2194    /// outer accessor onto the composite and then dispatches onto the
2195    /// per-axis accessor — the two-level dispatch means every per-
2196    /// `:politicas` reader now routes through a typed dispatch on the
2197    /// substrate primitive at both altitudes.
2198    ///
2199    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2200    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2201    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2202    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2203    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2204    /// composite whether or not the author declared the outer slot.
2205    /// The outer accessor preserves the "author-omitted vs authored-
2206    /// empty" partition the inner accessor's `is_empty()`-gated
2207    /// renderer overlay collapses — routing the presence bit through
2208    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2209    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2210    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2211    ///
2212    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2213    /// composite was accessed inline at two production sites — the
2214    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2215    /// `self.politicas.clone().unwrap_or_default()` traversal head
2216    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2217    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2218    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2219    /// then observes), and the [`Self::declared_mesh_slots`] M3
2220    /// declared-slot-set enumerator's `self.politicas.is_some()`
2221    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2222    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2223    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2224    /// coherence gate reads) — two open-coded outer-field accesses
2225    /// that expressed no compile-time link back to the typed slot at
2226    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2227    /// outer axis to a richer author surface (a per-cluster
2228    /// `:politicas-overrides` slot the operator materializes at
2229    /// admission time so a cluster-specific policy can tighten the
2230    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2231    /// promotion of the plain `Option<MeshPolicy>` to a richer
2232    /// `{static, dynamic}` partition once the M4 per-edge
2233    /// contrato-scoped policy-override surface lands, the M5 traffic-
2234    /// shaping composition the caixa-operator's per-Aplicacao mesh
2235    /// admission webhook keys off) would have had to be threaded
2236    /// through both open-coded copies in lockstep or the Aplicacao-
2237    /// composition seed's default-fold arm would silently disagree
2238    /// with the M3 declared-slot enumerator on which policy composite
2239    /// a given Caixa resolves to — the seed reading an operator-
2240    /// resolved slot while the enumerator's presence probe read the
2241    /// raw slot would silently split the build-time mesh-artifact
2242    /// emission gate from the M3 declared-slot enumerator's kind-
2243    /// coherence gate, a two-consumer split far from the source
2244    /// `caixa.lisp` with no field naming the policy-drift root cause.
2245    /// Lifting the resolution rule to a typed method on the substrate
2246    /// primitive means every downstream consumer of the caixa's per-
2247    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2248    /// reaches for exactly one typed dispatch — the resolver's
2249    /// accept-set migrates as a unit on any future axis addition.
2250    ///
2251    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2252    /// composite-reference accessor — sibling to the opening
2253    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2254    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2255    /// reference sub-family, extends the "one typed dispatch on the
2256    /// substrate primitive, thin projections at each consumer"
2257    /// discipline onto the first of the three M3 mesh-slot axes.
2258    /// Peer of the closed inner mesh-slot outer-composite family the
2259    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2260    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2261    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2262    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2263    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2264    /// mesh-slot arm of the composite-reference family the remaining
2265    /// two axes (`:placement`, `:entrada`) fold onto in future
2266    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2267    /// composite by copy or clone) because every downstream consumer
2268    /// of the mesh-policy composite treats it as a read-only per-axis
2269    /// dispatch source — the reference-view is the narrowest borrow
2270    /// that supports every present + roadmapped consumer (per-axis
2271    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2272    /// presence-probe early return on the "author-omitted `:politicas`
2273    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2274    /// seed's default-fold arm) without cloning the composite through
2275    /// every consumer's fast path. The `Option` half of the return-
2276    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2277    /// cluster-default applies" partition (not a default composite
2278    /// the downstream must reject on emptiness) — the accessor
2279    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2280    /// through the reference-return unchanged. Named `politicas()` to
2281    /// match the storage field's name verbatim and the tatara-lisp
2282    /// author-surface term (`:politicas`) the field's own docstring
2283    /// already carries.
2284    #[must_use]
2285    pub fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2286        self.politicas.as_ref()
2287    }
2288
2289    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2290    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2291    /// reference accessor every consumer of the top-level manifest's
2292    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2293    /// reader keys off — returns the author-declared `:placement` typed
2294    /// composite verbatim as an `Option<&Placement>` reference over the
2295    /// same backing storage the raw `self.placement.as_ref()` field
2296    /// access borrows from, with `None` naming the "no `:placement`
2297    /// block authored — every per-axis placement scalar defers to the
2298    /// cluster-default arm named on the per-axis
2299    /// [`crate::aplicacao::Placement::estrategia`] /
2300    /// [`crate::aplicacao::Placement::clusters`] /
2301    /// [`crate::aplicacao::Placement::affinity`] /
2302    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2303    /// docstrings" partition every downstream caixa-mesh /
2304    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2305    /// "emit no per-`:placement` overlay" and the sibling
2306    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2307    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2308    ///
2309    /// The outer `:placement` slot carries the M3 mesh-slot per-
2310    /// Aplicacao typed distribution composite — the load-bearing
2311    /// container of every where-does-this-Aplicacao-run axis every
2312    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2313    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2314    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2315    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2316    /// Aplicacao's typed distribution composite; §V CSE invariants —
2317    /// "distribution is a first-class typed composite, not a runtime
2318    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2319    /// typed inter-Servico contrato-edge overlay the per-cluster
2320    /// mesh renderer keys off). Every per-`:placement` axis threads
2321    /// through a lifted per-slot accessor on the
2322    /// [`crate::aplicacao::Placement`] type: the
2323    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2324    /// MESH-COMPOSITION distribution-strategy scalar, the
2325    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2326    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2327    /// M3-Adaptive-compression-hint optional-scalar, and the
2328    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2329    /// sharding extractor-expression optional-scalar. Every downstream
2330    /// consumer that reaches for a placement axis first passes through
2331    /// this outer accessor onto the composite and then dispatches onto
2332    /// the per-axis accessor — the two-level dispatch means every per-
2333    /// `:placement` reader now routes through a typed dispatch on the
2334    /// substrate primitive at both altitudes.
2335    ///
2336    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2337    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2338    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2339    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2340    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2341    /// whether or not the author declared the outer slot. The outer
2342    /// accessor preserves the "author-omitted vs authored-empty" partition
2343    /// the inner accessor collapses at the cluster-default fold —
2344    /// routing the presence bit through this accessor keeps the
2345    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2346    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2347    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2348    /// dispatch.
2349    ///
2350    /// Prior to this lift the `.placement` `Option<Placement>`
2351    /// composite was accessed inline at two production sites — the
2352    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2353    /// `self.placement.clone().unwrap_or_default()` traversal head
2354    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2355    /// the [`crate::aplicacao::Placement::default`] cluster-default
2356    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2357    /// then observes), and the [`Self::declared_mesh_slots`] M3
2358    /// declared-slot-set enumerator's `self.placement.is_some()`
2359    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2360    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2361    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2362    /// coherence gate reads) — two open-coded outer-field accesses
2363    /// that expressed no compile-time link back to the typed slot at
2364    /// the [`Caixa`] altitude. A future extension of the `:placement`
2365    /// outer axis to a richer author surface (a per-cluster
2366    /// `:placement-overrides` slot the operator materializes at
2367    /// admission time so a cluster-specific placement can tighten the
2368    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2369    /// per-tenant placement-alias table the M4
2370    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2371    /// per-CR at admission time, a promotion of the plain
2372    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2373    /// once Orleans-style virtual-actor dynamic placement comes into
2374    /// typed scope) would have had to be threaded through both open-
2375    /// coded copies in lockstep or the Aplicacao-composition seed's
2376    /// default-fold arm would silently disagree with the M3 declared-
2377    /// slot enumerator on which distribution composite a given Caixa
2378    /// resolves to — the seed reading an operator-resolved slot while
2379    /// the enumerator's presence probe read the raw slot would
2380    /// silently split the build-time distribution-artifact emission
2381    /// gate from the M3 declared-slot enumerator's kind-coherence
2382    /// gate, a two-consumer split far from the source `caixa.lisp`
2383    /// with no field naming the distribution-drift root cause.
2384    /// Lifting the resolution rule to a typed method on the substrate
2385    /// primitive means every downstream consumer of the caixa's per-
2386    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2387    /// reaches for exactly one typed dispatch — the resolver's
2388    /// accept-set migrates as a unit on any future axis addition.
2389    ///
2390    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2391    /// composite-reference accessor — sibling to the opening
2392    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2393    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2394    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2395    /// composite-reference sub-family, folds on the "one typed
2396    /// dispatch on the substrate primitive, thin projections at each
2397    /// consumer" discipline extended onto the second of the three M3
2398    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2399    /// composite family the sibling
2400    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2401    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2402    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2403    /// accessor pins already close on the inner
2404    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2405    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2406    /// [`Self::politicas`] opened, extending the discipline onto the
2407    /// second of the three M3 mesh-slot axes. The remaining M3
2408    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2409    /// discipline in the final sibling lift, closing the outer top-
2410    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2411    /// Returns `Option<&Placement>` (not the owning composite by copy
2412    /// or clone) because every downstream consumer of the placement
2413    /// composite treats it as a read-only per-axis dispatch source —
2414    /// the reference-view is the narrowest borrow that supports every
2415    /// present + roadmapped consumer (per-axis accessor dispatch,
2416    /// serde composite-serialization on the programs.yaml overlay,
2417    /// presence-probe early return on the "author-omitted `:placement`
2418    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2419    /// seed's default-fold arm) without cloning the composite through
2420    /// every consumer's fast path. The `Option` half of the return-
2421    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2422    /// cluster-default applies" partition (not a default composite
2423    /// the downstream must reject on emptiness) — the accessor
2424    /// projects the raw `Option<Placement>` slot's presence bit
2425    /// through the reference-return unchanged. Named `placement()` to
2426    /// match the storage field's name verbatim and the tatara-lisp
2427    /// author-surface term (`:placement`) the field's own docstring
2428    /// already carries.
2429    #[must_use]
2430    pub fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2431        self.placement.as_ref()
2432    }
2433
2434    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2435    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2436    /// composite-reference accessor every consumer of the top-level
2437    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2438    /// composite reader keys off — returns the author-declared
2439    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2440    /// reference over the same backing storage the raw
2441    /// `self.entrada.as_ref()` field access borrows from, with `None`
2442    /// naming the "no `:entrada` block authored — this Aplicacao is
2443    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2444    /// partition every downstream caixa-mesh Gateway-API artifact
2445    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2446    /// backend for this Aplicacao" and the sibling
2447    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2448    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2449    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2450    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2451    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2452    /// the same `Option<&Entrada>` presence bit unchanged).
2453    ///
2454    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2455    /// Aplicacao typed external-gateway composite — the load-bearing
2456    /// container of every how-does-the-outside-world-reach-this-
2457    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2458    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2459    /// external-entry composite; §V CSE invariants — "the external
2460    /// gateway is a first-class typed composite, not a per-Servico
2461    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2462    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2463    /// API renderer keys off). Every per-`:entrada` axis threads
2464    /// through a lifted per-slot accessor on the
2465    /// [`crate::aplicacao::Entrada`] type: the
2466    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2467    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2468    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2469    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2470    /// backend `trigger.service.port` scalar, and the
2471    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2472    /// resolver every HTTPRoute-aware renderer consumes. Every
2473    /// downstream consumer that reaches for an entry axis first passes
2474    /// through this outer accessor onto the composite and then
2475    /// dispatches onto the per-axis accessor — the two-level dispatch
2476    /// means every per-`:entrada` reader now routes through a typed
2477    /// dispatch on the substrate primitive at both altitudes.
2478    ///
2479    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2480    /// seed: the Aplicacao-view builder forwards the outer `Option`
2481    /// arm verbatim (no default fold — `:entrada` is inherently
2482    /// optional; a cluster-internal Aplicacao has no external gateway
2483    /// at all, not "an external gateway that defaults to nothing"), so
2484    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2485    /// `Option<&Entrada>`-return accessor observes the same presence
2486    /// bit whether or not the author declared the outer slot. Routing
2487    /// the presence bit through this accessor keeps the
2488    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2489    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2490    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2491    /// hostname/backend/path emission dispatch.
2492    ///
2493    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2494    /// was accessed inline at two production sites — the
2495    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2496    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2497    /// which drives the forward onto the peer inner
2498    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2499    /// Gateway-API fan-out then observes), and the
2500    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2501    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2502    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2503    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2504    /// kind-coherence gate reads) — two open-coded outer-field
2505    /// accesses that expressed no compile-time link back to the typed
2506    /// slot at the [`Caixa`] altitude. A future extension of the
2507    /// `:entrada` outer axis to a richer author surface (a per-cluster
2508    /// `:entrada-overrides` slot the operator materializes at admission
2509    /// time so a cluster-specific hostname can pin the caixa-declared
2510    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2511    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2512    /// CR materializer resolves per-CR at admission time, a promotion
2513    /// of the plain `Option<Entrada>` to a richer
2514    /// `{public, private, internal}` partition once Cilium-identity-
2515    /// scoped internal gateways come into typed scope) would have had
2516    /// to be threaded through both open-coded copies in lockstep or the
2517    /// Aplicacao-composition seed's forward arm would silently
2518    /// disagree with the M3 declared-slot enumerator on which external-
2519    /// gateway composite a given Caixa resolves to — the seed reading
2520    /// an operator-resolved slot while the enumerator's presence probe
2521    /// read the raw slot would silently split the build-time gateway-
2522    /// artifact emission gate from the M3 declared-slot enumerator's
2523    /// kind-coherence gate, a two-consumer split far from the source
2524    /// `caixa.lisp` with no field naming the entry-drift root cause.
2525    /// Lifting the resolution rule to a typed method on the substrate
2526    /// primitive means every downstream consumer of the caixa's per-
2527    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2528    /// surface reaches for exactly one typed dispatch — the resolver's
2529    /// accept-set migrates as a unit on any future axis addition.
2530    ///
2531    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2532    /// return composite-reference accessor — closes the outer-`Caixa`
2533    /// `Option<&Composite>` composite-reference sub-family opened by
2534    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2535    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2536    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2537    /// folds on the "one typed dispatch on the substrate primitive,
2538    /// thin projections at each consumer" discipline extended onto the
2539    /// third and final M3 mesh-slot axis. Peer of the closed inner
2540    /// mesh-slot outer-composite family the sibling
2541    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2542    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2543    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2544    /// accessor pins already close on the inner
2545    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2546    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2547    /// altitudes of the outer-composite reference-return discipline
2548    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2549    /// slot presence) now carry the full five-arm accept-set behind a
2550    /// typed dispatch on the substrate primitive. Returns
2551    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2552    /// because every downstream consumer of the entrada composite
2553    /// treats it as a read-only per-axis dispatch source — the
2554    /// reference-view is the narrowest borrow that supports every
2555    /// present + roadmapped consumer (per-axis accessor dispatch,
2556    /// serde composite-serialization on the programs.yaml overlay,
2557    /// presence-probe early return on the "author-omitted `:entrada`
2558    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2559    /// seed's forward arm) without cloning the composite through every
2560    /// consumer's fast path. The `Option` half of the return-type
2561    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2562    /// cluster-internal Aplicacao" partition (not a default composite
2563    /// the downstream must reject on emptiness — a cluster-internal
2564    /// Aplicacao has no external gateway at all, not "a default gateway
2565    /// that emits nothing"); the accessor projects the raw
2566    /// `Option<Entrada>` slot's presence bit through the reference-
2567    /// return unchanged. Named `entrada()` to match the storage field's
2568    /// name verbatim and the tatara-lisp author-surface term
2569    /// (`:entrada`) the field's own docstring already carries.
2570    #[must_use]
2571    pub fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
2572        self.entrada.as_ref()
2573    }
2574
2575    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
2576    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
2577    /// an `Option<&CiRun>`, borrowed from the typed slot's own
2578    /// `Option<CiRun>` storage. `None` when the slot is absent (every
2579    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
2580    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
2581    /// not silently accepted).
2582    ///
2583    /// Named `ci()` to match the storage field's name and the
2584    /// tatara-lisp author surface (`:ci`); mirrors the sibling
2585    /// `Option<&Composite>` accessors on this same `Caixa` altitude
2586    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
2587    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
2588    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
2589    /// at every consumer.
2590    #[must_use]
2591    pub fn ci(&self) -> Option<&canteiro_types::CiRun> {
2592        self.ci.as_ref()
2593    }
2594
2595    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
2596    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
2597    /// accessor every consumer of the top-level manifest's per-Supervisor
2598    /// restart-strategy axis keys off — returns the author-declared
2599    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
2600    /// `Copy`-projected from the typed slot's own
2601    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
2602    /// (`:estrategia` is a flat-spread supervisor-only slot every
2603    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
2604    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2605    /// still omit to defer to [`RestartStrategy::default`] —
2606    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
2607    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
2608    /// [`SupervisorSpec::default`]-inherited strategy without any silent
2609    /// promotion to a fresh explicit variant at the accessor boundary).
2610    ///
2611    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
2612    /// restart-strategy discriminant every substrate-side per-Supervisor
2613    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
2614    /// closed-set `one_for_one | one_for_all | rest_for_one |
2615    /// simple_one_for_one` algebra translated onto pleme-io's typed
2616    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
2617    /// slot algebra the operator's hierarchical reconciliation scheduler
2618    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
2619    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
2620    /// supervisor slots are flat on Caixa (vs nested under a
2621    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
2622    /// level of nesting"), so the accessor's altitude is the outer
2623    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
2624    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
2625    /// (eafb619) accessor keys off. The two typed axes — the outer
2626    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
2627    /// (author-omitted arm carried as `None`) and the inner post-
2628    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
2629    /// (`Option` collapsed through the [`Self::supervisor_view`]
2630    /// `unwrap_or_default()` fold) — now share one accessor discipline for
2631    /// the shared substrate concept "the author-declared OTP-shaped
2632    /// sibling-restart-strategy variant that partitions the downstream
2633    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
2634    /// `None` arm is the pre-composition presence bit every declared-slot
2635    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
2636    /// inner-altitude non-`Option` `RestartStrategy` is the post-
2637    /// composition partition-dispatch input every strategy-arm consumer
2638    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
2639    /// Supervisor sibling-restart branch, the future M4
2640    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
2641    /// webhook) fans on.
2642    ///
2643    /// Prior to this lift the `.estrategia` field was accessed inline at
2644    /// two production sites in `caixa-core/src/manifest.rs` — the
2645    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
2646    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
2647    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2648    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
2649    /// `SupervisorSpec` construction site at `estrategia:
2650    /// self.estrategia.unwrap_or_default()` (which composes the flat-
2651    /// spread outer author-surface `Option<RestartStrategy>` onto the
2652    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
2653    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
2654    /// coded field-accesses that expressed no compile-time link back to
2655    /// the typed slot. A future extension of the outer `:estrategia` axis
2656    /// to a richer author surface (a per-cluster strategy override the
2657    /// operator pins through a future `:estrategia-overrides` overlay the
2658    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
2659    /// a per-tenant strategy-alias table the M4 CR materializer resolves
2660    /// per-CR, a per-Supervisor dynamic strategy derivation the future
2661    /// adaptive-supervision engine computes from child-failure-history
2662    /// topology, a per-child-cohort strategy split the future
2663    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
2664    /// absorption roadmap acknowledges, a promotion of the plain
2665    /// `Option<RestartStrategy>` to a richer
2666    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
2667    /// operator-resolved overlay lands) would have had to be threaded
2668    /// through both open-coded copies in lockstep or the enumerator's
2669    /// presence probe and the composition site's `unwrap_or_default()`
2670    /// fold would silently disagree on which strategy a given [`Caixa`]
2671    /// resolves to (an author's `:estrategia OneForAll` would satisfy
2672    /// the enumerator's presence probe while the composition site
2673    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
2674    /// the resolution rule to a typed method on the substrate primitive
2675    /// means every downstream consumer of the caixa's per-`Caixa` outer-
2676    /// altitude sibling-restart-strategy surface reaches for exactly one
2677    /// typed dispatch — the resolver's accept-set migrates as a unit on
2678    /// any future axis addition.
2679    ///
2680    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2681    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2682    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
2683    /// projection pattern the sibling per-`Caixa` `:max-restarts`
2684    /// `Option<u32>` and (through the future duration-newtype landing)
2685    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
2686    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
2687    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
2688    /// the post-composition [`SupervisorSpec`] altitude — same "one
2689    /// typed dispatch on the substrate primitive, thin projections at
2690    /// each consumer" discipline extended onto the pre-composition outer
2691    /// author-surface [`Caixa`] altitude for the same OTP-shaped
2692    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
2693    /// `Option<&Composite>` composite-reference family the sibling
2694    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
2695    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
2696    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
2697    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
2698    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
2699    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
2700    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2701    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
2702    /// pins on the inner-altitude per-`:placement` composite. Named
2703    /// `estrategia()` to match the storage field's name and the
2704    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
2705    /// / per-[`crate::aplicacao::Placement`] peer
2706    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
2707    /// verbatim; the accessor's identity name maps onto the canonical
2708    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
2709    /// docstring already carries.
2710    #[must_use]
2711    pub fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
2712        self.estrategia
2713    }
2714
2715    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
2716    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
2717    /// scalar accessor every consumer of the top-level manifest's per-
2718    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
2719    /// returns the author-declared `:max-restarts` typed `Option<u32>`
2720    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
2721    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
2722    /// accessor returns by value; no borrow of `&self` past the call).
2723    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
2724    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
2725    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2726    /// still omit to defer to the [`Self::supervisor_view`]
2727    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
2728    ///
2729    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
2730    /// `MaxIntensity` restart-budget count that pairs with the sibling
2731    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
2732    /// restart-intensity ratio the supervisor trips its own escalation on
2733    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
2734    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
2735    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
2736    /// reconciliation scheduler fans on). The slot is *flat-spread* on
2737    /// the outer top-level `Caixa` (per the field-shape docstring at
2738    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
2739    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
2740    /// accessor's altitude is the outer [`Caixa`] surface rather than the
2741    /// composed [`SupervisorSpec`] altitude the sibling
2742    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
2743    /// off. The two typed axes — the outer author-surface `Option<u32>`
2744    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
2745    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
2746    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
2747    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
2748    /// shared substrate concept "the author-declared OTP-shaped
2749    /// restart-budget count every downstream per-Supervisor consumer's
2750    /// restart-intensity budget-vs-count comparator fans on".
2751    ///
2752    /// Prior to this lift the `.max_restarts` field was accessed inline
2753    /// at two production sites in `caixa-core/src/manifest.rs` — the
2754    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
2755    /// presence-probe arm at `if self.max_restarts.is_some()` (which
2756    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
2757    /// kind-coherence gate's per-slot label push) and the
2758    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
2759    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
2760    /// flat-spread outer author-surface `Option<u32>` onto the inner
2761    /// post-composition [`SupervisorSpec`] `u32` field the
2762    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
2763    /// coded field-accesses that expressed no compile-time link back to
2764    /// the typed slot. A future extension of the outer `:max-restarts`
2765    /// axis to a richer author surface (a per-cluster restart-budget
2766    /// override the operator pins through a future `:max-restarts-overrides`
2767    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
2768    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
2769    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
2770    /// budget derivation the future adaptive-supervision engine computes
2771    /// from child-failure-history topology, a promotion of the plain
2772    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
2773    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
2774    /// per-child-cohort roadmap lands) would have had to be threaded
2775    /// through both open-coded copies in lockstep or the enumerator's
2776    /// presence probe and the composition site's `unwrap_or(5)` fold
2777    /// would silently disagree on which restart-budget a given [`Caixa`]
2778    /// resolves to (an author's `:max-restarts 10` would satisfy the
2779    /// enumerator's presence probe while the composition site silently
2780    /// composed the OTP-canonical `5`, or vice versa). Lifting the
2781    /// resolution rule to a typed method on the substrate primitive means
2782    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
2783    /// restart-budget-count surface reaches for exactly one typed dispatch
2784    /// — the resolver's accept-set migrates as a unit on any future axis
2785    /// addition.
2786    ///
2787    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2788    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2789    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
2790    /// projection pattern the sibling per-`Caixa`
2791    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
2792    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
2793    /// Peer of the inner-altitude
2794    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
2795    /// on the post-composition [`SupervisorSpec`] altitude — same "one
2796    /// typed dispatch on the substrate primitive, thin projections at
2797    /// each consumer" discipline extended onto the pre-composition outer
2798    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
2799    /// shaped restart-budget-count axis. Named `max_restarts()` to match
2800    /// the storage field's name and the per-[`SupervisorSpec`] peer
2801    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
2802    /// discipline verbatim; the accessor's identity maps onto the
2803    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
2804    /// field's docstring already carries.
2805    #[must_use]
2806    pub const fn max_restarts(&self) -> Option<u32> {
2807        self.max_restarts
2808    }
2809
2810    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
2811    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
2812    /// denominator raw-duration-string scalar accessor every consumer of
2813    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
2814    /// window axis keys off — returns the author-declared `:restart-window`
2815    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
2816    /// from the typed slot's own `Option<String>` storage. `None` when
2817    /// the slot is absent (the canonical "never reset — every restart
2818    /// across the supervisor's lifetime counts against the sibling
2819    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
2820    /// `defcaixa` carries by `#[serde(default)]` and every
2821    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
2822    /// [`Self::supervisor_view`] `restart_window: None` composition
2823    /// through the [`crate::supervisor::duration_codec::parse`] soft-
2824    /// swallow `.and_then(|s| … .ok())` fold).
2825    ///
2826    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
2827    /// shaped `Period` sliding-observation-interval duration string that
2828    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
2829    /// budget count to form the `MaxIntensity / Period` restart-intensity
2830    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
2831    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
2832    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
2833    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
2834    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
2835    /// holds an `Option<Duration>` routed through the shared
2836    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
2837    /// — so the outer altitude's accessor returns `Option<&str>` (raw
2838    /// authoring surface) while the inner altitude's
2839    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
2840    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
2841    /// is closed by the sibling [`Self::validate_restart_window`] gate
2842    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
2843    /// the offending value; the view-construction path
2844    /// [`Self::supervisor_view`] soft-swallows the same parse error to
2845    /// `None` to keep the view best-effort.
2846    ///
2847    /// Prior to this lift the `.restart_window` field was accessed inline
2848    /// at three production sites in `caixa-core/src/manifest.rs` — the
2849    /// [`Self::declared_supervisor_slots`]
2850    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
2851    /// `if self.restart_window.is_some()` (which drives the
2852    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2853    /// coherence gate's per-slot label push), the
2854    /// [`Self::validate_restart_window`] `let Some(s) =
2855    /// self.restart_window.as_deref()` empty-and-shape gate binding
2856    /// (which folds the raw string through the shared
2857    /// [`crate::supervisor::duration_codec::parse`] to surface
2858    /// [`ManifestError::RestartWindowMalformed`] naming the offending
2859    /// value), and the [`Self::supervisor_view`] `self.restart_window
2860    /// .as_deref().and_then(…)` view-construction fold (which composes
2861    /// the flat-spread outer author-surface `Option<String>` onto the
2862    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
2863    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
2864    /// three open-coded field-accesses that expressed no compile-time
2865    /// link back to the typed slot. A future extension of the outer
2866    /// `:restart-window` axis to a richer author surface (a per-cluster
2867    /// window override, a per-tenant window-alias table, a per-Supervisor
2868    /// dynamic window derivation the future adaptive-supervision engine
2869    /// computes from child-failure-history topology, a promotion of the
2870    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
2871    /// once the future author-surface parser lands at the [`Caixa`]
2872    /// altitude and the raw-string form is retired) would have had to be
2873    /// threaded through every open-coded copy in lockstep or the three
2874    /// consumers would silently disagree on which raw string a given
2875    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
2876    /// method on the substrate primitive means every downstream consumer
2877    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
2878    /// string surface reaches for exactly one typed dispatch — the
2879    /// resolver's accept-set migrates as a unit on any future axis
2880    /// addition.
2881    ///
2882    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
2883    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
2884    /// spread projection pattern the sibling per-`Caixa`
2885    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
2886    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
2887    /// the sub-family onto the sibling `Option<&str>` raw-duration-
2888    /// string arm (the outer altitude's raw-string form; the inner
2889    /// altitude's parsed [`Duration`] form is the peer
2890    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
2891    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
2892    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
2893    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
2894    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
2895    /// sub-family already carries — same "one typed dispatch on the
2896    /// substrate primitive, thin projections at each consumer"
2897    /// discipline extended onto the M2 supervisor-tree flat-spread
2898    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
2899    /// to match the storage field's name and the per-[`SupervisorSpec`]
2900    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
2901    /// method-name discipline verbatim; the accessor's identity maps
2902    /// onto the canonical OTP-shape supervision vocabulary the
2903    /// `:restart-window` field's docstring already carries.
2904    #[must_use]
2905    pub fn restart_window(&self) -> Option<&str> {
2906        self.restart_window.as_deref()
2907    }
2908
2909    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
2910    /// outer-composite OTP-appup-shaped per-prior-version migration-
2911    /// entry-list slice accessor every consumer of the top-level
2912    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
2913    /// slice-view keys off — returns the author-declared `:upgrade-from`
2914    /// typed `Vec<UpgradeFromEntry>` verbatim as a
2915    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
2916    /// the raw `self.upgrade_from.as_slice()` field access borrows
2917    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
2918    /// arm every `defcaixa` without an `:upgrade-from` block carries;
2919    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
2920    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
2921    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
2922    /// possibly empty — and the returned `&[UpgradeFromEntry]`
2923    /// degenerates to an empty slice on that arm without any silent
2924    /// `None` collapse).
2925    ///
2926    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
2927    /// migration block — the load-bearing container of every per-
2928    /// prior-`:versao` migration-instruction list the wasm-operator
2929    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
2930    /// `.appup` per-prior-version `LoadModule | StateChange |
2931    /// SoftPurge | Purge | Restart` instruction algebra translated
2932    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
2933    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
2934    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
2935    /// threads through a lifted per-entry accessor on the
2936    /// [`UpgradeFromEntry`] type: the
2937    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
2938    /// version scalar accessor and the
2939    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
2940    /// return per-entry instruction-list accessor (0137e5a). Every
2941    /// downstream consumer of the hot-upgrade path first passes
2942    /// through this outer accessor onto the slice and then dispatches
2943    /// per-entry through the inner accessors — the two-level dispatch
2944    /// means every per-`:upgrade-from` reader now routes through a
2945    /// typed dispatch on the substrate primitive at both altitudes.
2946    ///
2947    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
2948    /// slot was accessed inline at production sites across three
2949    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
2950    /// enumerator's `self.upgrade_from.is_empty()` presence probe
2951    /// (caixa-core/src/manifest.rs, which drives the
2952    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
2953    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2954    /// gate reads), the [`crate::StandardLayout::verify`] per-
2955    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
2956    /// layout.rs, which fans onto the
2957    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
2958    /// cross-entry duplicate gate, the
2959    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
2960    /// SemVer-precedence cross-slot gate, the
2961    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2962    /// `:state-change` ↔ `:on-state-change` cross-slot composition
2963    /// gate, and the per-instruction script-path existence-probe walk
2964    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
2965    /// resolve every declared migration script against the layout
2966    /// root), and the [`crate::render::servico_m2_overlay`] per-
2967    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
2968    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
2969    /// projection (caixa-core/src/render.rs, which drives the
2970    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
2971    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
2972    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
2973    /// A future extension of the outer `:upgrade-from` axis (a per-
2974    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
2975    /// resolves at admission time so a cluster-specific migration
2976    /// policy can tighten a caixa-declared step without re-authoring
2977    /// the `caixa.lisp`, promotion of the plain
2978    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
2979    /// partition once runtime-resolved hot-upgrade instructions land,
2980    /// per-entry priority annotation once multi-strategy fan-out
2981    /// lands) would have had to be threaded through all six open-
2982    /// coded copies in lockstep or one consumer would silently
2983    /// disagree with the peers on which upgrade slice a given Caixa
2984    /// resolves to — a six-consumer split at the enumerator, the
2985    /// three-stage validate pass, the script-path probe walk, and the
2986    /// M2 overlay emitter, far from the source `caixa.lisp` with no
2987    /// field naming the upgrade-drift root cause. Lifting the
2988    /// resolution rule to a typed method on the substrate primitive
2989    /// means every downstream consumer of the caixa's per-`Caixa`
2990    /// OTP-appup outer-slice surface reaches for exactly one typed
2991    /// dispatch — the resolver's accept-set migrates as a unit on any
2992    /// future axis addition.
2993    ///
2994    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
2995    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
2996    /// outer-`Caixa` `&[Composite]` composite-slice projection
2997    /// pattern the sibling `:children`
2998    /// [`crate::supervisor::ChildSpec`] / `:membros`
2999    /// [`crate::aplicacao::Membro`] / `:contratos`
3000    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3001    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3002    /// `Option<&Composite>` composite-reference family the sibling
3003    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3004    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3005    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3006    /// `Option<&Composite>` altitude, extended here to the outer-
3007    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3008    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3009    /// (0137e5a) — same "one typed dispatch on the substrate
3010    /// primitive, thin projections at each consumer" discipline
3011    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3012    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3013    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3014    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3015    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3016    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3017    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3018    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3019    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3020    /// slice" projection pattern onto the sibling M2 typed-composite-
3021    /// element axis (`UpgradeFromEntry` composite, matching the
3022    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3023    /// different altitude).
3024    ///
3025    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3026    /// because every downstream consumer of the hot-upgrade list
3027    /// treats it as a read-only sequence — the slice-view is the
3028    /// narrowest borrow that supports every present + roadmapped
3029    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3030    /// serialization through
3031    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3032    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3033    /// the typed view reaches for (the storage-side `Vec` remains
3034    /// reachable through the `pub upgrade_from` field for the
3035    /// mutation-carrying serde round-trip and per-test fixture-
3036    /// mutation paths). Named `upgrade_from()` to match the storage
3037    /// field's `snake_case` name; the kebab-case author-surface tag
3038    /// `:upgrade-from` is the same axis after tatara-lisp's
3039    /// kebab↔snake fold and the accessor's identity maps onto the
3040    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3041    /// already carries.
3042    #[must_use]
3043    pub fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3044        self.upgrade_from.as_slice()
3045    }
3046
3047    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3048    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3049    /// slice accessor every consumer of the top-level manifest's per-
3050    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3051    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3052    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3053    /// the same backing buffer the raw `self.children.as_slice()` field
3054    /// access borrows from. Empty-slice-carrying (the "no static children
3055    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3056    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3057    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3058    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3059    /// on those arms without any silent `None` collapse).
3060    ///
3061    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3062    /// static-child list — the load-bearing container of every per-
3063    /// child `{caixa, versao, restart}` triple the wasm-operator's
3064    /// hierarchical reconciler dispatches on at supervisor-tree
3065    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3066    /// static-child list translated onto pleme-io's typed
3067    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3068    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3069    /// dispatch fans on). Every per-child axis threads through a lifted
3070    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3071    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3072    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3073    /// version-requirement scalar accessor, and the
3074    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3075    /// per-child post-exit restart-decision-policy discriminant
3076    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3077    /// tree path first passes through this outer accessor onto the
3078    /// slice and then dispatches per-child through the inner accessors
3079    /// — the two-level dispatch means every per-`:children` reader now
3080    /// routes through a typed dispatch on the substrate primitive at
3081    /// both altitudes.
3082    ///
3083    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3084    /// accessed inline at three production sites across two files —
3085    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3086    /// declared-slot enumerator's `!self.children.is_empty()` presence
3087    /// probe (caixa-core/src/manifest.rs, which drives the
3088    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3089    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3090    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3091    /// per-supervisor typed-view composer's `self.children.clone()`
3092    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3093    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3094    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3095    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3096    /// `:children :caixa` self-parent refusal probe's
3097    /// `&caixa.children`-borrowed
3098    /// [`crate::supervisor::validate_no_self_supervision`] input
3099    /// (caixa-core/src/layout.rs, which pins the "no child names the
3100    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3101    /// extension of the outer `:children` axis (a per-cluster
3102    /// `:children-overrides` overlay the wasm-engine operator resolves
3103    /// at admission time so a cluster-specific child-set can tighten
3104    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3105    /// promotion of the plain `Vec<ChildSpec>` to a richer
3106    /// `{static, dynamic}` partition once Erlang/OTP's
3107    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3108    /// axis, per-child priority annotation once multi-strategy fan-out
3109    /// lands) would have had to be threaded through all three open-
3110    /// coded copies in lockstep or one consumer would silently
3111    /// disagree with the peers on which child slice a given Caixa
3112    /// resolves to — the enumerator's presence probe reading the raw
3113    /// slot while the peer view-composer's fold-in path read an
3114    /// operator-resolved slot would silently split the paired
3115    /// declared-slot enumerator and typed-view composition, and the
3116    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3117    /// refusal probe reading a third borrow would silently drift the
3118    /// cross-slot coherence gate's traversal input from the two peers,
3119    /// a three-consumer split at the enumerator, the view composer,
3120    /// and the self-parent gate far from the source `caixa.lisp` with
3121    /// no field naming the child-set-drift root cause. Lifting the
3122    /// resolution rule to a typed method on the substrate primitive
3123    /// means every downstream consumer of the caixa's per-`Caixa`
3124    /// OTP-supervisor outer-slice surface reaches for exactly one
3125    /// typed dispatch — the resolver's accept-set migrates as a unit
3126    /// on any future axis addition.
3127    ///
3128    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3129    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3130    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3131    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3132    /// at the outer altitude of the closed inner-`SupervisorSpec`
3133    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3134    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3135    /// borrow-shared" outer-accessor discipline extended onto the
3136    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3137    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3138    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3139    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3140    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3141    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3142    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3143    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3144    /// M2 typed-composite-element axis
3145    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3146    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3147    /// different altitude).
3148    ///
3149    /// Returns `&[crate::supervisor::ChildSpec]` (not
3150    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3151    /// child list treats it as a read-only sequence — the slice-view
3152    /// is the narrowest borrow that supports every present +
3153    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3154    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3155    /// input, `serde` slice-serialization) without leaking the backing
3156    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3157    /// reaches for (the storage-side `Vec` remains reachable through
3158    /// the `pub children` field for the mutation-carrying serde round-
3159    /// trip and per-test fixture-mutation paths, including the
3160    /// [`Self::supervisor_view`] fold-in path that clones the slot
3161    /// into the typed view). Named `children()` to match the storage
3162    /// field's name verbatim and the tatara-lisp author-surface term
3163    /// (`:children`) the field's own docstring already carries; the
3164    /// accessor's identity maps onto the canonical OTP supervision
3165    /// vocabulary the [`Caixa::children`] field's docstring already
3166    /// reaches for ("Static children of a supervisor").
3167    #[must_use]
3168    pub fn children(&self) -> &[crate::supervisor::ChildSpec] {
3169        self.children.as_slice()
3170    }
3171
3172    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3173    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3174    /// accessor every consumer of the top-level manifest's per-Aplicacao
3175    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3176    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3177    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3178    /// same backing buffer the raw `self.membros.as_slice()` field access
3179    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3180    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3181    /// and every partially-authored Aplicacao carries before the
3182    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3183    /// `&[Membro]` degenerates to an empty slice on those arms without any
3184    /// silent `None` collapse).
3185    ///
3186    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3187    /// per-Aplicacao member list — the load-bearing container of every
3188    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3189    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3190    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3191    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3192    /// the `:entrada :para` external-gateway destination validates
3193    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3194    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3195    /// threads through a lifted per-entry accessor on the
3196    /// [`crate::aplicacao::Membro`] type: the
3197    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3198    /// identity scalar accessor (4a32abf) and the peer
3199    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3200    /// version-requirement scalar accessor (a40b0e3). Every downstream
3201    /// consumer of the mesh-graph path first passes through this outer
3202    /// accessor onto the slice and then dispatches per-member through
3203    /// the inner accessors — the two-level dispatch means every per-
3204    /// `:membros` reader now routes through a typed dispatch on the
3205    /// substrate primitive at both altitudes.
3206    ///
3207    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3208    /// inline at three production sites across two files — the
3209    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3210    /// enumerator's `!self.membros.is_empty()` presence probe
3211    /// (caixa-core/src/manifest.rs, which drives the
3212    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3213    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3214    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3215    /// composer's `self.membros.clone()` per-member fold-in path
3216    /// (caixa-core/src/manifest.rs, which materializes the typed
3217    /// [`crate::aplicacao::AplicacaoSpec`] view every
3218    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3219    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3220    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3221    /// [`crate::aplicacao::validate_no_self_membership`] input
3222    /// (caixa-core/src/layout.rs, which pins the "no member names the
3223    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3224    /// extension of the outer `:membros` axis (a per-cluster
3225    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3226    /// admission time so a cluster-specific member-set can tighten a
3227    /// caixa-declared list without re-authoring the `caixa.lisp`,
3228    /// promotion of the plain `Vec<Membro>` to a richer
3229    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3230    /// members land as a typed axis, per-member priority annotation once
3231    /// multi-strategy fan-out lands) would have had to be threaded
3232    /// through all three open-coded copies in lockstep or one consumer
3233    /// would silently disagree with the peers on which member slice a
3234    /// given Caixa resolves to — the enumerator's presence probe reading
3235    /// the raw slot while the peer view-composer's fold-in path read an
3236    /// operator-resolved slot would silently split the paired
3237    /// declared-slot enumerator and typed-view composition, and the
3238    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3239    /// refusal probe reading a third borrow would silently drift the
3240    /// cross-slot coherence gate's traversal input from the two peers, a
3241    /// three-consumer split at the enumerator, the view composer, and
3242    /// the self-membership gate far from the source `caixa.lisp` with no
3243    /// field naming the member-set-drift root cause. Lifting the
3244    /// resolution rule to a typed method on the substrate primitive
3245    /// means every downstream consumer of the caixa's per-`Caixa`
3246    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3247    /// typed dispatch — the resolver's accept-set migrates as a unit on
3248    /// any future axis addition.
3249    ///
3250    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3251    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3252    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3253    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3254    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3255    /// altitude. Peer at the outer altitude of the closed inner-
3256    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3257    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3258    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3259    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3260    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3261    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3262    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3263    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3264    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3265    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3266    /// pattern onto the sibling M3 typed-composite-element axis
3267    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3268    /// [`crate::AplicacaoSpec::membros`] element type at a different
3269    /// altitude).
3270    ///
3271    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3272    /// because every downstream consumer of the member list treats it
3273    /// as a read-only sequence — the slice-view is the narrowest borrow
3274    /// that supports every present + roadmapped consumer (`.iter()`,
3275    /// `.len()`, `.is_empty()`, the
3276    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3277    /// input, `serde` slice-serialization) without leaking the backing
3278    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3279    /// reaches for (the storage-side `Vec` remains reachable through the
3280    /// `pub membros` field for the mutation-carrying serde round-trip
3281    /// and per-test fixture-mutation paths, including the
3282    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3283    /// the typed view). Named `membros()` to match the storage field's
3284    /// name verbatim and the tatara-lisp author-surface term
3285    /// (`:membros`) the field's own docstring already carries; the
3286    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3287    /// vocabulary the [`Caixa::membros`] field's docstring already
3288    /// reaches for ("Member Servicos that make up this Aplicacao").
3289    #[must_use]
3290    pub fn membros(&self) -> &[crate::aplicacao::Membro] {
3291        self.membros.as_slice()
3292    }
3293
3294    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3295    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3296    /// inter-Servico contract-list slice accessor every consumer of the
3297    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3298    /// slice-view keys off — returns the author-declared `:contratos`
3299    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3300    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3301    /// backing buffer the raw `self.contratos.as_slice()` field access
3302    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3303    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3304    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3305    /// single member with no inter-Servico edge carries; the returned
3306    /// `&[WitContract]` degenerates to an empty slice on those arms
3307    /// without any silent `None` collapse).
3308    ///
3309    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3310    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3311    /// container of every per-edge `{de, para, wit, endpoint | subject |
3312    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3313    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3314    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3315    /// adjacency-list seed dispatch on at mesh-artifact materialization
3316    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3317    /// `:membros` vertex set resolves against, closed by the
3318    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3319    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3320    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3321    /// per-edge axis threads through a lifted per-entry accessor on the
3322    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3323    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3324    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3325    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3326    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3327    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3328    /// and the WIT-world discriminant. Every downstream consumer of the
3329    /// mesh-graph edge path first passes through this outer accessor
3330    /// onto the slice and then dispatches per-contract through the
3331    /// inner accessors — the two-level dispatch means every
3332    /// per-`:contratos` reader now routes through a typed dispatch on
3333    /// the substrate primitive at both altitudes.
3334    ///
3335    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3336    /// accessed inline at two production sites in
3337    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3338    /// mesh-slot declared-slot enumerator's
3339    /// `!self.contratos.is_empty()` presence probe (which drives the
3340    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3341    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3342    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3343    /// typed-view composer's `self.contratos.clone()` per-contract
3344    /// fold-in path (which materializes the typed
3345    /// [`crate::aplicacao::AplicacaoSpec`] view every
3346    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3347    /// downstream `caixa-mesh` renderer dispatches on). A future
3348    /// extension of the outer `:contratos` axis (a per-cluster
3349    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3350    /// at admission time so a cluster-specific edge-set can tighten a
3351    /// caixa-declared list without re-authoring the `caixa.lisp`,
3352    /// promotion of the plain `Vec<WitContract>` to a richer
3353    /// `{static, dynamic}` partition once runtime-resolved contract
3354    /// edges land, per-edge policy annotation once the M4 per-edge
3355    /// policy overlay axis lands) would have had to be threaded through
3356    /// both open-coded copies in lockstep or one consumer would
3357    /// silently disagree with the peer on which edge slice a given
3358    /// Caixa resolves to — the enumerator's presence probe reading the
3359    /// raw slot while the peer view-composer's fold-in path read an
3360    /// operator-resolved slot would silently split the paired
3361    /// declared-slot enumerator and typed-view composition, a
3362    /// two-consumer split at the enumerator and the view composer far
3363    /// from the source `caixa.lisp` with no field naming the edge-set-
3364    /// drift root cause. Lifting the resolution rule to a typed method
3365    /// on the substrate primitive means every downstream consumer of
3366    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3367    /// reaches for exactly one typed dispatch — the resolver's
3368    /// accept-set migrates as a unit on any future axis addition.
3369    ///
3370    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3371    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3372    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3373    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3374    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3375    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3376    /// mesh-slot arm of the composite-slice sub-family the sibling
3377    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3378    /// Peer at the outer altitude of the closed inner-
3379    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3380    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3381    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3382    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3383    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3384    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3385    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3386    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3387    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3388    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3389    /// pattern onto the sibling M3 typed-composite-element axis
3390    /// ([`crate::aplicacao::WitContract`] composite, matching the
3391    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3392    /// different altitude).
3393    ///
3394    /// Returns `&[crate::aplicacao::WitContract]` (not
3395    /// `&Vec<WitContract>`) because every downstream consumer of the
3396    /// contract list treats it as a read-only sequence — the slice-view
3397    /// is the narrowest borrow that supports every present + roadmapped
3398    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3399    /// discriminant dispatch, `serde` slice-serialization) without
3400    /// leaking the backing `Vec`'s grow/push/reserve surface no
3401    /// consumer of the typed view reaches for (the storage-side `Vec`
3402    /// remains reachable through the `pub contratos` field for the
3403    /// mutation-carrying serde round-trip and per-test fixture-mutation
3404    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3405    /// clones the slot into the typed view). Named `contratos()` to
3406    /// match the storage field's name verbatim and the tatara-lisp
3407    /// author-surface term (`:contratos`) the field's own docstring
3408    /// already carries; the accessor's identity maps onto the canonical
3409    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3410    /// docstring already reaches for ("WIT-typed inter-Servico
3411    /// contracts").
3412    #[must_use]
3413    pub fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3414        self.contratos.as_slice()
3415    }
3416
3417    /// Compose the Aplicacao-related flat slots into a single typed
3418    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3419    /// downstream renderer consumption. Returns `None` when the
3420    /// caixa isn't a `:kind Aplicacao`.
3421    #[must_use]
3422    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3423        if !self.kind().is_aplicacao() {
3424            return None;
3425        }
3426        Some(crate::aplicacao::AplicacaoSpec {
3427            membros: self.membros().to_vec(),
3428            contratos: self.contratos().to_vec(),
3429            politicas: self.politicas().cloned().unwrap_or_default(),
3430            placement: self.placement().cloned().unwrap_or_default(),
3431            entrada: self.entrada().cloned(),
3432        })
3433    }
3434
3435    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3436    /// *declares* a value on, in canonical declaration order
3437    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3438    /// `:entrada`). A slot counts as declared when its backing field
3439    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3440    ///
3441    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3442    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3443    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3444    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3445    /// caixa-flux / caixa-helm renderers only emit them for an
3446    /// Aplicacao. On any *other* kind a declared mesh slot is the
3447    /// manifest field's documented "ignored otherwise" (see the
3448    /// `:membros` … `:entrada` field docs): it silently passes
3449    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3450    /// rendered — far from the source caixa.lisp.
3451    /// [`crate::StandardLayout::verify`] consults this to reject that
3452    /// silent-drop at caixa-build time
3453    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3454    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3455    /// a slot foreign to the kind is a build error, not a silent drop.
3456    ///
3457    /// Lifted as a typed method (rather than an inline disjunction at
3458    /// the verify call site) so the mesh-slot set lives in one place —
3459    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3460    /// overlay, distributed-app takeover config) is one push here, and
3461    /// every consumer reaching for "which mesh slots are set" (the
3462    /// verify gate, a future `feira lint` kind-coherence advisory)
3463    /// inherits the canonical order without rolling its own.
3464    ///
3465    /// Each per-arm kebab-case label is routed through the peer
3466    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3467    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3468    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3469    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3470    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3471    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3472    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3473    /// kebab-case label + renderer-side artifact key) route through one
3474    /// canonical declaration per arm — same discipline the peer
3475    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3476    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3477    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3478    /// axis, extended here to close the M3 mesh-slot author-facing-label
3479    /// axis so both altitudes of the typed-slot algebra
3480    /// (per-Servico M2 + per-Aplicacao M3) share the same
3481    /// "one canonical byte-string per arm, next to the axis" discipline.
3482    #[must_use]
3483    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3484        let mut slots = Vec::new();
3485        if !self.membros().is_empty() {
3486            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3487        }
3488        if !self.contratos().is_empty() {
3489            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3490        }
3491        if self.politicas().is_some() {
3492            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3493        }
3494        if self.placement().is_some() {
3495            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3496        }
3497        if self.entrada().is_some() {
3498            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3499        }
3500        slots
3501    }
3502
3503    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3504    /// caixa *declares* a value on, in canonical declaration order
3505    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3506    /// `:children`). A slot counts as declared when its backing field
3507    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3508    ///
3509    /// The supervisor-tree slots compose the typed OTP supervisor of a
3510    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3511    /// `:children` field docs above). [`Self::supervisor_view`] only
3512    /// folds them into a validatable [`SupervisorSpec`] when the kind
3513    /// matches (returns `None` otherwise), and the wasm-operator's
3514    /// hierarchical reconciler only consumes them for a Supervisor. On
3515    /// any *other* kind a declared supervisor slot is the manifest
3516    /// field's documented "ignored otherwise" (see the `:estrategia` …
3517    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3518    /// and then vanishes — never validated, never reconciled — far from
3519    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3520    /// this to reject that silent-drop at caixa-build time
3521    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3522    /// exact mirror of the [`Self::declared_mesh_slots`] /
3523    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3524    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3525    /// error, not a silent drop.
3526    #[must_use]
3527    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3528        let mut slots = Vec::new();
3529        if self.estrategia().is_some() {
3530            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3531        }
3532        if self.max_restarts().is_some() {
3533            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3534        }
3535        if self.restart_window().is_some() {
3536            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3537        }
3538        if !self.children().is_empty() {
3539            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3540        }
3541        slots
3542    }
3543
3544    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3545    /// caixa *declares* a value on, in canonical declaration order
3546    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3547    /// declared when its backing field carries a value — a `Some(...)`,
3548    /// or a non-empty `Vec`.
3549    ///
3550    /// The M2 slots configure the runtime of a long-running wasm
3551    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3552    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3553    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3554    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3555    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3556    /// emit these slots for a Servico; on any *other* kind a declared M2
3557    /// slot is the manifest field's documented "ignored otherwise": its
3558    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3559    /// but the value is never rendered into a chart / programs.yaml entry
3560    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
3561    /// vanishes, far from the source caixa.lisp.
3562    /// [`crate::StandardLayout::verify`] consults this to reject that
3563    /// silent-drop at caixa-build time
3564    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
3565    /// mirror of the [`Self::declared_mesh_slots`] /
3566    /// [`Self::declared_supervisor_slots`] gates on the peer
3567    /// kind-exclusive slot sets: a slot foreign to the kind is a build
3568    /// error, not a silent drop.
3569    ///
3570    /// Each per-arm kebab-case label is routed through the peer
3571    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
3572    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
3573    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
3574    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
3575    /// both halves of the M2 top-level slot's dual axis (author-facing
3576    /// kebab-case label + renderer-side camelCase overlay-container wire
3577    /// key) route through one canonical declaration per arm — same
3578    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
3579    /// author-label consts (889dc18) establish on the sibling
3580    /// per-callback axis inside the `:behavior` overlay block.
3581    #[must_use]
3582    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
3583        let mut slots = Vec::new();
3584        if self.limits().is_some() {
3585            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
3586        }
3587        if self.behavior().is_some() {
3588            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
3589        }
3590        if !self.upgrade_from().is_empty() {
3591            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
3592        }
3593        slots
3594    }
3595
3596    /// The kebab-case `:slot` tags of every code-surface slot this caixa
3597    /// declares a value on that its [`CaixaKind`] doesn't natively own,
3598    /// in canonical declaration order (`:exe` → `:servicos`). A
3599    /// code-surface slot is owned by exactly one kind: `:exe` by
3600    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
3601    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
3602    /// `ComputeUnit` daemon surface).
3603    ///
3604    /// Each is silently ignored when declared on the wrong kind: the
3605    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
3606    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
3607    /// code-running kind a declared `:exe` / `:servicos` is the manifest
3608    /// field's documented "ignored otherwise" — its path is checked for
3609    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
3610    /// (which run after [`Caixa::from_lisp`]), but the value is never
3611    /// rendered into a build target or programs.yaml entry. It silently
3612    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
3613    /// caixa.lisp, with no field naming which slot is foreign.
3614    ///
3615    /// [`crate::StandardLayout::verify`] consults this to reject that
3616    /// silent-drop at caixa-build time
3617    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
3618    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
3619    /// gates ([`Self::declared_servico_slots`] /
3620    /// [`Self::declared_supervisor_slots`] /
3621    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
3622    /// axis to be closed on the typed surface. The Supervisor /
3623    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
3624    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
3625    /// diagnostics — they fire ahead of this gate on the same `verify`
3626    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
3627    /// and this method is moot. For Biblioteca / Binario / Servico, this
3628    /// gate fires when a code-running kind declares another code-running
3629    /// kind's exclusive code surface.
3630    ///
3631    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
3632    /// may legitimately ship a `lib/` helper that the underlying
3633    /// substrate (the nix flake for Binario, the wasm component build
3634    /// for Servico) bundles into its build, so the slot's
3635    /// declared-on-wrong-kind cardinality isn't a structural error on
3636    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
3637    /// is the native case (the slot's owning kind). Supervisor /
3638    /// Aplicacao declaring `:bibliotecas` is gated upstream by
3639    /// [`crate::LayoutError::SupervisorOwnsCode`] /
3640    /// [`crate::LayoutError::AplicacaoOwnsCode`].
3641    ///
3642    /// Lifted as a typed method (rather than an inline disjunction at
3643    /// the verify call site) so the foreign-code-slot set lives in one
3644    /// place — a future kind that gains its own code-surface slot is
3645    /// one push here, and every consumer reaching for "which code
3646    /// surfaces are foreign to this kind" (the verify gate, a future
3647    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
3648    /// per-caixa build-target classifier) inherits the canonical order
3649    /// without rolling its own.
3650    #[must_use]
3651    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
3652        let mut slots = Vec::new();
3653        if !self.exe().is_empty() && !self.kind().requires_exe() {
3654            slots.push(":exe");
3655        }
3656        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
3657            slots.push(":servicos");
3658        }
3659        slots
3660    }
3661
3662    /// Validate every entry of `:deps` and `:deps-dev` through
3663    /// [`Dep::validate`] — closing the parity loop with the per-axis
3664    /// `:versao` gates already wired into the typed-graph
3665    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
3666    /// 9888b13) and typed supervisor tree
3667    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
3668    ///
3669    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
3670    /// were the only `:versao` axes still untyped past
3671    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
3672    /// as a String without parsing it, so a malformed-but-non-empty
3673    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
3674    /// silently passed parse and the `semver::Error` surfaced at
3675    /// lacre-resolve time, far from the source caixa.lisp, with no
3676    /// field naming which `:deps` entry carried the typo. Lifting the
3677    /// gate here makes the four `:versao` typed surfaces (`:deps`,
3678    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
3679    /// every requirement string past `validate_deps` is round-trippable
3680    /// through [`crate::parse_requirement`] without re-checking at the
3681    /// resolver layer.
3682    ///
3683    /// Both lists run through the same per-entry validator so a typo
3684    /// in `:deps-dev` surfaces with the same diagnostic as one in
3685    /// `:deps` — neither axis is a second-class citizen of the typed
3686    /// surface.
3687    ///
3688    /// Within each list, [`DepError::DuplicateNome`] closes the
3689    /// set-not-multiset discipline on the `:nome` axis: two entries
3690    /// naming the same caixa carry two `:versao` / `:fonte` / feature
3691    /// triples that the caixa-resolver's lacre pipeline collapses to one
3692    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
3693    /// silently overwrites the first at `concrete_versao`-resolve time
3694    /// (the same "second wins / one silently overwrites the other"
3695    /// shape the peer typed-graph duplicate gates already close on every
3696    /// other Vec-shaped authoring surface that keys by name). The
3697    /// duplicate check fires per-list and runs *after* each per-entry
3698    /// [`Dep::validate`] call so a malformed-and-duplicated entry
3699    /// surfaces its narrower per-entry diagnostic
3700    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
3701    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
3702    /// diagnostic — the canonical "per-entry shape before cross-entry
3703    /// uniqueness" precedence the peer `:children :caixa`
3704    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
3705    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
3706    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
3707    /// ([`crate::AplicacaoSpec::validate_placement`]),
3708    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
3709    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
3710    /// and the within-`:upgrade-from`-entry per-instruction-class
3711    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
3712    /// [`crate::UpgradeError::DuplicateStateChange`],
3713    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
3714    ///
3715    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
3716    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
3717    /// same name in both tables (the dev table's pin overrides the
3718    /// runtime table's pin in test/dev contexts), and caixa's surface
3719    /// mirrors that convention until a deliberate choice retires the
3720    /// override pattern. Only within-list duplicates are structurally
3721    /// incoherent — those are what this gate closes.
3722    pub fn validate_deps(&self) -> Result<(), DepError> {
3723        for &list in crate::dep::DepList::ALL {
3724            let mut seen = std::collections::HashSet::new();
3725            for dep in self.deps_of(list) {
3726                dep.validate()?;
3727                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
3728                    DepError::DuplicateNome {
3729                        nome: dep.nome().to_string(),
3730                        list: list.as_str(),
3731                    }
3732                })?;
3733            }
3734        }
3735        Ok(())
3736    }
3737
3738    /// Reject `:nome` values the K8s apiserver would refuse at admission
3739    /// time. The top-level Caixa identity flows directly into every
3740    /// substrate-side artifact's `metadata.name` axis: the
3741    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
3742    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
3743    /// aggregator keys ComputeUnit derivation off
3744    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
3745    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
3746    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
3747    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
3748    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
3749    /// ([`caixa-mesh::lib::cilium_network_policies`],
3750    /// [`caixa-mesh::lib::gateway_routes`]), and the default
3751    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
3752    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
3753    /// schema enforces the DNS-1123 label rule on admission; a
3754    /// structurally invalid `:nome` (`"MyApp"` — the canonical
3755    /// "I copied the display name verbatim" footgun, `"my_app"` — the
3756    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
3757    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
3758    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
3759    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
3760    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
3761    /// failure surfaced at `kubectl apply` time as a `metadata.name:
3762    /// Invalid value` rejection on whichever derived artifact admitted
3763    /// first, far from the source `caixa.lisp` and without any field
3764    /// naming the offending `:nome`.
3765    ///
3766    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
3767    /// substrate-side predicate the per-axis name gates already share:
3768    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
3769    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
3770    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
3771    /// diagnostic is self-locating (the offending `:nome` is named
3772    /// verbatim) and the author can grep their `caixa.lisp` for
3773    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
3774    /// every per-axis sibling gate already exposes
3775    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
3776    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
3777    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
3778    ///
3779    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
3780    /// derive macro stores the raw String) is gated by the narrower
3781    /// [`ManifestError::NomeEmpty`] arm before the predicate is
3782    /// consulted, mirroring the empty-first cascade every per-axis
3783    /// name gate already uses (e.g. `MembroCaixaEmpty` before
3784    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
3785    pub fn validate_nome(&self) -> Result<(), ManifestError> {
3786        // Routes through the shared
3787        // [`crate::render::require_valid_dns_1123_label`] gate the peer
3788        // name axes each land on so drift between the eight axes'
3789        // accepted DNS-1123-label sets is structurally impossible.
3790        let nome = self.nome();
3791        crate::render::require_valid_dns_1123_label(
3792            nome,
3793            || ManifestError::NomeEmpty,
3794            |reason| ManifestError::NomeInvalid {
3795                nome: nome.to_string(),
3796                reason,
3797            },
3798        )
3799    }
3800
3801    /// Reject `:nome` values whose joint length with the canonical
3802    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
3803    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
3804    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
3805    /// substrate carries materializes the caixa's `:nome` through the
3806    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
3807    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
3808    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
3809    /// `ChartDir.name` + `Chart.yaml::name`
3810    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
3811    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
3812    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
3813    /// `oci://<registry>/lareira-<nome>` chart ref
3814    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
3815    /// admission rule strict-parses against DNS-1123-label, the Helm
3816    /// operator's tracking-secret name is derived from `release_name`
3817    /// and is itself DNS-1123-label-bounded, and the rendered chart's
3818    /// K8s object `metadata.name` axes embed the chart name as a
3819    /// prefix — every one fails admission on a > 63-byte chart name.
3820    ///
3821    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
3822    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
3823    /// `:nome` of 56–63 bytes silently passed validate (the inner
3824    /// DNS-1123 check accepts the bare `:nome`) but produced a
3825    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
3826    /// rejected at admission — far from the source `caixa.lisp`, with
3827    /// no field naming the overflow root cause. The
3828    /// [`lareira_chart_name`] helper's own doc comment
3829    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
3830    /// "the M4 admission webhook will pin the joint-length invariant
3831    /// when it lands". This gate lands the invariant at the
3832    /// manifest-validate layer rather than waiting for the apiserver
3833    /// — the same fail-at-the-source posture every peer per-axis
3834    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
3835    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
3836    /// `:edicao`, etc.) takes.
3837    ///
3838    /// Thin wrapper around
3839    /// [`crate::render::is_lareira_chart_name_shape`] (the
3840    /// substrate-side predicate that composes [`lareira_chart_name`] +
3841    /// [`is_dns_1123_label`] via the lifted
3842    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
3843    /// shared parser-shaped reason into the
3844    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
3845    /// diagnostic is self-locating (the offending `:nome` is named
3846    /// verbatim alongside the rendered chart name and the budget) and
3847    /// the author can shorten in one edit. The gate runs across every
3848    /// `:kind` — `:nome` is the substrate-wide identity axis any
3849    /// future renderer the substrate adds can derive a
3850    /// `lareira-<nome>` artifact from, and uniform enforcement closes
3851    /// the drift footgun where a future kind grows a chart-emitting
3852    /// render path while the validate cascade doesn't catch it.
3853    ///
3854    /// Runs *after* [`Self::validate_nome`] so the narrower
3855    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
3856    /// structurally-malformed `:nome` (empty, uppercase, underscore,
3857    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
3858    /// specific shape error rather than the chart-name-budget error,
3859    /// preserving the legitimate "well-shaped `:nome` that happens to
3860    /// overflow the joint cap" arm for this gate.
3861    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
3862        let nome = self.nome();
3863        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
3864            ManifestError::NomeChartNameBudgetExceeded {
3865                nome: nome.to_string(),
3866                reason,
3867            }
3868        })
3869    }
3870
3871    /// Reject `:versao` values that don't parse as [`semver::Version`].
3872    /// The top-level Caixa version flows directly into every
3873    /// substrate-side artifact that carries a "this is which version of
3874    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
3875    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
3876    /// SemVer-2-strict at `helm template` / `helm install` time per
3877    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
3878    /// `feira publish` Zig-style `v<versao>` git tag
3879    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
3880    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
3881    /// `versao:` value the `lareira-fleet-programs` aggregator carries
3882    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
3883    /// `:latest` tags the substrate's `wasi-service-flake` builds with
3884    /// `skopeo push`, the lacre closure's pinned versions
3885    /// ([`caixa-resolver`] keys `concrete_versao`), and the
3886    /// `:upgrade-from :from` references peers in this exact `versao`
3887    /// shape (`semver::Version`, not `VersionReq`). Each consumer
3888    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
3889    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
3890    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
3891    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
3892    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
3893    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
3894    /// into the version field a peer `:deps :versao` accepts;
3895    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
3896    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
3897    /// derive macro stores the raw String) and the failure surfaced at
3898    /// the *first* downstream consumer that strict-parses it: at
3899    /// `helm install` time as a chart-version rejection, at
3900    /// `feira publish` time as a malformed git tag, at lacre-resolve
3901    /// time as a `semver::Error` not naming the offending caixa, at
3902    /// `feira upgrade --to <versao>` time as an unresolvable
3903    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
3904    /// and without any field naming the offending `:versao`.
3905    ///
3906    /// Thin wrapper around [`semver::Version::parse`] — the same parser
3907    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
3908    /// and [`crate::UpgradeFromEntry::validate`] (the peer
3909    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
3910    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
3911    /// variant, carrying the offending `:versao` verbatim + a
3912    /// parser-shaped reason naming the specific violation, so the
3913    /// diagnostic is self-locating (the author can grep their
3914    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
3915    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
3916    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
3917    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
3918    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
3919    /// now structurally equivalent (every value past validate is
3920    /// round-trippable through [`semver::Version::parse`] without
3921    /// re-checking at the renderer, resolver, or operator hot-upgrade
3922    /// layer), peer with the four `:versao` requirement axes (`:deps`,
3923    /// `:deps-dev`, `:membros`, `:children`) the prior commits
3924    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
3925    ///
3926    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
3927    /// the derive macro stores the raw String) is gated by the
3928    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
3929    /// consulted, mirroring the empty-first cascade every per-axis
3930    /// version gate already uses (e.g. `MembroVersaoEmpty` before
3931    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
3932    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
3933    pub fn validate_versao(&self) -> Result<(), ManifestError> {
3934        let versao = self.versao();
3935        if versao.is_empty() {
3936            return Err(ManifestError::VersaoEmpty);
3937        }
3938        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
3939            versao: versao.to_string(),
3940            reason: e.to_string(),
3941        })?;
3942        Ok(())
3943    }
3944
3945    /// Reject `:restart-window` values the shared
3946    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
3947    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
3948    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
3949    /// `Option<Duration>` routed through the shared codec via `with =
3950    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
3951    /// view-construction path ([`Self::supervisor_view`]) folds the
3952    /// raw string through the same shared codec and soft-swallows the
3953    /// parse error as `None` to keep the view best-effort. Without
3954    /// this gate a malformed `:restart-window` (`"1.5s"` — the
3955    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
3956    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
3957    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
3958    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
3959    /// edge case) silently produced a `SupervisorSpec` with
3960    /// `restart_window: None`, indistinguishable from the canonical
3961    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
3962    /// `MaxIntensity / Period` invariant turns into a never-reset
3963    /// supervisor far from the source `caixa.lisp`, with no field
3964    /// naming the offending `:restart-window`. Lifting the gate to a
3965    /// Caixa-level validator mirrors the trajectory of the peer
3966    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
3967    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
3968    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
3969    /// (line 196: "reject invalid `:restart-window` (non-duration)").
3970    ///
3971    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
3972    /// (the shared codec backing `:supervisor :restart-window` as
3973    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
3974    /// `:politicas :circuit-breaker :window` — all three covered by
3975    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
3976    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
3977    /// variant, carrying the offending raw string + a parser-shaped
3978    /// reason naming the canonical authoring form, so the diagnostic
3979    /// is self-locating (the author can grep their `caixa.lisp` for
3980    /// `:restart-window "<value>"` and fix it in one edit) and
3981    /// uniform with every other manifest-level validate diagnostic.
3982    /// With this gate the four `:restart-window`-shaped surfaces (the
3983    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
3984    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
3985    /// now structurally equivalent — every value past the codec is in
3986    /// one accepted set, by construction.
3987    ///
3988    /// `None` (the canonical "omit the slot to express no reset"
3989    /// shape) is accepted trivially — the gate is a no-op when the
3990    /// author didn't author a window. The empty string is rejected by
3991    /// the shared codec (its digit-only gate refuses an empty
3992    /// magnitude), surfacing the same `RestartWindowMalformed`
3993    /// diagnostic as every other rejected non-canonical shape.
3994    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
3995        let Some(s) = self.restart_window() else {
3996            return Ok(());
3997        };
3998        crate::supervisor::duration_codec::parse(s)
3999            .map(|_| ())
4000            .map_err(|reason| ManifestError::RestartWindowMalformed {
4001                restart_window: s.to_string(),
4002                reason,
4003            })
4004    }
4005
4006    /// Reject per-entry values on the three Caixa-level code-surface
4007    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
4008    /// layout checker's `root.join(p)` sandbox would silently subvert.
4009    /// Same three structural footguns the peer
4010    /// [`BehaviorSpec::validate`] (b0c8389) and
4011    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
4012    /// (26da2c7) already close on the M2 `:behavior :on-*` and
4013    /// `:upgrade-from :state-change :script` axes, here lifted onto
4014    /// the three top-level code-path axes through the shared
4015    /// [`is_sandboxed_relative_path`] predicate:
4016    ///
4017    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
4018    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
4019    ///     [`Path::join`] as the base itself — `root.join("")` ==
4020    ///     `root`, so the existence check (`self.exists(&root)`)
4021    ///     trivially passes (the project root exists), and the layout
4022    ///     silently treats the project root as a biblioteca / exe /
4023    ///     servico entry. The `:bibliotecas` loop then hands the root
4024    ///     to `tatara_lisp::read` at `feira build` time as if the root
4025    ///     directory itself were a Lisp source file — a parse error
4026    ///     far from the source `caixa.lisp` with no field naming the
4027    ///     offending entry.
4028    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
4029    ///     [`Path::join`] *replaces* the base when the right-hand side
4030    ///     is absolute, so `root.join("/etc/passwd")` resolves to
4031    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
4032    ///     The existence check then silently consults whatever the
4033    ///     escaped path resolves to — for `:bibliotecas`, the layout
4034    ///     has no `starts_with`-fence (only `:exe` is fenced under
4035    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
4036    ///     `:bibliotecas` entry that happens to resolve on disk
4037    ///     silently passes. For `:exe` / `:servicos` the fence catches
4038    ///     the absolute case downstream as `ExeOutsideDir` /
4039    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
4040    ///     doesn't exist), but with a downstream-shaped diagnostic
4041    ///     that names the resolved escape path rather than the
4042    ///     authoring footgun at the source.
4043    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
4044    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
4045    ///     [`std::path::Component::ParentDir`] anywhere round-trips
4046    ///     through [`Path::join`] as a traversal above the caixa root.
4047    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
4048    ///     *component-aware* (not canonical-path-aware), so
4049    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
4050    ///     is **true** even though the canonical resolution
4051    ///     `{parent of root}/escape.lisp` lives outside the caixa root
4052    ///     — the fence silently lets the parent-escape through, and
4053    ///     the existence check passes if that escape-target happens
4054    ///     to exist. Caught regardless of where the `..` sits
4055    ///     (leading, mid-path, trailing) so the gate matches the peer
4056    ///     predicate's full coverage.
4057    ///
4058    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
4059    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
4060    /// same per-slot diagnostic shape every peer per-axis path-gate
4061    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
4062    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
4063    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
4064    /// order [`Caixa::declared_foreign_code_slots`] uses for its
4065    /// canonical foreign-code-slot diagnostic, so a manifest with
4066    /// multiple malformed slots surfaces the lexicographically-earliest
4067    /// slot's diagnostic deterministically.
4068    ///
4069    /// Lifted to the typed surface as a Caixa-level validator (peer
4070    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
4071    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
4072    /// and wired into [`crate::StandardLayout::verify`] before the
4073    /// existence-check loops so the diagnostic names the offending
4074    /// slot at the source caixa.lisp rather than reporting a
4075    /// downstream `MissingEntry` / `ExeOutsideDir` /
4076    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
4077    /// The fourth typed code-path surface — every author-supplied
4078    /// path on the manifest — is now structurally accept-shaped
4079    /// past validate, peer with `:behavior :on-*` and
4080    /// `:upgrade-from :state-change :script`.
4081    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
4082        /// Per-slot file-type contract for the three Caixa-level
4083        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
4084        /// Each variant names the predicate the per-entry file-type
4085        /// gate consults; [`Self::None`] opts the slot out of any
4086        /// file-type contract. Lifted as a typed local enum so the
4087        /// per-slot dispatch is exhaustive at the `match` — adding a
4088        /// future axis to the typed-substrate `:` slot set (the
4089        /// future `:assets` resource axis the M5 roadmap names, the
4090        /// future `:nix-flake` derivation axis the caixa-flake
4091        /// emitter consults) lands as one variant + one `match` arm,
4092        /// not a coordinated rewrite of every per-slot bool flag.
4093        ///
4094        /// Peer of the typed-substrate per-slot variant disciplines
4095        /// already established on this surface
4096        /// ([`crate::supervisor::RestartStrategy`] +
4097        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
4098        /// supervision-tree axis,
4099        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
4100        /// placement axis, [`crate::aplicacao::WitTarget`] on the
4101        /// `:contratos` payload-target axis): the typed `enum` is
4102        /// the substrate's single source of truth for the per-axis
4103        /// dispatch, and every consumer (the per-arm body here, the
4104        /// future feira-lint per-slot diagnostic renderer, the M4
4105        /// per-axis admission webhook) reaches for the same typed
4106        /// surface rather than re-deriving the partition from inline
4107        /// flag combinations.
4108        enum CodePathFileType {
4109            /// `:exe` — nix-build derivation output, no terminating-
4110            /// extension contract (the canonical `"exe/<name>"`
4111            /// fixtures the layout's `ExeOutsideDir` error message
4112            /// documents carry no extension by convention).
4113            None,
4114            /// `:bibliotecas` — tatara-lisp source files the
4115            /// `feira build` loop reads through `tatara_lisp::read`
4116            /// at parse time. Routes to [`is_lisp_extension`].
4117            LispSource,
4118            /// `:servicos` — ComputeUnit-CR YAML files the
4119            /// caixa-helm / caixa-flux renderers consume through
4120            /// `serde_yaml::from_str`. Routes to
4121            /// [`is_computeunit_yaml_extension`].
4122            ComputeUnitYaml,
4123        }
4124
4125        // The per-slot [`CodePathFileType`] selects which axes carry the
4126        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
4127        // source axis (the `feira build` loop at
4128        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
4129        // `tatara_lisp::read` at parse time) — the lifted
4130        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
4131        // `:exe` is the nix-built executable surface (per the canonical
4132        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
4133        // error message documents and every in-tree
4134        // `caixa_with_code_paths` positive control uses) — its file-type
4135        // contract is "nix-build derivation output", not a typed source
4136        // file, so [`CodePathFileType::None`] opts the slot out of any
4137        // file-type gate. `:servicos` is the `.computeunit.yaml`
4138        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
4139        // renderers consume each entry through `serde_yaml::from_str` as
4140        // a typed `ComputeUnit` CR) — the lifted
4141        // [`is_computeunit_yaml_extension`] predicate gates the compound
4142        // `.computeunit.yaml` suffix. All three axes are surfaced through
4143        // the same iteration so the sandbox-shape + duplicate gates
4144        // apply uniformly; the typed file-type dispatch fires per-slot
4145        // exactly where the downstream consumer's accepted set demands
4146        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
4147        // compounding lift on the peer 64772a9 `:bibliotecas`
4148        // `.lisp`-gate trajectory — the second of the three code-path
4149        // axes to land on a typed compound-suffix gate, with the same
4150        // self-locating per-slot diagnostic shape every peer per-axis
4151        // file-type lift uses (`*NonLispExtension { slot, path }` /
4152        // `*NonComputeUnitYamlExtension { slot, path }`).
4153        for (slot, list, file_type) in [
4154            (
4155                ":bibliotecas",
4156                &self.bibliotecas,
4157                CodePathFileType::LispSource,
4158            ),
4159            (":exe", &self.exe, CodePathFileType::None),
4160            (
4161                ":servicos",
4162                &self.servicos,
4163                CodePathFileType::ComputeUnitYaml,
4164            ),
4165        ] {
4166            // Per-slot set-not-multiset gate on the typed code-path axis.
4167            // Every peer Vec-shaped author-supplied list past validate is
4168            // a set, not a multiset: `:membros :caixa`
4169            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
4170            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
4171            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
4172            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
4173            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
4174            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
4175            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
4176            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
4177            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
4178            // the three code-path lists are the last Vec-shaped author-
4179            // supplied slots on the typed Caixa surface still admitting a
4180            // duplicate entry silently. Scope is per-list (`:bibliotecas`
4181            // duplicates are flagged within `:bibliotecas`, not across
4182            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
4183            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
4184            // legitimate dev-vs-runtime shape on the dep axis, fenced
4185            // separately by [`crate::dep::validate_no_self_dep`]). On the
4186            // code-path axis a cross-slot collision is structurally
4187            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
4188            // fence — `:exe` and `:servicos` entries are confined to their
4189            // own directory trees, so the only way a string could appear
4190            // on two code-path lists is the (rare, structurally invalid)
4191            // case where `:bibliotecas` carries an `"exe/<x>"` or
4192            // `"servicos/<x>.yaml"`-shaped path.
4193            //
4194            // Without the gate three authoring footguns silently passed:
4195            //
4196            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
4197            //     canonical copy-paste-the-wrong-file footgun. `feira
4198            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
4199            //     list and re-parses the same file twice, wasting work
4200            //     and silently masking the author's intent to declare a
4201            //     *second* biblioteca.
4202            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
4203            //     Binario surface. The future `caixa-flake` `nix flake`
4204            //     emitter that materializes each `:exe` entry as a flake
4205            //     `packages.<exe-name>` derivation would collide on the
4206            //     duplicate package name and surface a flake-eval error
4207            //     far from the source `caixa.lisp`.
4208            //   - `:servicos ("servicos/x.computeunit.yaml"
4209            //     "servicos/x.computeunit.yaml")` — the same footgun on
4210            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
4211            //     renderers already refuse `:servicos.len() != 1` with
4212            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
4213            //     that diagnostic surfaces "too many servicos" without
4214            //     naming "duplicate entry" — the typed self-locating
4215            //     "which entry is the duplicate" framing only lands at
4216            //     this gate.
4217            //
4218            // Same `seen.insert(entry.as_str())` shape every peer per-list
4219            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
4220            // 86c769b, `:deps` 359fba5) and the same "structural shape
4221            // checks fire before the duplicate check on the same entry"
4222            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
4223            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
4224            // empty entry first, not the duplicate on the later pair).
4225            let mut seen = std::collections::HashSet::new();
4226            for entry in list {
4227                let path = Path::new(entry);
4228                match is_sandboxed_relative_path(path) {
4229                    Ok(()) => {}
4230                    Err(PathShapeViolation::Empty) => {
4231                        return Err(ManifestError::CodePathEmpty { slot });
4232                    }
4233                    Err(PathShapeViolation::Absolute) => {
4234                        return Err(ManifestError::CodePathAbsolute {
4235                            slot,
4236                            path: path.to_path_buf(),
4237                        });
4238                    }
4239                    Err(PathShapeViolation::ParentEscape) => {
4240                        return Err(ManifestError::CodePathParentEscape {
4241                            slot,
4242                            path: path.to_path_buf(),
4243                        });
4244                    }
4245                }
4246                // The per-slot file-type gate dispatched through the
4247                // typed [`CodePathFileType`] selector above. Each variant
4248                // routes to the lifted predicate the downstream consumer
4249                // demands:
4250                //
4251                //   - [`LispSource`] → [`is_lisp_extension`] for
4252                //     `:bibliotecas` (the `feira build` loop's
4253                //     `tatara_lisp::read` consumer);
4254                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
4255                //     for `:servicos` (the caixa-helm / caixa-flux
4256                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
4257                //     accepted set);
4258                //   - [`None`] for `:exe` — the nix-build derivation-
4259                //     output axis has no terminating-extension contract.
4260                //
4261                // Fires after the sandbox-shape arms so a path that is
4262                // *both* sandbox-escaping and wrong-extension surfaces
4263                // the more fundamental sandbox-shape diagnostic first
4264                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
4265                // `ParentEscape` → `NonLispExtension` arm-ordering on
4266                // `:behavior :on-*` c97815a, and `EmptyScript` →
4267                // `AbsoluteScript` → `ParentEscapeScript` →
4268                // `NonLispExtensionScript` on
4269                // `:upgrade-from :state-change :script` 33cc830), and
4270                // before the duplicate gate so the narrower per-entry
4271                // file-type shape dominates the cross-entry uniqueness
4272                // diagnostic (a
4273                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
4274                // `:servicos` surfaces
4275                // `CodePathNonComputeUnitYamlExtension` on the first
4276                // entry rather than `CodePathDuplicate` on the pair —
4277                // peer with the 64772a9 `:bibliotecas`
4278                // `("lib/x.txt" "lib/x.txt")` ordering).
4279                match file_type {
4280                    CodePathFileType::None => {}
4281                    CodePathFileType::LispSource => {
4282                        if !is_lisp_extension(path) {
4283                            return Err(ManifestError::CodePathNonLispExtension {
4284                                slot,
4285                                path: path.to_path_buf(),
4286                            });
4287                        }
4288                    }
4289                    CodePathFileType::ComputeUnitYaml => {
4290                        if !is_computeunit_yaml_extension(path) {
4291                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
4292                                slot,
4293                                path: path.to_path_buf(),
4294                            });
4295                        }
4296                    }
4297                }
4298                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
4299                    ManifestError::CodePathDuplicate {
4300                        slot,
4301                        path: path.to_path_buf(),
4302                    }
4303                })?;
4304            }
4305        }
4306        Ok(())
4307    }
4308
4309    /// Reject `:etiquetas` lists with an empty entry or with two entries
4310    /// agreeing on the same string. `:etiquetas` is the universal
4311    /// registry-search-tag axis on [`Caixa`] (every kind carries the
4312    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4313    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
4314    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
4315    /// a [`std::collections::BTreeSet`] alongside the four substrate-
4316    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
4317    /// Two authoring footguns silently passed validate without this gate:
4318    ///
4319    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
4320    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
4321    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
4322    ///     `chart.metadata.keywords` admits the value without a strict
4323    ///     parser-side gate, but the empty keyword has no operational
4324    ///     meaning — it indexes nothing in the future caixa-registry
4325    ///     search axis and clutters the rendered chart with a no-op tag.
4326    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
4327    ///     copy-paste-the-wrong-tag footgun) silently passed validate
4328    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
4329    ///     at chart render — a "second wins / one silently disappears"
4330    ///     shape divergent from every peer typed-graph set gate
4331    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
4332    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
4333    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4334    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4335    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4336    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
4337    ///     on `:upgrade-from`, the per-instruction-class singularity
4338    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
4339    ///     [`crate::UpgradeError::DuplicateStateChange`] /
4340    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
4341    ///     discipline is uniform: every Vec-shaped author-supplied list
4342    ///     past validate is set-not-multiset, by construction.
4343    ///
4344    /// Past the empty arm the gate enforces the chart-keyword shape
4345    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
4346    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
4347    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
4348    /// continuation. Closes the canonical paste-from-doc footguns the
4349    /// bare empty + duplicate arms left open: paste-from-aligned-doc
4350    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
4351    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
4352    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
4353    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
4354    /// — the author meant three separate list entries), path-separator
4355    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
4356    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
4357    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
4358    /// control bytes that would silently land as malformed search tags
4359    /// in the rendered Chart.yaml `keywords:` array and break the
4360    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
4361    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
4362    /// established on the sibling universal-axis `Vec<String>` surface
4363    /// — the second universal-axis Vec<String> surface to land the
4364    /// empty-first-then-shape-then-duplicate per-entry cascade.
4365    ///
4366    /// Same empty-first cascade discipline every peer per-axis gate
4367    /// uses: the per-entry empty arm fires before the per-entry shape
4368    /// arm fires before the cross-entry duplicate arm, so an
4369    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
4370    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
4371    /// has no value" defect) before either the shape or the duplicate
4372    /// diagnostic. Walks the list in declaration order so the
4373    /// first-collision diagnostic surfaces the lexicographically-
4374    /// earliest offending position, peer with every other duplicate
4375    /// gate on this surface.
4376    ///
4377    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
4378    /// caixa-build gate alongside the peer universal gates
4379    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4380    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
4381    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
4382    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4383    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4384    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4385    /// slot sets. The future caixa-registry search axis can reach for
4386    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
4387    /// chart-keyword-shaped string without re-deriving the precondition.
4388    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
4389        let mut seen = std::collections::HashSet::new();
4390        for etiqueta in self.etiquetas() {
4391            if etiqueta.is_empty() {
4392                return Err(ManifestError::EtiquetaEmpty);
4393            }
4394            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
4395                ManifestError::EtiquetaInvalid {
4396                    etiqueta: etiqueta.clone(),
4397                    reason,
4398                }
4399            })?;
4400            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
4401                ManifestError::EtiquetaDuplicate {
4402                    etiqueta: etiqueta.clone(),
4403                }
4404            })?;
4405        }
4406        Ok(())
4407    }
4408
4409    /// Reject `:autores` lists with an empty entry or with two entries
4410    /// agreeing on the same string. `:autores` is the universal
4411    /// maintainer-axis on [`Caixa`] (every kind carries the
4412    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4413    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
4414    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
4415    /// to a `Maintainer { name, email: None }` without dedup). Two
4416    /// authoring footguns silently passed validate without this gate:
4417    ///
4418    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
4419    ///     blank-doc footgun) rendered as
4420    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
4421    ///     empty maintainer name has no operational meaning — it
4422    ///     identifies no one in the substrate's authorship index and
4423    ///     clutters the rendered chart with a no-op maintainer.
4424    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
4425    ///     the copy-paste-the-wrong-author footgun) silently passed
4426    ///     validate and rendered as two identical maintainer entries.
4427    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
4428    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
4429    ///     rendered `keywords:` array at chart-render time), the
4430    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
4431    ///     entries stack verbatim in the chart, divergent from every
4432    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
4433    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
4434    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4435    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4436    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4437    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
4438    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
4439    ///     `:etiquetas`).
4440    ///
4441    /// Past the empty arm the gate enforces the chart-maintainer-name
4442    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
4443    /// the structural single-line printable-UTF-8 floor every realistic
4444    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
4445    /// or trailing whitespace, no ASCII control characters anywhere,
4446    /// Unicode bytes accepted. Closes the canonical paste-from-doc
4447    /// footguns the bare empty + duplicate arms left open:
4448    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
4449    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
4450    /// pasted a multi-line block of author records into one `:autores`
4451    /// entry instead of splitting into one entry per author),
4452    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
4453    /// and the paste-from-binary-blob control bytes that would silently
4454    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
4455    /// `maintainers:` array. Mirrors the shape-predicate cascade
4456    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4457    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
4458    /// establish past their own empty arms on the sibling universal-axis
4459    /// `Option<String>` surfaces — the first universal-axis Vec<String>
4460    /// surface to land the empty-first-then-shape-then-duplicate per-entry
4461    /// cascade.
4462    ///
4463    /// Same empty-first cascade discipline every peer per-axis gate
4464    /// uses: the per-entry empty arm fires before the per-entry shape
4465    /// arm before the cross-entry duplicate arm. Walks the list in
4466    /// declaration order so the first-collision diagnostic surfaces the
4467    /// lexicographically-earliest offending position, peer with every
4468    /// other duplicate gate on this surface.
4469    ///
4470    /// Universal-axis (every kind carries `:autores`), so wired at the
4471    /// caixa-build gate alongside the peer universal gates
4472    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4473    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4474    /// [`Self::validate_code_paths`] — before the kind-coherence gates
4475    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4476    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4477    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4478    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4479    /// slot sets.
4480    pub fn validate_autores(&self) -> Result<(), ManifestError> {
4481        let mut seen = std::collections::HashSet::new();
4482        for autor in self.autores() {
4483            if autor.is_empty() {
4484                return Err(ManifestError::AutorEmpty);
4485            }
4486            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
4487                ManifestError::AutorInvalid {
4488                    autor: autor.clone(),
4489                    reason,
4490                }
4491            })?;
4492            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
4493                ManifestError::AutorDuplicate {
4494                    autor: autor.clone(),
4495                }
4496            })?;
4497        }
4498        Ok(())
4499    }
4500
4501    /// Reject `:repositorio` values whose shape the shared
4502    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
4503    /// `repositorio: Option<String>` slot on [`Caixa`] is the
4504    /// universal git-shaped homepage axis every kind carries — the
4505    /// substrate routes the same string through two load-bearing
4506    /// consumers:
4507    ///
4508    ///   - [`caixa-helm`] folds it verbatim into the rendered
4509    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
4510    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
4511    ///     the chart `README.md` `repo = …` interpolation
4512    ///     (`caixa-helm/src/lib.rs:359`).
4513    ///   - [`caixa-flux`] folds it verbatim into the standalone
4514    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
4515    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
4516    ///     `GitRepository.spec.url` the cluster's source-controller
4517    ///     polls — the load-bearing deploy-time axis.
4518    ///
4519    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4520    /// substitute a placeholder when the slot is absent (`None` → the
4521    /// fallback fires); a `Some("")` *skips the fallback* and silently
4522    /// passes the empty string through to `Chart.yaml home: ""` /
4523    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
4524    /// controller both reject the empty URL far from the source
4525    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
4526    /// Similarly a malformed `:repositorio` (whitespace, control char,
4527    /// missing `:` separator, leading `-`) silently lands in the
4528    /// rendered artifacts and breaks at `git clone` / `helm template`
4529    /// / `flux reconcile` time.
4530    ///
4531    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
4532    /// same shared predicate the peer [`crate::DepSource::validate`]
4533    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
4534    /// gate the two `git URL`-shaped surfaces on the typed Caixa
4535    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
4536    /// structurally equivalent: every value past validate is
4537    /// guaranteed-acceptable by the predicate's union of constraints
4538    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
4539    /// control chars, ASCII only, no leading `:`, contains a `:`
4540    /// separator). The predicate accepts every documented authoring
4541    /// shape — `github:org/repo` shorthand, `https://host/path`,
4542    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
4543    /// scp-style SSH, `file:///path` — and refuses the canonical
4544    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
4545    /// injection footguns at validate time. Maps the predicate's
4546    /// `String` reason verbatim into the
4547    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
4548    /// offending value + parser-shaped reason so the diagnostic is
4549    /// self-locating (the author can grep their `caixa.lisp` for
4550    /// `:repositorio "<value>"` and fix it in one edit).
4551    ///
4552    /// `None` (the canonical "omit the slot to express no published
4553    /// homepage" shape) is accepted trivially — the gate is a no-op
4554    /// when the author didn't declare a value. `Some("")` is gated by
4555    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
4556    /// shape predicate is consulted, mirroring the empty-first cascade
4557    /// every peer per-axis identity gate uses
4558    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
4559    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
4560    /// [`crate::DepError::FonteRepoEmpty`] →
4561    /// [`crate::DepError::FonteRepoInvalid`]).
4562    ///
4563    /// Universal-axis (every kind carries `:repositorio`), so wired at
4564    /// the caixa-build gate alongside the peer universal gates
4565    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4566    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4567    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
4568    /// before the kind-coherence gates
4569    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4570    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4571    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4572    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4573    /// specific slot sets.
4574    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
4575        let Some(s) = self.repositorio() else {
4576            return Ok(());
4577        };
4578        if s.is_empty() {
4579            return Err(ManifestError::RepositorioEmpty);
4580        }
4581        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
4582            repositorio: s.to_string(),
4583            reason,
4584        })
4585    }
4586
4587    /// Reject `:descricao` values that are the empty string. The flat
4588    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
4589    /// free-form-prose homepage axis every kind carries — the
4590    /// substrate routes the same string through two load-bearing
4591    /// consumers in the [`caixa-helm`] renderer:
4592    ///
4593    ///   - `build_chart_yaml` folds it verbatim into the rendered
4594    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
4595    ///     field (`caixa-helm/src/lib.rs:232-235`).
4596    ///   - `build_readme` folds it verbatim into the rendered chart
4597    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
4598    ///
4599    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4600    /// substitute a `caixa.nome`-derived placeholder when the slot is
4601    /// absent (`None` → the fallback fires); a `Some("")` *skips the
4602    /// fallback* and silently passes the empty string through to
4603    /// `Chart.yaml description: ""` / a blank chart `README.md`
4604    /// header. Helm's chart spec requires a non-empty `description:`
4605    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
4606    /// `WARNING [chart.metadata.description]: description is required`),
4607    /// so the empty `Some("")` silently lands in the rendered
4608    /// artifacts and breaks at `helm lint` / `helm install` time far
4609    /// from the source `caixa.lisp`, with no field naming the
4610    /// offending `:descricao`.
4611    ///
4612    /// `None` (the canonical "omit the slot to defer to the renderer's
4613    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
4614    /// the gate is a no-op when the author didn't declare a value.
4615    /// `Some("")` is gated by the narrower
4616    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
4617    /// shape every peer per-axis empty gate uses
4618    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4619    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4620    /// [`ManifestError::RepositorioEmpty`]).
4621    ///
4622    /// Universal-axis (every kind carries `:descricao`), so wired at
4623    /// the caixa-build gate alongside the peer universal gates
4624    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4625    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4626    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4627    /// [`Self::validate_code_paths`] — before the kind-coherence
4628    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4629    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4630    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4631    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4632    /// specific slot sets.
4633    ///
4634    /// Past the empty arm the gate enforces the chart-description
4635    /// shape predicate via [`crate::render::is_chart_description_shape`]:
4636    /// the structural single-line UTF-8 floor every realistic chart
4637    /// description in the wild matches — 1..=512 bytes, no leading
4638    /// or trailing whitespace, no ASCII control characters anywhere
4639    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
4640    /// carriage return, and every other control byte), Unicode
4641    /// continuation bytes accepted (the canonical fixtures carry
4642    /// `→` and `—`). Closes the canonical paste-from-doc footguns
4643    /// the bare empty-arm gate left open: paste-from-aligned-doc
4644    /// leading / trailing whitespace (`" Checkout flow."`,
4645    /// `"Checkout flow. "`), paste-from-multiline-doc newline
4646    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
4647    /// (`"Checkout\rflow."`), tab-from-aligned-doc
4648    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
4649    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
4650    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
4651    /// [`Self::validate_edicao`] establish past their own empty arms
4652    /// on the sibling universal-axis `Option<String>` Caixa-level
4653    /// value-shape surfaces.
4654    ///
4655    /// The empty-first cascade discipline mirrors every peer per-axis
4656    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
4657    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
4658    /// diagnostic surfaces on `Some("")` rather than the broader
4659    /// shape-predicate diagnostic — peer with how
4660    /// [`ManifestError::LicencaEmpty`] runs before
4661    /// [`ManifestError::LicencaInvalid`],
4662    /// [`ManifestError::EdicaoEmpty`] runs before
4663    /// [`ManifestError::EdicaoInvalid`],
4664    /// [`ManifestError::RepositorioEmpty`] runs before
4665    /// [`ManifestError::RepositorioInvalid`].
4666    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
4667        let Some(s) = self.descricao() else {
4668            return Ok(());
4669        };
4670        if s.is_empty() {
4671            return Err(ManifestError::DescricaoEmpty);
4672        }
4673        crate::render::is_chart_description_shape(s).map_err(|reason| {
4674            ManifestError::DescricaoInvalid {
4675                descricao: s.to_string(),
4676                reason,
4677            }
4678        })?;
4679        Ok(())
4680    }
4681
4682    /// Reject `:licenca` values that are the empty string. The flat
4683    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
4684    /// SPDX-shaped license-expression axis every kind carries — the
4685    /// substrate routes the same string through the [`caixa-helm`]
4686    /// renderer's `build_readme` which folds it verbatim into the
4687    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
4688    /// section (`caixa-helm/src/lib.rs:361`) via
4689    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
4690    /// fallback only fires on `None`; a `Some("")` *skips the
4691    /// fallback* and silently passes the empty string through to a
4692    /// chart `README.md` whose `License` section renders as the bare
4693    /// trailing period (`.\n`) — peer footgun with the
4694    /// `Some("")`-skips-`unwrap_or_else` shape the
4695    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
4696    /// gates close on the sibling free-form-prose and git-URL axes.
4697    ///
4698    /// `None` (the canonical "omit the slot to defer to the
4699    /// renderer's `MIT` fallback" shape every existing fixture
4700    /// carries) is accepted trivially — the gate is a no-op when the
4701    /// author didn't declare a value. `Some("")` is gated by the
4702    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
4703    /// empty-arm shape every peer per-axis empty gate uses
4704    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4705    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4706    /// [`ManifestError::RepositorioEmpty`],
4707    /// [`ManifestError::DescricaoEmpty`]).
4708    ///
4709    /// Universal-axis (every kind carries `:licenca`), so wired at
4710    /// the caixa-build gate alongside the peer universal gates
4711    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4712    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4713    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4714    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
4715    /// — before the kind-coherence gates
4716    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4717    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4718    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4719    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4720    /// specific slot sets.
4721    ///
4722    /// Past the empty arm the gate enforces the SPDX-expression shape
4723    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
4724    /// structural alphabet floor every realistic SPDX expression in
4725    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
4726    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
4727    /// single ASCII space (token separator). Closes the canonical
4728    /// paste-from-doc footguns the bare empty-arm gate left open:
4729    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
4730    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
4731    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
4732    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
4733    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
4734    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
4735    /// Apache-2.0"`), and semicolon-list-separator confusion
4736    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
4737    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
4738    /// establish past their own empty arms.
4739    ///
4740    /// The empty-first cascade discipline mirrors every peer per-axis
4741    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
4742    /// [`ManifestError::LicencaInvalid`], so the narrower empty
4743    /// diagnostic surfaces on `Some("")` rather than the broader
4744    /// shape-predicate diagnostic — peer with how
4745    /// [`ManifestError::EdicaoEmpty`] runs before
4746    /// [`ManifestError::EdicaoInvalid`],
4747    /// [`ManifestError::RepositorioEmpty`] runs before
4748    /// [`ManifestError::RepositorioInvalid`].
4749    ///
4750    /// A future tightening on this axis can extend the alphabet
4751    /// floor into a full SPDX expression parser + license-id
4752    /// allowlist (rejecting alphabet-valid values that don't name a
4753    /// real SPDX license identifier — e.g., `"NotAReal"` is
4754    /// alphabet-valid but no `NotAReal` license-id exists). That
4755    /// parser only becomes meaningful past a real SPDX-spec
4756    /// dependency; this gate establishes the structural floor by
4757    /// refusing every non-SPDX-alphabet value at validate time.
4758    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
4759        let Some(s) = self.licenca() else {
4760            return Ok(());
4761        };
4762        if s.is_empty() {
4763            return Err(ManifestError::LicencaEmpty);
4764        }
4765        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
4766            ManifestError::LicencaInvalid {
4767                licenca: s.to_string(),
4768                reason,
4769            }
4770        })?;
4771        Ok(())
4772    }
4773
4774    /// Reject `:edicao` values that are the empty string. The flat
4775    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
4776    /// language-edition axis every kind carries — it determines the
4777    /// tatara-lisp macro surface + compatibility flags the substrate
4778    /// applies when building a caixa, and lands verbatim in the
4779    /// `Caixa::template` author-time scaffold (the canonical
4780    /// `:edicao "2026"` line every `feira init` emits via
4781    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
4782    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
4783    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
4784    /// `caixa-core/src/render.rs:2510`) via
4785    /// `edicao: Some("2026".into())`.
4786    ///
4787    /// `None` (the canonical "omit the slot to defer to the
4788    /// substrate's default edition" shape every existing
4789    /// [`caixa-resolver`] integration test fixture carries via
4790    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
4791    /// is accepted trivially — the gate is a no-op when the author
4792    /// didn't declare a value. `Some("")` is gated by the narrower
4793    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
4794    /// shape every peer per-axis empty gate uses
4795    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4796    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4797    /// [`ManifestError::RepositorioEmpty`],
4798    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
4799    ///
4800    /// Universal-axis (every kind carries `:edicao`), so wired at
4801    /// the caixa-build gate alongside the peer universal gates
4802    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4803    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4804    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4805    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4806    /// [`Self::validate_code_paths`] — before the kind-coherence
4807    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4808    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4809    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4810    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4811    /// specific slot sets.
4812    ///
4813    /// Past the empty arm the gate enforces the canonical year-shape
4814    /// predicate: every documented tatara-lisp edition is a 4-digit
4815    /// ASCII decimal year (`"2026"` is the only edition currently
4816    /// minted; future-introduced siblings will follow the same
4817    /// shape, peer with Cargo's `[package] edition` grammar which
4818    /// every value Cargo has ever accepted matches — `"2015"`,
4819    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
4820    /// 4 ASCII decimal bytes is rejected with the narrower
4821    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
4822    /// shape-predicate cascade [`Self::validate_repositorio`]
4823    /// establishes past its own empty arm
4824    /// ([`ManifestError::RepositorioEmpty`] →
4825    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
4826    /// paste-from-doc footguns the bare empty-arm gate left open:
4827    ///
4828    ///   - leading / trailing whitespace from a paste-from-doc
4829    ///     (`"2026 "`, `" 2026"`)
4830    ///   - control characters / CRLF from a paste-from-multiline-doc
4831    ///     (`"2026\n"`)
4832    ///   - non-ASCII look-alikes from a fullwidth keyboard
4833    ///     (`"2026"`) which would silently land as a non-ASCII
4834    ///     string in the rendered caixa.lisp
4835    ///   - free-form non-year values (`"x"`, `"latest"`,
4836    ///     `"nightly"`) that have no operational meaning on the
4837    ///     substrate's build-time edition selector
4838    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
4839    ///     `"r2026"`) — common version-tag idioms that don't apply
4840    ///     to the year-shaped edition axis
4841    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
4842    ///     edition is a year, not a fractional version
4843    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
4844    ///     `"00026"`) that don't name a year
4845    ///
4846    /// `None` (the canonical "omit the slot to defer to the
4847    /// substrate's default edition" shape every existing
4848    /// [`caixa-resolver`] integration test fixture carries via
4849    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
4850    /// is accepted trivially — the gate is a no-op when the author
4851    /// didn't declare a value. The empty-first cascade discipline
4852    /// mirrors every peer per-axis identity gate:
4853    /// [`ManifestError::EdicaoEmpty`] runs before
4854    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
4855    /// diagnostic surfaces on `Some("")` rather than the broader
4856    /// shape-predicate diagnostic — peer with how
4857    /// [`ManifestError::NomeEmpty`] runs before
4858    /// [`ManifestError::NomeInvalid`],
4859    /// [`ManifestError::VersaoEmpty`] runs before
4860    /// [`ManifestError::VersaoInvalid`],
4861    /// [`ManifestError::RepositorioEmpty`] runs before
4862    /// [`ManifestError::RepositorioInvalid`].
4863    ///
4864    /// A future tightening on this axis can extend the shape
4865    /// predicate into a known-edition allowlist (rejecting
4866    /// year-shaped values that don't name a tatara-lisp edition
4867    /// the substrate actually understands — e.g., `"1999"` is
4868    /// year-shaped but no `1999` edition exists). That allowlist
4869    /// only becomes meaningful past the introduction of a sibling
4870    /// edition to `"2026"`; this gate establishes the structural
4871    /// floor by refusing every non-year-shaped value at validate
4872    /// time.
4873    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
4874        let Some(s) = self.edicao() else {
4875            return Ok(());
4876        };
4877        if s.is_empty() {
4878            return Err(ManifestError::EdicaoEmpty);
4879        }
4880        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
4881            return Err(ManifestError::EdicaoInvalid {
4882                edicao: s.to_string(),
4883                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
4884            });
4885        }
4886        Ok(())
4887    }
4888
4889    /// Compose the supervisor-related flat slots into a single
4890    /// [`SupervisorSpec`] for validation. Returns `None` when the
4891    /// caixa isn't a `:kind Supervisor`.
4892    ///
4893    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
4894    /// simple (one form, no nested `:supervisor (…)` block); this view
4895    /// is the "typed shape" the operator + supervisor reconciler
4896    /// consume.
4897    #[must_use]
4898    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
4899        if !self.kind().is_supervisor() {
4900            return None;
4901        }
4902        // Fold through the shared `supervisor::duration_codec::parse`
4903        // — the same parser the serde-routed `with = "duration_codec"`
4904        // on `SupervisorSpec::restart_window`, the `:politicas
4905        // :timeout` codec, and the `:politicas :circuit-breaker
4906        // :window` codec all consume. The prior inline f64-shaped
4907        // duplicate (`parse_window_inline`) admitted every magnitude
4908        // the integer-magnitude gate (1c55a2a) rejects on the three
4909        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
4910        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
4911        // `None` (i.e. "no reset"), divergent from the shared codec's
4912        // integer-magnitude discipline by construction. The fold
4913        // closes the divergence: every value the typed
4914        // `SupervisorSpec` carries past `supervisor_view` is in the
4915        // shared codec's accepted set. The `.ok()` here preserves the
4916        // existing soft-swallow shape on this view-construction path;
4917        // the new [`Caixa::validate_restart_window`] (sibling of
4918        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
4919        // the offending raw string at build time so authoring tools
4920        // (`feira lint`, the future layout-side wire-up) surface a
4921        // self-locating diagnostic instead of a silently dropped
4922        // window.
4923        let restart_window = self
4924            .restart_window()
4925            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
4926        Some(SupervisorSpec {
4927            estrategia: self.estrategia().unwrap_or_default(),
4928            max_restarts: self.max_restarts().unwrap_or(5),
4929            restart_window,
4930            children: self.children().to_vec(),
4931        })
4932    }
4933
4934    /// A minimal starter manifest emitted by `feira init`.
4935    #[must_use]
4936    pub fn template(nome: &str) -> String {
4937        format!(
4938            "(defcaixa\n  \
4939               :nome        {nome:?}\n  \
4940               :versao      \"0.1.0\"\n  \
4941               :kind        Biblioteca\n  \
4942               :edicao      \"2026\"\n  \
4943               :descricao   \"FIXME — describe this caixa\"\n  \
4944               :autores     ()\n  \
4945               :etiquetas   ()\n  \
4946               :deps        ()\n  \
4947               :deps-dev    ()\n  \
4948               :bibliotecas (\"lib/{nome}.lisp\"))\n"
4949        )
4950    }
4951
4952    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
4953    /// back after mutation (e.g. `feira add`).
4954    ///
4955    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
4956    /// The derive-macro `compile_from_sexp` path is the inverse, so any
4957    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
4958    #[must_use]
4959    pub fn to_lisp(&self) -> String {
4960        let json = serde_json::to_value(self).expect("Caixa serialize");
4961        let sexp = tatara_lisp::domain::json_to_sexp(&json);
4962        let tatara_lisp::Sexp::List(items) = sexp else {
4963            return format!("(defcaixa {sexp})\n");
4964        };
4965        let mut out = String::from("(defcaixa");
4966        let mut i = 0;
4967        while i + 1 < items.len() {
4968            out.push_str("\n  ");
4969            out.push_str(&items[i].to_string());
4970            out.push(' ');
4971            out.push_str(&items[i + 1].to_string());
4972            i += 2;
4973        }
4974        out.push_str(")\n");
4975        out
4976    }
4977}
4978
4979/// Errors raised by top-level [`Caixa`] validators that don't fit
4980/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
4981/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
4982/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
4983/// through every substrate-side artifact's `metadata.name` /
4984/// version derivation.
4985///
4986/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
4987/// doc-comment anticipates) can hold one of each per-axis error
4988/// family without reshaping individual diagnostics; this enum is
4989/// the first such per-Caixa-identity family.
4990#[derive(Debug, Error, PartialEq, Eq)]
4991pub enum ManifestError {
4992    #[error(
4993        ":nome is empty (every caixa must name itself; the value flows \
4994         into every K8s artifact's `metadata.name` derivation and into \
4995         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
4996    )]
4997    NomeEmpty,
4998    #[error(
4999        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
5000         apiserver enforces this rule on every `metadata.name` the \
5001         caixa's substrate-side renderers derive from `:nome` — the \
5002         `lareira-<nome>` Helm chart name, the programs.yaml entry \
5003         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
5004         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
5005         name; use a lowercase alphanumeric + hyphen identifier like \
5006         `\"checkout\"` or `\"cart-v2\"`)"
5007    )]
5008    NomeInvalid { nome: String, reason: String },
5009    #[error(
5010        ":nome {nome:?} overflows the joint-length budget on the canonical \
5011         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
5012         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
5013         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
5014         `chart:` slot, `caixa-tatara`'s `release_name` + \
5015         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
5016         joint name through the canonical `lareira_chart_name` helper, and \
5017         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
5018         DNS-1123 label cap on every chart-name-derived `metadata.name` \
5019         reject any joint name exceeding 63 bytes; the narrower \
5020         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
5021         arm gates the chart-name budget downstream renderers inherit)"
5022    )]
5023    NomeChartNameBudgetExceeded { nome: String, reason: String },
5024    #[error(
5025        ":versao is empty (every caixa must pin its own version; the value flows \
5026         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
5027         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
5028         `:latest` tags, the lacre closure's `concrete_versao`, and the \
5029         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
5030    )]
5031    VersaoEmpty,
5032    #[error(
5033        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
5034         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
5035         with optional `-prerelease` and `+build` — across every artifact derived \
5036         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
5037         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
5038         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
5039         and the `:upgrade-from :from` peers that match against this exact shape; \
5040         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
5041         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
5042         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
5043    )]
5044    VersaoInvalid { versao: String, reason: String },
5045    #[error(
5046        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
5047         substrate consumes this string through the shared \
5048         `supervisor::duration_codec` — the same parser routed via `with = \
5049         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
5050         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
5051         the canonical authoring form is `<integer><unit>` where the unit is one \
5052         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
5053         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
5054         Without this gate a malformed `:restart-window` silently produced a \
5055         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
5056         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
5057         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
5058         layer with the offending value named verbatim. Omit the slot entirely to \
5059         express \"no reset\"; carry a positive integer duration to express the \
5060         sliding window)"
5061    )]
5062    RestartWindowMalformed {
5063        restart_window: String,
5064        reason: String,
5065    },
5066    #[error(
5067        "{slot} entry is an empty path string — every {slot} entry must name \
5068         a file relative to the caixa root; omit the entry to omit the file \
5069         (the layout checker's `root.join(\"\")` resolves to the caixa root \
5070         itself, so an empty entry silently aliases the project root as a \
5071         declared {slot} file, then fails downstream at parse / existence \
5072         time with a diagnostic that names the root rather than the offending \
5073         entry)"
5074    )]
5075    CodePathEmpty { slot: &'static str },
5076    #[error(
5077        "{slot} entry {} is an absolute path — entries must be relative to \
5078         the caixa root, since `Path::join` replaces the base with an absolute \
5079         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
5080         outside the caixa root sandbox; rewrite the entry as a relative path \
5081         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
5082         `\"servicos/<name>.computeunit.yaml\"`)",
5083        path.display()
5084    )]
5085    CodePathAbsolute { slot: &'static str, path: PathBuf },
5086    #[error(
5087        "{slot} entry {} contains a `..` component — entries must not traverse \
5088         above the caixa root (the layout's `starts_with(<dir>)` fence on \
5089         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
5090         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
5091         has no such fence, so a leading `..` escapes unconditionally if the \
5092         resolved target happens to exist)",
5093        path.display()
5094    )]
5095    CodePathParentEscape { slot: &'static str, path: PathBuf },
5096    #[error(
5097        "{slot} entry {} does not terminate in the `.lisp` extension — every \
5098         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
5099         loop reads through `tatara_lisp::read` at parse time, so any other \
5100         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
5101         structurally a parser error far from the source caixa.lisp, with \
5102         no field naming the offending `:bibliotecas` entry. Pin a relative \
5103         path under the caixa root whose terminating extension is \
5104         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
5105         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
5106         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
5107         (33cc830) axes already carry through the same lifted \
5108         `is_lisp_extension` predicate",
5109        path.display()
5110    )]
5111    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
5112    #[error(
5113        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
5114         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
5115         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
5116         through `serde_yaml::from_str` at chart / FluxCD bundle render \
5117         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
5118         off-by-one-segment `.computeunit-yaml`, the editor-backup \
5119         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
5120         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
5121         source caixa.lisp, with no field naming the offending `:servicos` \
5122         entry. Pin a relative path under the caixa root whose terminating \
5123         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
5124         `\"servicos/<name>.computeunit.yaml\"`, \
5125         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
5126         contract the sibling `:bibliotecas` axis (64772a9) already carries \
5127         on the tatara-lisp-source axis through the peer lifted \
5128         `is_lisp_extension` predicate, here on the compound-suffix axis \
5129         `Path::extension` can't express on its own through the lifted \
5130         `is_computeunit_yaml_extension` predicate",
5131        path.display()
5132    )]
5133    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
5134    #[error(
5135        "{slot} entry {} appears more than once (the code-path list is \
5136         a set, not a multiset; every peer Vec-shaped author-supplied \
5137         list past validate is set-not-multiset — `:membros :caixa`, \
5138         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5139         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
5140         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
5141         code-path lists are the last Vec-shaped author-supplied slots on \
5142         the typed Caixa surface still admitting a duplicate entry. \
5143         `:bibliotecas` duplicates re-parse the same file at \
5144         `feira build` time and silently mask the author's intent to \
5145         declare a *second* biblioteca; `:exe` duplicates collide on the \
5146         flake `packages.<name>` derivation key at the future \
5147         `caixa-flake` materializer; `:servicos` duplicates surface as the \
5148         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
5149         rejection far from the source `caixa.lisp`. Drop the duplicate \
5150         or rename it to the actual second file intended)",
5151        path.display()
5152    )]
5153    CodePathDuplicate { slot: &'static str, path: PathBuf },
5154    #[error(
5155        ":etiquetas entry is empty (every tag must carry a non-empty \
5156         registry-search identifier; the empty entry has no operational \
5157         meaning — it indexes nothing in the future caixa-registry search \
5158         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
5159         with a no-op tag; omit the entry to express \"no tag on this \
5160         position\")"
5161    )]
5162    EtiquetaEmpty,
5163    #[error(
5164        ":etiquetas entry {etiqueta:?} appears more than once (the \
5165         registry-search tag set is a set, not a multiset; duplicate \
5166         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
5167         at chart render — a \"second wins / one silently disappears\" \
5168         shape divergent from every peer typed-graph set gate \
5169         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
5170         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
5171         duplicate or rename it to the actual tag intended)"
5172    )]
5173    EtiquetaDuplicate { etiqueta: String },
5174    #[error(
5175        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
5176         {reason} (the substrate consumes this string through the shared \
5177         `crate::render::is_chart_keyword_shape` predicate — the same \
5178         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
5179         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
5180         continuation. The canonical authoring shapes are short kebab-case \
5181         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
5182         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
5183         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
5184         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
5185         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
5186         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
5187         `\"mesh,http,grpc\"` — the author meant to author three separate \
5188         list entries; path-separator confusion `\"caixa/servico\"`; \
5189         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
5190         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
5191         `\"café\"` — every legitimate search tag is strict ASCII; \
5192         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
5193         passed `from_lisp` + `validate_etiquetas` + \
5194         `StandardLayout::verify` and landed in the rendered \
5195         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
5196         malformed search tag — Artifact Hub's keyword index + the future \
5197         caixa-registry's keyword index would either silently drop the \
5198         tag or fail to index it far from the source caixa.lisp; the gate \
5199         moves the diagnostic to the manifest layer with the offending \
5200         value named verbatim)"
5201    )]
5202    EtiquetaInvalid { etiqueta: String, reason: String },
5203    #[error(
5204        ":autores entry is empty (every maintainer must carry a non-empty \
5205         identifier; the empty entry has no operational meaning — it \
5206         identifies no one in the substrate's authorship index and renders \
5207         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
5208         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
5209         omit the entry to express \"no maintainer on this position\")"
5210    )]
5211    AutorEmpty,
5212    #[error(
5213        ":autores entry {autor:?} appears more than once (the maintainer \
5214         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
5215         `maintainers:` rendering does *no* dedup — duplicate entries \
5216         stack verbatim in `Chart.yaml` as two identical \
5217         `Maintainer {{ name, email: None }}` records, divergent from every \
5218         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
5219         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5220         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
5221         rename it to the actual author intended)"
5222    )]
5223    AutorDuplicate { autor: String },
5224    #[error(
5225        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
5226         {reason} (the substrate consumes this string through the shared \
5227         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
5228         single-line-UTF-8 floor every realistic chart maintainer name carries: \
5229         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
5230         characters anywhere, Unicode bytes accepted. The canonical authoring \
5231         shapes are short single-line identifiers like `\"pleme-io\"`, \
5232         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
5233         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
5234         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
5235         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
5236         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
5237         records into one entry instead of splitting into one entry per author; \
5238         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
5239         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
5240         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5241         `validate_autores` + `StandardLayout::verify` and landed in the \
5242         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
5243         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
5244         round-trip — every chart-aware UI (`helm list`, `helm search`, \
5245         Artifact Hub maintainer index) would render the maintainer name in a \
5246         single-line column far from the source caixa.lisp; the gate moves the \
5247         diagnostic to the manifest layer with the offending value named \
5248         verbatim)"
5249    )]
5250    AutorInvalid { autor: String, reason: String },
5251    #[error(
5252        ":repositorio is the empty string (every published caixa names its \
5253         git source via a non-empty `:repositorio` locator — the value \
5254         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
5255         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
5256         `GitRepository.spec.url` via `caixa-flux`'s \
5257         `ClusterBundleOpts::for_caixa`; both consumers' \
5258         `Option::unwrap_or_else` fallbacks only fire when the slot is \
5259         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
5260         `url: \"\"` in the rendered artifacts and breaks at `helm \
5261         template` / FluxCD source-controller reconcile time far from the \
5262         source caixa.lisp; omit the slot entirely to defer to the \
5263         renderer's `https://github.com/pleme-io/<nome>` / \
5264         `caixa.nome`-derived fallback, or carry a canonical authoring \
5265         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
5266         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
5267         `\"file:///path\"`)"
5268    )]
5269    RepositorioEmpty,
5270    #[error(
5271        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
5272         (the substrate consumes this string through the shared \
5273         `crate::render::is_git_repo_url` predicate — the same parser the \
5274         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
5275         value through via `DepSource::validate`; the canonical authoring \
5276         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
5277         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
5278         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
5279         scp-style SSH form. Without this gate a malformed `:repositorio` \
5280         (whitespace from a paste-from-doc; control characters / CRLF \
5281         from a paste-from-multiline-doc; a leading `-` from a \
5282         CLI-argument-injection footgun; a missing `:` separator from a \
5283         bare `org/repo` shape git treats as a relative filesystem path) \
5284         silently landed in the rendered `Chart.yaml home:` and the \
5285         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
5286         FluxCD reconcile time far from the source caixa.lisp; the gate \
5287         moves the diagnostic to the manifest layer with the offending \
5288         value named verbatim)"
5289    )]
5290    RepositorioInvalid { repositorio: String, reason: String },
5291    #[error(
5292        ":descricao is the empty string (every published caixa names \
5293         its purpose via a non-empty `:descricao` summary — the value \
5294         flows verbatim into the rendered `lareira-<nome>` Helm \
5295         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
5296         `build_chart_yaml` and into the chart `README.md` header via \
5297         `build_readme`; both consumers' `Option::unwrap_or_else` \
5298         `caixa.nome`-derived fallbacks only fire when the slot is \
5299         `None`, so an empty `Some(\"\")` silently lands as \
5300         `description: \"\"` / a blank `README.md` header in the \
5301         rendered artifacts and breaks at `helm lint` time \
5302         (`WARNING [chart.metadata.description]: description is \
5303         required` on `apiVersion: v2` charts) far from the source \
5304         caixa.lisp; omit the slot entirely to defer to the \
5305         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
5306         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
5307         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
5308         Servico.\"`)"
5309    )]
5310    DescricaoEmpty,
5311    #[error(
5312        ":descricao {descricao:?} is not a valid chart-description shape: \
5313         {reason} (the substrate consumes this string through the shared \
5314         `crate::render::is_chart_description_shape` predicate — the same \
5315         single-line-UTF-8 floor every realistic chart description carries: \
5316         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
5317         characters anywhere, Unicode prose bytes accepted. The canonical \
5318         authoring shapes are short single-line summaries like `\"Canonical \
5319         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
5320         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
5321         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
5322         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
5323         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
5324         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
5325         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
5326         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5327         `validate_descricao` + `StandardLayout::verify` and landed in the \
5328         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
5329         field + `README.md` header paragraph as a YAML-illegal multi-line \
5330         scalar or a silently-trimmed whitespace round-trip — every \
5331         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
5332         render the description in a single-line column far from the source \
5333         caixa.lisp; the gate moves the diagnostic to the manifest layer \
5334         with the offending value named verbatim)"
5335    )]
5336    DescricaoInvalid { descricao: String, reason: String },
5337    #[error(
5338        ":licenca is the empty string (every published caixa names \
5339         its license via a non-empty `:licenca` SPDX expression — the \
5340         value flows verbatim into the rendered `lareira-<nome>` Helm \
5341         chart's `README.md` `## License` section via `caixa-helm`'s \
5342         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
5343         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
5344         only fires when the slot is `None`, so an empty `Some(\"\")` \
5345         silently lands as a bare trailing period in the rendered \
5346         chart `README.md` `License` section far from the source \
5347         caixa.lisp; omit the slot entirely to defer to the \
5348         renderer's `MIT` fallback, or carry a canonical SPDX \
5349         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
5350         `\"Apache-2.0 OR MIT\"`)"
5351    )]
5352    LicencaEmpty,
5353    #[error(
5354        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
5355         (the substrate consumes this string through the shared \
5356         `crate::render::is_spdx_expression_shape` predicate — the same \
5357         alphabet-floor parser every peer per-axis value-shape gate routes \
5358         its value through; the canonical authoring shapes are single \
5359         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
5360         compound expressions like `\"Apache-2.0 OR MIT\"`, \
5361         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
5362         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
5363         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
5364         like `\"LicenseRef-MyLicense\"` / \
5365         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
5366         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
5367         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
5368         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
5369         a smart-quote paste; underscore-instead-of-hyphen typo \
5370         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
5371         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
5372         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
5373         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
5374         `README.md` `## License` section + a future SPDX-aware \
5375         `Chart.yaml license:` emitter would refuse the value at \
5376         `helm lint` time far from the source caixa.lisp; the gate moves \
5377         the diagnostic to the manifest layer with the offending value \
5378         named verbatim)"
5379    )]
5380    LicencaInvalid { licenca: String, reason: String },
5381    #[error(
5382        ":edicao is the empty string (every published caixa names \
5383         its language edition via a non-empty `:edicao` value — the \
5384         edition determines the tatara-lisp macro surface + \
5385         compatibility flags the substrate applies when building \
5386         the caixa; the canonical `Caixa::template` scaffold every \
5387         `feira init` emits carries `:edicao \"2026\"` verbatim and \
5388         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
5389         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
5390         construction, so an empty `Some(\"\")` silently lands as a \
5391         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
5392         a future renderer-side consumer that folds it through \
5393         `Option::unwrap_or_else` will skip the fallback and pass the \
5394         empty edition through to the substrate's build-time edition \
5395         selector far from the source caixa.lisp; omit the slot \
5396         entirely to defer to the substrate's default edition, or \
5397         carry a canonical edition like `\"2026\"`)"
5398    )]
5399    EdicaoEmpty,
5400    #[error(
5401        ":edicao {edicao:?} is not a valid edition: {reason} (every \
5402         documented tatara-lisp edition is a 4-digit ASCII decimal \
5403         year — `\"2026\"` is the only edition currently minted; \
5404         future-introduced siblings will follow the same shape, peer \
5405         with Cargo's `[package] edition` grammar which every value \
5406         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
5407         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
5408         paste-from-doc footguns silently passed: a trailing space \
5409         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
5410         from a paste-from-multiline-doc, a fullwidth-keyboard \
5411         look-alike (`\"2026\"`), a free-form non-year value \
5412         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
5413         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
5414         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
5415         wrong-length numeric value (`\"26\"`, `\"202\"`, \
5416         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
5417         rendered caixa.lisp and broke at the substrate's \
5418         build-time edition selector far from the source caixa.lisp; \
5419         omit the slot entirely to defer to the substrate's default \
5420         edition, or carry a canonical 4-digit ASCII decimal year \
5421         like `\"2026\"`)"
5422    )]
5423    EdicaoInvalid { edicao: String, reason: String },
5424}
5425
5426#[cfg(test)]
5427mod tests {
5428    use super::*;
5429
5430    #[test]
5431    fn template_round_trips() {
5432        let src = Caixa::template("demo");
5433        let c = Caixa::from_lisp(&src).expect("template must parse");
5434        assert_eq!(c.nome, "demo");
5435        assert_eq!(c.versao, "0.1.0");
5436        assert_eq!(c.kind, CaixaKind::Biblioteca);
5437        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
5438        assert!(c.deps.is_empty());
5439        assert!(c.deps_dev.is_empty());
5440    }
5441
5442    #[test]
5443    fn register_populates_registry() {
5444        Caixa::register();
5445        let kws = tatara_lisp::domain::registered_keywords();
5446        assert!(kws.contains(&"defcaixa"));
5447    }
5448
5449    #[test]
5450    fn to_lisp_round_trips() {
5451        let src = Caixa::template("demo");
5452        let c1 = Caixa::from_lisp(&src).unwrap();
5453        let emitted = c1.to_lisp();
5454        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
5455        assert_eq!(c1, c2);
5456    }
5457
5458    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
5459    //
5460    // The compounding pin: the variant stores only the typed
5461    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
5462    // (canonical keyword, description, consumer) routes through the enum's
5463    // own accessors at Display time. Prior to that closure the variant
5464    // carried each accessor's return value as a stored `&'static str`
5465    // snapshot alongside `dialeto`; a caller could construct the variant
5466    // with a snapshot that drifted from what `dialeto`'s accessors would
5467    // return, and every downstream user-facing projection would silently
5468    // disagree with the classification. Storing only the axis makes the
5469    // drift structurally impossible.
5470
5471    #[test]
5472    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
5473        // Single-field construction is the whole compounding shape — a
5474        // future re-introduction of a snapshot field (a `palavra_canonica:
5475        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
5476        // would re-open the drift surface and this construction would fail
5477        // to compile with "missing field" until every snapshot was seeded
5478        // at the call site again. The compile-time guarantee is the
5479        // invariant; the assertion below only witnesses that the
5480        // construction is well-formed after the closure.
5481        let err = LeituraError::DialetoEstrangeiro {
5482            dialeto: crate::dialeto::CaixaDialeto::Molde,
5483        };
5484        assert!(matches!(
5485            err,
5486            LeituraError::DialetoEstrangeiro {
5487                dialeto: crate::dialeto::CaixaDialeto::Molde,
5488            }
5489        ));
5490    }
5491
5492    #[test]
5493    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
5494        // For every foreign-dialect classification the variant surfaces —
5495        // [`crate::dialeto::CaixaDialeto::Molde`] and
5496        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
5497        // variants [`Caixa::from_lisp`] raises this error for — the
5498        // rendered [`std::fmt::Display`] byte-string must interpolate each
5499        // typed accessor's return verbatim. A future re-introduction of a
5500        // stored `&'static str` snapshot alongside `dialeto` that Display
5501        // read instead of the accessor would fail this pin as soon as the
5502        // two disagreed; a future accessor rebrand (a per-dialect
5503        // consumer rename, a canonical-keyword shift once the substrate
5504        // migration named in [`crate::dialeto`] completes) reaches every
5505        // consumer through one typed dispatch and this pin verifies the
5506        // display path is one of them.
5507        for d in [
5508            crate::dialeto::CaixaDialeto::Molde,
5509            crate::dialeto::CaixaDialeto::MoldePosicional,
5510        ] {
5511            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
5512            assert!(
5513                rendered.contains(d.palavra_canonica()),
5514                "Display must interpolate `dialeto.palavra_canonica()` \
5515                 verbatim — a stored snapshot would silently drift from \
5516                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
5517            );
5518            assert!(
5519                rendered.contains(d.descricao()),
5520                "Display must interpolate `dialeto.descricao()` verbatim. \
5521                 dialect: {d}, rendered: {rendered:?}"
5522            );
5523            assert!(
5524                rendered.contains(d.consumidor()),
5525                "Display must interpolate `dialeto.consumidor()` verbatim. \
5526                 dialect: {d}, rendered: {rendered:?}"
5527            );
5528        }
5529    }
5530
5531    #[test]
5532    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
5533        // The end-to-end pin the compounding closure defends: a
5534        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
5535        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
5536        // rendered Display byte-string names the Molde accessors'
5537        // returns verbatim. Any future path that constructed the variant
5538        // with a mismatched snapshot (a stored `palavra_canonica:
5539        // "defcaixa"` on a `Molde` classification) would land Display
5540        // pointing at `defcaixa` while the typed axis said `Molde` — the
5541        // exact drift the closure removes.
5542        let src = r#"
5543          (defcaixa
5544            :name "x"
5545            :kind :Biblioteca
5546            :ecosystem :rust-single-crate
5547            :package {:name "x" :version "0.1.0"})
5548        "#;
5549        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
5550        match err {
5551            LeituraError::DialetoEstrangeiro { dialeto } => {
5552                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
5553                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5554                assert!(rendered.contains(dialeto.palavra_canonica()));
5555                assert!(rendered.contains(dialeto.consumidor()));
5556                assert!(rendered.contains(dialeto.descricao()));
5557            }
5558            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
5559        }
5560    }
5561
5562    #[test]
5563    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
5564        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
5565        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
5566        // positional-arity `defmolde` form written under a `(defcaixa …)`
5567        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
5568        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
5569        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
5570        // so no test exercised the positional-arity path through
5571        // `Caixa::from_lisp` specifically; the sibling
5572        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
5573        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
5574        // two arms route through the lifted
5575        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
5576        // typed predicate — the same predicate the pre-lift `foreign =>`
5577        // wildcard resolved to today — and this pin makes the
5578        // positional-arity arm's byte-shape at the gate explicit rather
5579        // than implied by wildcard-absorption. A future regression that
5580        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
5581        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
5582        // from the two-arity closure) would fail this pin at caixa-core
5583        // test time rather than surfacing far from the change as a
5584        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
5585        // …)` silently parsing past the derive.
5586        let src = r#"
5587          (defcaixa todoku-go
5588            :kind :Biblioteca
5589            :ecosystem :go
5590            :package {:name "todoku-go" :version "0.3.0"})
5591        "#;
5592        let err =
5593            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
5594        match err {
5595            LeituraError::DialetoEstrangeiro { dialeto } => {
5596                assert_eq!(
5597                    dialeto,
5598                    crate::dialeto::CaixaDialeto::MoldePosicional,
5599                    "DialetoEstrangeiro must carry the MoldePosicional \
5600                     variant verbatim — the positional-arity `defmolde` \
5601                     form under a `(defcaixa …)` head is the \
5602                     `MoldePosicional` arm's canonical byte-shape"
5603                );
5604                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5605                assert!(
5606                    rendered.contains(dialeto.palavra_canonica()),
5607                    "Display must interpolate `dialeto.palavra_canonica()` \
5608                     verbatim on the MoldePosicional arm; rendered: \
5609                     {rendered:?}"
5610                );
5611                assert!(
5612                    rendered.contains(dialeto.consumidor()),
5613                    "Display must interpolate `dialeto.consumidor()` \
5614                     verbatim on the MoldePosicional arm; rendered: \
5615                     {rendered:?}"
5616                );
5617                assert!(
5618                    rendered.contains(dialeto.descricao()),
5619                    "Display must interpolate `dialeto.descricao()` \
5620                     verbatim on the MoldePosicional arm; rendered: \
5621                     {rendered:?}"
5622                );
5623            }
5624            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
5625        }
5626    }
5627
5628    #[test]
5629    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
5630        // Load-bearing byte-parity pin: for every arm in
5631        // [`crate::dialeto::CaixaDialeto::ALL`], the
5632        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
5633        // partition must agree with the lifted
5634        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
5635        // typed predicate — i.e. from_lisp raises
5636        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
5637        // `d.is_molde_family()` returns `true`, and does NOT raise
5638        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
5639        // predicate returns `false` (the arm's source falls through to
5640        // the derive — parses cleanly on
5641        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
5642        // [`LeituraError::Leitura`] on
5643        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
5644        //
5645        // Pre-lift the gate hand-rolled a three-arm match
5646        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
5647        // whose `foreign =>` wildcard expressed no compile-time link
5648        // back to the substrate primitive's arm-family; a future fifth
5649        // dialect the [`crate::dialeto`] module doc's "third dialect"
5650        // hazard actualises would fall silently onto the wildcard
5651        // regardless of whether it belonged to the `defmolde` family or
5652        // to a distinct `defcaixa`-family. Post-lift the partition
5653        // resolves through
5654        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
5655        // typed dispatch, and this pin refuses any future regression
5656        // that silently split the from_lisp partition from the typed
5657        // predicate — the two paths now migrate as one on any future
5658        // arm addition.
5659        //
5660        // Sibling in shape to the peer
5661        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
5662        // (e9d2315) that pins the same byte-parity between
5663        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
5664        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
5665        // `== "defmolde"` classifier — extends the discipline from the
5666        // two paths within the [`crate::dialeto`] primitive onto the
5667        // third external consumer of the `defmolde`-family partition
5668        // (the [`Caixa::from_lisp`] gate that raises
5669        // [`LeituraError::DialetoEstrangeiro`]).
5670        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
5671            (
5672                crate::dialeto::CaixaDialeto::Pacote,
5673                r#"
5674                  (defcaixa
5675                    :nome   "checkout"
5676                    :versao "0.1.0"
5677                    :kind   Biblioteca
5678                    :edicao "2026"
5679                    :descricao "canonical Pacote source"
5680                    :autores ()
5681                    :etiquetas ()
5682                    :deps ()
5683                    :deps-dev ()
5684                    :bibliotecas ("lib/checkout.lisp"))
5685                "#,
5686            ),
5687            (
5688                crate::dialeto::CaixaDialeto::Molde,
5689                r#"
5690                  (defcaixa
5691                    :name "base64"
5692                    :kind :Biblioteca
5693                    :ecosystem :rust-single-crate
5694                    :package {:name "base64" :version "0.22.1"}
5695                    :workflows [:auto-release])
5696                "#,
5697            ),
5698            (
5699                crate::dialeto::CaixaDialeto::MoldePosicional,
5700                r#"
5701                  (defcaixa todoku-go
5702                    :kind :Biblioteca
5703                    :ecosystem :go
5704                    :package {:name "todoku-go" :version "0.3.0"})
5705                "#,
5706            ),
5707            (
5708                crate::dialeto::CaixaDialeto::Desconhecido,
5709                r#"(defcaixa :licenca "MIT")"#,
5710            ),
5711        ];
5712
5713        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
5714        // must appear in the fixture table so the pin's arm-set stays
5715        // synchronised with the enum's arm-set. Fails at test time if a
5716        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
5717        // (with a corresponding `is_molde_family` return) forgot to
5718        // extend this fixture table with a canonical source for the new
5719        // arm — the pin cannot cover an arm it has no source for.
5720        for &expected in crate::dialeto::CaixaDialeto::ALL {
5721            assert!(
5722                fixtures.iter().any(|(d, _)| *d == expected),
5723                "fixture table must carry a canonical source for every \
5724                 CaixaDialeto arm; missing: {expected:?}"
5725            );
5726        }
5727
5728        for &(expected_dialect, src) in fixtures {
5729            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
5730                panic!(
5731                    "fixture source for {expected_dialect:?} must classify \
5732                     cleanly, got err: {err:?}"
5733                )
5734            });
5735            assert_eq!(
5736                classified, expected_dialect,
5737                "fixture source for {expected_dialect:?} must classify as \
5738                 {expected_dialect:?} (drift here defeats the byte-parity \
5739                 pin below — a source labelled for one arm but classifying \
5740                 as another would silently satisfy or violate the pin for \
5741                 the wrong reason)"
5742            );
5743
5744            let outcome = Caixa::from_lisp(src);
5745            match (expected_dialect.is_molde_family(), &outcome) {
5746                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
5747                    assert_eq!(
5748                        *dialeto, expected_dialect,
5749                        "DialetoEstrangeiro must carry the same typed arm \
5750                         the classifier returned — a drift here would let \
5751                         from_lisp raise the error while pointing at the \
5752                         wrong dialect (e.g. rejecting a \
5753                         MoldePosicional source as Molde). arm: \
5754                         {expected_dialect:?}"
5755                    );
5756                }
5757                (true, other) => panic!(
5758                    "arm {expected_dialect:?} has is_molde_family() = true \
5759                     so from_lisp must raise DialetoEstrangeiro carrying \
5760                     {expected_dialect:?}; got: {other:?}"
5761                ),
5762                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
5763                    "arm {expected_dialect:?} has is_molde_family() = false \
5764                     so from_lisp must NOT raise DialetoEstrangeiro; got \
5765                     one carrying: {dialeto:?}. This means the typed \
5766                     predicate and the from_lisp partition disagree on \
5767                     this arm — exactly the drift this pin refuses."
5768                ),
5769                (false, _) => {
5770                    // A non-molde arm's source falls through to the
5771                    // derive: Pacote sources parse to Ok(_); Desconhecido
5772                    // sources surface as LeituraError::Leitura from the
5773                    // derive's own unknown-keyword rejection. Either
5774                    // shape is acceptable here — the pin's promise is
5775                    // narrower: "no DialetoEstrangeiro on
5776                    // is_molde_family() == false".
5777                }
5778            }
5779        }
5780    }
5781
5782    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
5783
5784    #[test]
5785    fn limits_round_trip_via_json() {
5786        use crate::LimitsSpec;
5787        use std::time::Duration;
5788        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5789        c.limits = Some(LimitsSpec {
5790            memory: Some(64 * 1024 * 1024),
5791            fuel: Some(1_000_000),
5792            wall_clock: Some(Duration::from_secs(30)),
5793            cpu: Some(500),
5794        });
5795        let json = serde_json::to_string(&c).unwrap();
5796        assert!(json.contains("\"limits\""));
5797        assert!(json.contains("\"64MiB\""));
5798        assert!(json.contains("\"30s\""));
5799        assert!(json.contains("\"500m\""));
5800        let back: Caixa = serde_json::from_str(&json).unwrap();
5801        assert_eq!(c.limits, back.limits);
5802    }
5803
5804    #[test]
5805    fn behavior_round_trip_via_json() {
5806        use crate::BehaviorSpec;
5807        use std::path::PathBuf;
5808        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5809        c.behavior = Some(BehaviorSpec {
5810            on_init: Some(PathBuf::from("lib/init.lisp")),
5811            on_call: Some(PathBuf::from("lib/handlers.lisp")),
5812            ..Default::default()
5813        });
5814        let json = serde_json::to_string(&c).unwrap();
5815        let back: Caixa = serde_json::from_str(&json).unwrap();
5816        assert_eq!(c.behavior, back.behavior);
5817    }
5818
5819    #[test]
5820    fn upgrade_from_round_trip_via_json() {
5821        use crate::{UpgradeFromEntry, UpgradeInstruction};
5822        use std::path::PathBuf;
5823        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5824        c.upgrade_from = vec![UpgradeFromEntry {
5825            from: "0.1.0".into(),
5826            instructions: vec![
5827                UpgradeInstruction::LoadModule {
5828                    module: "demo".into(),
5829                },
5830                UpgradeInstruction::StateChange {
5831                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
5832                },
5833                UpgradeInstruction::SoftPurge {
5834                    module: "demo-old".into(),
5835                },
5836            ],
5837        }];
5838        let json = serde_json::to_string(&c).unwrap();
5839        let back: Caixa = serde_json::from_str(&json).unwrap();
5840        assert_eq!(c.upgrade_from, back.upgrade_from);
5841    }
5842
5843    #[test]
5844    fn supervisor_view_returns_typed_shape() {
5845        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
5846        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
5847        c.kind = CaixaKind::Supervisor;
5848        c.bibliotecas.clear();
5849        c.estrategia = Some(RestartStrategy::OneForOne);
5850        c.max_restarts = Some(5);
5851        c.restart_window = Some("60s".into());
5852        c.children = vec![ChildSpec {
5853            caixa: "worker".into(),
5854            versao: "^0.1".into(),
5855            restart: RestartPolicy::Permanent,
5856        }];
5857        let view = c.supervisor_view().expect("Supervisor kind has a view");
5858        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
5859        assert_eq!(view.max_restarts, 5);
5860        assert_eq!(
5861            view.restart_window,
5862            Some(std::time::Duration::from_secs(60))
5863        );
5864        assert_eq!(view.children.len(), 1);
5865        view.validate().unwrap();
5866    }
5867
5868    #[test]
5869    fn supervisor_view_none_for_non_supervisor_kinds() {
5870        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5871        assert!(c.supervisor_view().is_none());
5872    }
5873
5874    #[test]
5875    fn declared_mesh_slots_empty_for_bare_caixa() {
5876        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5877        assert!(c.declared_mesh_slots().is_empty());
5878    }
5879
5880    #[test]
5881    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
5882        use crate::{Entrada, Membro};
5883        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5884        // Set a non-adjacent pair (:membros + :entrada) to pin that the
5885        // canonical declaration order is preserved regardless of which
5886        // subset is populated.
5887        c.membros = vec![Membro {
5888            caixa: "a".into(),
5889            versao: "^0.1".into(),
5890        }];
5891        c.entrada = Some(Entrada {
5892            host: "x.example.com".into(),
5893            para: "a".into(),
5894            paths: vec![],
5895            port: 8080,
5896        });
5897        assert_eq!(
5898            c.declared_mesh_slots(),
5899            vec![
5900                crate::render::M3_AUTHOR_KEY_MEMBROS,
5901                crate::render::M3_AUTHOR_KEY_ENTRADA,
5902            ]
5903        );
5904    }
5905
5906    #[test]
5907    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
5908        // Scalar-value pin: the five author-facing kebab-case labels the
5909        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
5910        // mesh slot axis, one arm per typed slot. Mirrors the peer
5911        // scalar-value pin the sibling
5912        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
5913        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
5914        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
5915        // carry (f49c8b0), so both altitudes of the typed-slot algebra
5916        // (per-Servico M2 + per-Aplicacao M3) share the same
5917        // "one canonical byte-string per arm" discipline. A future
5918        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
5919        // `:politicas` → `:policies`, `:placement` → `:distribution`,
5920        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
5921        // and every consumer that reaches for the label picks it up at
5922        // build time rather than at runtime as a downstream mismatch.
5923        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
5924        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
5925        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
5926        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
5927        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
5928    }
5929
5930    #[test]
5931    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
5932        // Production-through-const pin: the five per-arm labels the
5933        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
5934        // `Vec` route through the lifted
5935        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
5936        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
5937        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
5938        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
5939        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
5940        // declaration order. A future re-order or drift at the tagger
5941        // (a rename that reaches the tagger but not the const, or vice
5942        // versa) surfaces here at build time rather than at runtime as
5943        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
5944        // `slots: <stale-kebab-case>` diagnostic far from the rename's
5945        // commit. Mirror of the peer
5946        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
5947        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
5948        // axis.
5949        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
5950        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5951        c.membros = vec![Membro {
5952            caixa: "a".into(),
5953            versao: "^0.1".into(),
5954        }];
5955        c.contratos = vec![WitContract {
5956            de: "a".into(),
5957            para: "a".into(),
5958            wit: "wasi:http/proxy".into(),
5959            endpoint: Some("/x".into()),
5960            subject: None,
5961            slot: None,
5962        }];
5963        c.politicas = Some(MeshPolicy::default());
5964        c.placement = Some(Placement {
5965            estrategia: PlacementStrategy::Replicated,
5966            clusters: vec!["rio".into()],
5967            affinity: None,
5968            shard_key: None,
5969        });
5970        c.entrada = Some(Entrada {
5971            host: "x.example.com".into(),
5972            para: "a".into(),
5973            paths: vec![],
5974            port: 8080,
5975        });
5976        assert_eq!(
5977            c.declared_mesh_slots(),
5978            vec![
5979                crate::render::M3_AUTHOR_KEY_MEMBROS,
5980                crate::render::M3_AUTHOR_KEY_CONTRATOS,
5981                crate::render::M3_AUTHOR_KEY_POLITICAS,
5982                crate::render::M3_AUTHOR_KEY_PLACEMENT,
5983                crate::render::M3_AUTHOR_KEY_ENTRADA,
5984            ]
5985        );
5986    }
5987
5988    #[test]
5989    fn declared_supervisor_slots_empty_for_bare_caixa() {
5990        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5991        assert!(c.declared_supervisor_slots().is_empty());
5992    }
5993
5994    #[test]
5995    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
5996        use crate::RestartStrategy;
5997        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5998        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
5999        // that the canonical declaration order is preserved regardless
6000        // of which subset is populated.
6001        c.estrategia = Some(RestartStrategy::OneForOne);
6002        c.restart_window = Some("60s".into());
6003        assert_eq!(
6004            c.declared_supervisor_slots(),
6005            vec![
6006                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6007                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6008            ]
6009        );
6010    }
6011
6012    #[test]
6013    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6014        // Scalar-value pin: the four author-facing kebab-case labels the
6015        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
6016        // supervision-tree slot axis, one arm per typed slot. Mirrors the
6017        // peer scalar-value pins the sibling
6018        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
6019        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
6020        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
6021        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
6022        // top-level M3 slot consts carry, so all three kind-scoped
6023        // typed-slot-family author-facing-label axes route through one
6024        // canonical per-arm declaration. A future rebrand
6025        // (`:estrategia` → `:strategy` for English uniformity,
6026        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
6027        // `MaxIntensity` name, `:restart-window` → `:period` matching
6028        // OTP's `Period` name, `:children` → `:workers` matching Elixir
6029        // idiom) lands as an edit to exactly one const, and every
6030        // consumer that reaches for the label picks it up at build time
6031        // rather than at runtime as a downstream mismatch.
6032        assert_eq!(
6033            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6034            ":estrategia"
6035        );
6036        assert_eq!(
6037            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6038            ":max-restarts"
6039        );
6040        assert_eq!(
6041            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6042            ":restart-window"
6043        );
6044        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
6045    }
6046
6047    #[test]
6048    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
6049        // Production-through-const pin: the four per-arm labels the
6050        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
6051        // return `Vec` route through the lifted
6052        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
6053        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
6054        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
6055        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
6056        // canonical declaration order. A future re-order or drift at the
6057        // tagger (a rename that reaches the tagger but not the const, or
6058        // vice versa) surfaces here at build time rather than at runtime
6059        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
6060        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6061        // commit. Mirror of the peer
6062        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6063        // (f49c8b0) and
6064        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
6065        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
6066        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
6067        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6068        c.estrategia = Some(RestartStrategy::OneForOne);
6069        c.max_restarts = Some(5);
6070        c.restart_window = Some("60s".into());
6071        c.children = vec![ChildSpec {
6072            caixa: "worker".into(),
6073            versao: "^0.1".into(),
6074            restart: RestartPolicy::Permanent,
6075        }];
6076        assert_eq!(
6077            c.declared_supervisor_slots(),
6078            vec![
6079                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6080                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6081                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6082                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
6083            ]
6084        );
6085    }
6086
6087    #[test]
6088    fn declared_servico_slots_empty_for_bare_caixa() {
6089        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6090        assert!(c.declared_servico_slots().is_empty());
6091    }
6092
6093    #[test]
6094    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
6095        use crate::{UpgradeFromEntry, UpgradeInstruction};
6096        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6097        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
6098        // the canonical declaration order is preserved regardless of
6099        // which subset is populated.
6100        c.limits = Some(crate::LimitsSpec {
6101            fuel: Some(1_000_000),
6102            ..Default::default()
6103        });
6104        c.upgrade_from = vec![UpgradeFromEntry {
6105            from: "0.1.0".into(),
6106            instructions: vec![UpgradeInstruction::Restart],
6107        }];
6108        assert_eq!(
6109            c.declared_servico_slots(),
6110            vec![
6111                crate::render::M2_AUTHOR_KEY_LIMITS,
6112                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6113            ]
6114        );
6115    }
6116
6117    #[test]
6118    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6119        // Scalar-value pin: the three author-facing kebab-case labels
6120        // the `(defcaixa … :<slot> (…))` surface admits on the M2
6121        // top-level slot axis, one arm per typed slot. Mirrors the peer
6122        // scalar-value pin the sibling renderer-side
6123        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
6124        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
6125        // consts carry, so both halves of the M2 top-level slot dual
6126        // axis (author-facing kebab-case label + renderer-side
6127        // camelCase overlay-container wire key) route through one
6128        // canonical per-arm declaration. A future rebrand
6129        // (`:limits` → `:sandbox` matching Lunatic per-process
6130        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
6131        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
6132        // matching Erlang's verbatim appup name) lands as an edit to
6133        // exactly one const, and every consumer that reaches for the
6134        // label picks it up at build time rather than at runtime as a
6135        // downstream mismatch.
6136        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
6137        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
6138        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
6139    }
6140
6141    #[test]
6142    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
6143        // Production-through-const pin: the three per-arm labels the
6144        // [`Caixa::declared_servico_slots`] tagger pushes onto its
6145        // return `Vec` route through the lifted
6146        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
6147        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
6148        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
6149        // declaration order. A future re-order or drift at the tagger
6150        // (a rename that reaches the tagger but not the const, or vice
6151        // versa) surfaces here at build time rather than at runtime as
6152        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
6153        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6154        // commit. Mirror of the peer
6155        // [`crate::behavior::BehaviorSpec::declared_slots`] production
6156        // tagger pin (889dc18) on the sibling per-callback axis.
6157        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
6158        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6159        c.limits = Some(crate::LimitsSpec {
6160            fuel: Some(1_000_000),
6161            ..Default::default()
6162        });
6163        c.behavior = Some(BehaviorSpec {
6164            on_init: Some(PathBuf::from("lib/init.lisp")),
6165            ..Default::default()
6166        });
6167        c.upgrade_from = vec![UpgradeFromEntry {
6168            from: "0.1.0".into(),
6169            instructions: vec![UpgradeInstruction::Restart],
6170        }];
6171        assert_eq!(
6172            c.declared_servico_slots(),
6173            vec![
6174                crate::render::M2_AUTHOR_KEY_LIMITS,
6175                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
6176                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6177            ]
6178        );
6179    }
6180
6181    #[test]
6182    fn existing_manifests_unaffected_by_new_optional_slots() {
6183        // Regression test: a caixa.lisp authored before M2 typed slots
6184        // should still parse + serialize cleanly. The bare `defcaixa`
6185        // emitted by `Caixa::template` has none of the new fields.
6186        let src = Caixa::template("legacy");
6187        let c = Caixa::from_lisp(&src).unwrap();
6188        assert!(c.limits.is_none());
6189        assert!(c.behavior.is_none());
6190        assert!(c.upgrade_from.is_empty());
6191        assert!(c.estrategia.is_none());
6192        assert!(c.children.is_empty());
6193
6194        // And to_lisp emits a manifest with the new slots in the
6195        // empty/default state — round-trippable.
6196        let emitted = c.to_lisp();
6197        let back = Caixa::from_lisp(&emitted).unwrap();
6198        assert_eq!(c, back);
6199    }
6200
6201    #[test]
6202    fn validate_deps_accepts_canonical_caixa() {
6203        // Positive control: the bare template — zero deps, zero
6204        // deps_dev — passes the gate trivially. A future axis added to
6205        // `Dep::validate` mustn't regress an empty-deps caixa to a
6206        // build error.
6207        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6208        c.validate_deps().unwrap();
6209    }
6210
6211    #[test]
6212    fn validate_deps_rejects_invalid_versao_in_deps() {
6213        // Fail-before-pass-after pin: a malformed `:deps :versao`
6214        // surfaces at validate_deps() time, not at lacre-resolve time.
6215        // Mirrors `rejects_invalid_membro_versao_requirement` and
6216        // `validate_rejects_invalid_child_versao_requirement` on the
6217        // other two `:versao` axes.
6218        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6219        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
6220        let err = c.validate_deps().unwrap_err();
6221        assert!(
6222            matches!(
6223                err,
6224                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6225                    if nome == "caixa-teia" && versao == "^bad-version"
6226            ),
6227            "got {err:?}"
6228        );
6229    }
6230
6231    #[test]
6232    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
6233        // Parity pin: `:deps-dev` must run through the same per-entry
6234        // validator as `:deps` — a typo in either axis surfaces the
6235        // same diagnostic. Without this leg, `:deps-dev` would be a
6236        // second-class citizen of the typed surface and an author
6237        // could land a build that passes validate_deps but fails at
6238        // `feira lock`-time when the dev-dep is resolved for a test
6239        // build.
6240        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6241        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
6242        let err = c.validate_deps().unwrap_err();
6243        assert!(
6244            matches!(
6245                err,
6246                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6247                    if nome == "tatara-check" && versao == "^^0.1"
6248            ),
6249            "got {err:?}"
6250        );
6251    }
6252
6253    #[test]
6254    fn validate_deps_runs_deps_before_deps_dev() {
6255        // Order pin: when both lists carry typos, the `:deps`
6256        // diagnostic surfaces first. The author's mental model is
6257        // "runtime deps are load-bearing; dev deps are scaffolding";
6258        // surfacing the runtime axis first matches that hierarchy.
6259        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6260        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
6261        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
6262        let err = c.validate_deps().unwrap_err();
6263        assert!(
6264            matches!(
6265                err,
6266                crate::dep::DepError::VersaoInvalid { ref nome, .. }
6267                    if nome == "runtime-dep"
6268            ),
6269            "expected `:deps` typo to surface first, got {err:?}"
6270        );
6271    }
6272
6273    #[test]
6274    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
6275        // Positive control sweep across both lists. Pin every
6276        // canonical Cargo-shaped form so a future tightening of the
6277        // accepted set surfaces here as a test failure (parity with
6278        // `accepts_canonical_membro_versao_forms` and
6279        // `validate_accepts_canonical_child_versao_forms`).
6280        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6281        c.deps = vec![
6282            Dep::simple("caret", "^0.1"),
6283            Dep::simple("tilde", "~0.1.2"),
6284            Dep::simple("exact", "0.1.0"),
6285            Dep::simple("wildcard", "*"),
6286            Dep::simple("multi-range", ">=0.1, <2"),
6287        ];
6288        c.deps_dev = vec![
6289            Dep::simple("dev-caret", "^0.1"),
6290            Dep::simple("dev-wildcard", "*"),
6291        ];
6292        c.validate_deps().unwrap();
6293    }
6294
6295    #[test]
6296    fn validate_deps_diagnostic_carries_offending_dep() {
6297        // Diagnostic-shape pin: the error names the offending entry's
6298        // `:nome` + `:versao` verbatim and carries a non-empty
6299        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
6300        // run can render the diagnostic without re-parsing.
6301        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6302        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
6303        let err = c.validate_deps().unwrap_err();
6304        let crate::dep::DepError::VersaoInvalid {
6305            nome,
6306            versao,
6307            reason,
6308        } = err
6309        else {
6310            panic!("expected VersaoInvalid, got other variant");
6311        };
6312        assert_eq!(nome, "caixa-teia");
6313        assert_eq!(versao, "not-a-req");
6314        assert!(
6315            !reason.is_empty(),
6316            "VersaoInvalid `reason` must carry the parser's wording verbatim"
6317        );
6318    }
6319
6320    #[test]
6321    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
6322        // Cross-axis pin: `validate_deps` walks both :deps and
6323        // :deps-dev through `Dep::validate`, and the new fonte gate
6324        // (`:tag` + `:branch` both set — the canonical "pin drift"
6325        // footgun) must surface from the :deps-dev arm with the
6326        // offending entry's :nome named. Pin the :deps-dev arm
6327        // explicitly so a future shortcut that only walks :deps
6328        // surfaces here as a regression.
6329        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6330        c.deps_dev = vec![Dep {
6331            nome: "dev-only".into(),
6332            versao: "^0.1".into(),
6333            fonte: Some(crate::DepSource::Git {
6334                repo: "github:p/x".into(),
6335                tag: Some("v1".into()),
6336                rev: None,
6337                branch: Some("main".into()),
6338            }),
6339            opcional: false,
6340            caracteristicas: vec![],
6341        }];
6342        let err = c.validate_deps().unwrap_err();
6343        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
6344            panic!("expected FontePinAmbiguous from :deps-dev walk");
6345        };
6346        assert_eq!(nome, "dev-only");
6347        assert!(pins.contains(":tag") && pins.contains(":branch"));
6348    }
6349
6350    #[test]
6351    fn validate_deps_rejects_empty_repo_in_deps() {
6352        // Parity pin on the :deps arm: an empty :repo on the runtime
6353        // deps list surfaces the same FonteRepoEmpty diagnostic the
6354        // dep.rs per-entry tests pin, naming the offending entry.
6355        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6356        c.deps = vec![Dep {
6357            nome: "runtime".into(),
6358            versao: "^0.1".into(),
6359            fonte: Some(crate::DepSource::Git {
6360                repo: String::new(),
6361                tag: Some("v1".into()),
6362                rev: None,
6363                branch: None,
6364            }),
6365            opcional: false,
6366            caracteristicas: vec![],
6367        }];
6368        let err = c.validate_deps().unwrap_err();
6369        assert!(
6370            matches!(
6371                err,
6372                crate::dep::DepError::FonteRepoEmpty { ref nome }
6373                    if nome == "runtime"
6374            ),
6375            "got {err:?}"
6376        );
6377    }
6378
6379    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
6380
6381    #[test]
6382    fn validate_deps_rejects_duplicate_nome_in_deps() {
6383        // Fail-before-pass-after pin: two `:deps` entries naming the same
6384        // caixa carry two `:versao` / `:fonte` / feature triples that the
6385        // caixa-resolver's lacre pipeline collapses (the second silently
6386        // overwrites the first at `concrete_versao`-resolve time). The
6387        // gate surfaces the duplicate at validate-time, naming the
6388        // offending caixa + the list, before the resolver-side silent
6389        // drop. Mirrors the peer typed-graph duplicate gates
6390        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
6391        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6392        c.deps = vec![
6393            Dep::simple("caixa-teia", "^0.1"),
6394            Dep::simple("caixa-teia", "^0.2"),
6395        ];
6396        let err = c.validate_deps().unwrap_err();
6397        assert!(
6398            matches!(
6399                err,
6400                crate::dep::DepError::DuplicateNome { ref nome, list }
6401                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6402            ),
6403            "got {err:?}"
6404        );
6405    }
6406
6407    #[test]
6408    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
6409        // Parity pin: `:deps-dev` runs through the same per-list
6410        // duplicate check as `:deps` — neither axis is a second-class
6411        // citizen of the set-not-multiset discipline.
6412        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6413        c.deps_dev = vec![
6414            Dep::simple("tatara-check", "*"),
6415            Dep::simple("tatara-check", "^0.1"),
6416        ];
6417        let err = c.validate_deps().unwrap_err();
6418        assert!(
6419            matches!(
6420                err,
6421                crate::dep::DepError::DuplicateNome { ref nome, list }
6422                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
6423            ),
6424            "got {err:?}"
6425        );
6426    }
6427
6428    #[test]
6429    fn validate_deps_accepts_cross_list_same_nome() {
6430        // The Cargo `[dependencies]` + `[dev-dependencies]` override
6431        // convention is preserved: a name appearing in *both* lists is
6432        // valid (the dev-pin overrides at test/dev time). Only
6433        // within-list duplicates are structurally incoherent — pin the
6434        // permissive cross-list semantics so a future shortcut that
6435        // collapses the two seen-sets into one surfaces here as a test
6436        // failure.
6437        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6438        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
6439        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
6440        c.validate_deps().unwrap();
6441    }
6442
6443    #[test]
6444    fn validate_deps_accepts_distinct_nome_in_both_lists() {
6445        // Positive control: distinct names within each list pass — the
6446        // gate's identity element on the canonical authoring shape.
6447        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6448        c.deps = vec![
6449            Dep::simple("caixa-teia", "^0.1"),
6450            Dep::simple("pleme-mesh", "*"),
6451        ];
6452        c.deps_dev = vec![
6453            Dep::simple("tatara-check", "*"),
6454            Dep::simple("dev-shim", "^0.1"),
6455        ];
6456        c.validate_deps().unwrap();
6457    }
6458
6459    #[test]
6460    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
6461        // Diagnostic-precedence pin: a malformed `:versao` on the
6462        // duplicating entry surfaces its narrower `VersaoInvalid`
6463        // diagnostic first, before the cross-entry duplicate gate fires
6464        // — the canonical "per-entry shape before cross-entry uniqueness"
6465        // precedence every peer set-not-multiset gate establishes
6466        // (`*_invalid_fires_before_duplicate_check` pins on
6467        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
6468        // `validate_upgrade_from`).
6469        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6470        c.deps = vec![
6471            Dep::simple("caixa-teia", "^0.1"),
6472            Dep::simple("caixa-teia", "^bad-version"),
6473        ];
6474        let err = c.validate_deps().unwrap_err();
6475        assert!(
6476            matches!(
6477                err,
6478                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6479                    if nome == "caixa-teia" && versao == "^bad-version"
6480            ),
6481            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
6482        );
6483    }
6484
6485    #[test]
6486    fn validate_deps_duplicate_diagnostic_names_first_collision() {
6487        // First-collision determinism pin: with three entries naming the
6488        // same caixa, the first colliding pair surfaces — not the last.
6489        // Mirrors the peer first-collision posture on every
6490        // duplicate-target gate
6491        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
6492        // — the second entry is the first collision; this gate uses the
6493        // same shape: the second entry's `:nome` lands in the diagnostic
6494        // because `seen.insert(first.nome)` already populated the set).
6495        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6496        c.deps = vec![
6497            Dep::simple("caixa-teia", "^0.1"),
6498            Dep::simple("caixa-teia", "^0.2"),
6499            Dep::simple("caixa-teia", "^0.3"),
6500        ];
6501        let err = c.validate_deps().unwrap_err();
6502        // The diagnostic carries the offending caixa name; the
6503        // implementation surfaces on the *second* entry (the first
6504        // collision), so the test pins the `:nome` value.
6505        assert!(
6506            matches!(
6507                err,
6508                crate::dep::DepError::DuplicateNome { ref nome, list }
6509                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6510            ),
6511            "got {err:?}"
6512        );
6513    }
6514
6515    #[test]
6516    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
6517        // Cross-list precedence pin: when both lists carry duplicates,
6518        // the `:deps` diagnostic surfaces first — same author-mental-
6519        // model ordering the `validate_deps_runs_deps_before_deps_dev`
6520        // pin establishes for malformed `:versao` (runtime axis before
6521        // dev axis).
6522        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6523        c.deps = vec![
6524            Dep::simple("runtime-dep", "^0.1"),
6525            Dep::simple("runtime-dep", "^0.2"),
6526        ];
6527        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
6528        let err = c.validate_deps().unwrap_err();
6529        assert!(
6530            matches!(
6531                err,
6532                crate::dep::DepError::DuplicateNome { ref nome, list }
6533                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6534            ),
6535            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
6536        );
6537    }
6538
6539    #[test]
6540    fn validate_deps_empty_lists_pass_duplicate_gate() {
6541        // Empty-set identity pin: the bare template (zero deps, zero
6542        // deps_dev) passes the duplicate gate as the gate's identity
6543        // element. A future tighten that conflates "empty" with
6544        // "missing" would regress this baseline.
6545        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6546        c.validate_deps().unwrap();
6547    }
6548
6549    #[test]
6550    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
6551        // Diagnostic-shape pin: the `list:` field tags which list the
6552        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
6553        // `feira lint` run can route the author to the right block in
6554        // their caixa.lisp without re-deriving the list from context.
6555        // Same self-locating shape every peer per-axis diagnostic
6556        // already exposes.
6557        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6558        c.deps_dev = vec![
6559            Dep::simple("dev-thing", "*"),
6560            Dep::simple("dev-thing", "^0.1"),
6561        ];
6562        let err = c.validate_deps().unwrap_err();
6563        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
6564            panic!("expected DuplicateNome from :deps-dev walk");
6565        };
6566        assert_eq!(nome, "dev-thing");
6567        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
6568    }
6569
6570    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
6571
6572    #[test]
6573    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
6574        // Thread-through pin on `:deps`: the per-entry
6575        // `Dep::validate_caracteristicas` gate fires inside
6576        // `Caixa::validate_deps`'s linear walk, so a malformed feature
6577        // list on any `:deps` entry surfaces as a `DepError` from
6578        // `validate_deps` — the same reachability shape every per-entry
6579        // `Dep::validate` arm threads through. Without this pin a future
6580        // shortcut that skips the per-entry `Dep::validate` call on the
6581        // cross-entry-uniqueness path would mask the within-entry
6582        // `:caracteristicas` gates.
6583        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6584        c.deps = vec![Dep {
6585            nome: "caixa-teia".into(),
6586            versao: "^0.1".into(),
6587            fonte: None,
6588            opcional: false,
6589            caracteristicas: vec!["http".into(), "http".into()],
6590        }];
6591        let err = c.validate_deps().unwrap_err();
6592        let crate::dep::DepError::CaracteristicaDuplicate {
6593            nome,
6594            caracteristica,
6595        } = err
6596        else {
6597            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
6598        };
6599        assert_eq!(nome, "caixa-teia");
6600        assert_eq!(caracteristica, "http");
6601    }
6602
6603    #[test]
6604    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
6605        // Peer thread-through pin on `:deps-dev`: same reachability as
6606        // the `:deps` arm above, on the dev-only authoring axis. Pins
6607        // that the `validate_deps` walk visits both lists' per-entry
6608        // gates uniformly. The empty-feature arm carries here so both
6609        // new `:caracteristicas` arms are surfaced via at least one
6610        // `validate_deps` thread-through.
6611        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6612        c.deps_dev = vec![Dep {
6613            nome: "caixa-teia".into(),
6614            versao: "^0.1".into(),
6615            fonte: None,
6616            opcional: false,
6617            caracteristicas: vec![String::new()],
6618        }];
6619        let err = c.validate_deps().unwrap_err();
6620        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
6621            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
6622        };
6623        assert_eq!(nome, "caixa-teia");
6624    }
6625
6626    #[test]
6627    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
6628        // Thread-through pin on `:deps`: the per-entry
6629        // `Dep::validate_caracteristicas` value-shape gate (lifted via
6630        // `crate::render::is_cargo_feature_name`) fires inside
6631        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
6632        // a structurally invalid feature name on any `:deps` entry
6633        // surfaces as `DepError::CaracteristicaInvalid` from
6634        // `validate_deps` — the same reachability shape every per-entry
6635        // `Dep::validate` arm threads through. Without this pin a
6636        // future shortcut that skips the per-entry `Dep::validate` call
6637        // on the cross-entry-uniqueness path would mask the within-
6638        // entry `:caracteristicas` value-shape gate.
6639        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6640        c.deps = vec![Dep {
6641            nome: "caixa-teia".into(),
6642            versao: "^0.1".into(),
6643            fonte: None,
6644            opcional: false,
6645            caracteristicas: vec!["+http".into()],
6646        }];
6647        let err = c.validate_deps().unwrap_err();
6648        let crate::dep::DepError::CaracteristicaInvalid {
6649            nome,
6650            caracteristica,
6651            ..
6652        } = err
6653        else {
6654            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
6655        };
6656        assert_eq!(nome, "caixa-teia");
6657        assert_eq!(caracteristica, "+http");
6658    }
6659
6660    #[test]
6661    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
6662        // Peer thread-through pin on `:deps-dev`: same reachability as
6663        // the `:deps` arm above, on the dev-only authoring axis. The
6664        // `http/json` shape carries here so the segment-separator
6665        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
6666        // confusion footgun) is surfaced via the cross-entry walk too —
6667        // pinning that the `:deps-dev` list visits the same per-entry
6668        // value-shape gate as the `:deps` list.
6669        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6670        c.deps_dev = vec![Dep {
6671            nome: "caixa-teia".into(),
6672            versao: "^0.1".into(),
6673            fonte: None,
6674            opcional: false,
6675            caracteristicas: vec!["http/json".into()],
6676        }];
6677        let err = c.validate_deps().unwrap_err();
6678        let crate::dep::DepError::CaracteristicaInvalid {
6679            nome,
6680            caracteristica,
6681            ..
6682        } = err
6683        else {
6684            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
6685        };
6686        assert_eq!(nome, "caixa-teia");
6687        assert_eq!(caracteristica, "http/json");
6688    }
6689
6690    #[test]
6691    fn to_lisp_preserves_deps() {
6692        let src = r#"
6693(defcaixa
6694  :nome "x"
6695  :versao "0.1.0"
6696  :kind Biblioteca
6697  :deps ((:nome "a" :versao "^0.1")
6698         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
6699"#;
6700        let c1 = Caixa::from_lisp(src).unwrap();
6701        let emitted = c1.to_lisp();
6702        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
6703        assert_eq!(c1.deps, c2.deps);
6704    }
6705
6706    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
6707
6708    fn caixa_with_nome(nome: &str) -> Caixa {
6709        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
6710        c.nome = nome.to_string();
6711        c
6712    }
6713
6714    #[test]
6715    fn validate_nome_accepts_canonical_template() {
6716        // Positive control: the bare `feira init`-style template's
6717        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
6718        // not regress this baseline shape. A future tightening of the
6719        // accepted set surfaces here as a test failure first.
6720        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6721        c.validate_nome().unwrap();
6722    }
6723
6724    #[test]
6725    fn validate_nome_accepts_canonical_forms() {
6726        // Positive-set sweep: each realistic caixa-name shape the K8s
6727        // apiserver accepts as a `metadata.name` label must pass —
6728        // single-word, hyphen-joined, version-suffixed, single-char,
6729        // two-char, digit-start (DNS-1123 allows this; the stricter
6730        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
6731        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
6732        // the peer member-name axis.
6733        for nome in [
6734            "checkout",
6735            "cart-v2",
6736            "a",
6737            "db",
6738            "3rd-party-shim",
6739            "payment-retry",
6740            "0",
6741        ] {
6742            caixa_with_nome(nome)
6743                .validate_nome()
6744                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
6745        }
6746    }
6747
6748    #[test]
6749    fn validate_nome_rejects_empty() {
6750        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
6751        // an empty `:nome` (the derive macro stores the raw String);
6752        // the gate's empty arm names the offending axis with a narrower
6753        // diagnostic than the `NomeInvalid` parse arm would emit.
6754        let c = caixa_with_nome("");
6755        let err = c.validate_nome().unwrap_err();
6756        assert_eq!(err, ManifestError::NomeEmpty);
6757    }
6758
6759    #[test]
6760    fn validate_nome_rejects_uppercase() {
6761        // The canonical "I copied the TitleCase display name verbatim"
6762        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
6763        // admission on every derived artifact (Helm chart, ComputeUnit,
6764        // CNP, HTTPRoute, label values); the gate moves the diagnostic
6765        // to the source `caixa.lisp` and the reason suggests the
6766        // lowercased fix verbatim.
6767        let c = caixa_with_nome("MyApp");
6768        let err = c.validate_nome().unwrap_err();
6769        let ManifestError::NomeInvalid { nome, reason } = err else {
6770            panic!("expected NomeInvalid for uppercase :nome");
6771        };
6772        assert_eq!(nome, "MyApp");
6773        assert!(
6774            reason.contains("uppercase") && reason.contains("myapp"),
6775            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
6776        );
6777    }
6778
6779    #[test]
6780    fn validate_nome_rejects_underscore() {
6781        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
6782        // `_`; the apiserver rejects on admission across every derived
6783        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
6784        // and `:children :caixa` (31bfa43).
6785        let c = caixa_with_nome("my_app");
6786        let err = c.validate_nome().unwrap_err();
6787        assert!(
6788            matches!(
6789                err,
6790                ManifestError::NomeInvalid { ref nome, ref reason }
6791                    if nome == "my_app" && reason.contains('_')
6792            ),
6793            "got {err:?}"
6794        );
6795    }
6796
6797    #[test]
6798    fn validate_nome_rejects_dot() {
6799        // A `:nome` is a single DNS-1123 label, not a subdomain. The
6800        // "I want to namespace with `.`" footgun the gate redirects to
6801        // `-` via the shared predicate's reason wording.
6802        let c = caixa_with_nome("team.app");
6803        let err = c.validate_nome().unwrap_err();
6804        assert!(
6805            matches!(
6806                err,
6807                ManifestError::NomeInvalid { ref nome, ref reason }
6808                    if nome == "team.app" && reason.contains('.')
6809            ),
6810            "got {err:?}"
6811        );
6812    }
6813
6814    #[test]
6815    fn validate_nome_rejects_leading_hyphen() {
6816        // DNS-1123 boundary rule: the label must start with an ASCII
6817        // alphanumeric. Pin the leading-`-` arm explicitly.
6818        let c = caixa_with_nome("-app");
6819        let err = c.validate_nome().unwrap_err();
6820        assert!(
6821            matches!(
6822                err,
6823                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
6824            ),
6825            "got {err:?}"
6826        );
6827    }
6828
6829    #[test]
6830    fn validate_nome_rejects_trailing_hyphen() {
6831        // Symmetric arm of the boundary rule, pinned separately so a
6832        // future relaxation that only checks the leading position
6833        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
6834        // and `_with_trailing_hyphen` on the supervisor / aplicacao
6835        // axes.
6836        let c = caixa_with_nome("app-");
6837        let err = c.validate_nome().unwrap_err();
6838        assert!(
6839            matches!(
6840                err,
6841                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
6842            ),
6843            "got {err:?}"
6844        );
6845    }
6846
6847    #[test]
6848    fn validate_nome_rejects_unicode() {
6849        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
6850        // bytes are rejected by the K8s apiserver on every name axis.
6851        let c = caixa_with_nome("café");
6852        let err = c.validate_nome().unwrap_err();
6853        assert!(
6854            matches!(
6855                err,
6856                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
6857            ),
6858            "got {err:?}"
6859        );
6860    }
6861
6862    #[test]
6863    fn validate_nome_rejects_whitespace() {
6864        // The paste-from-sketch / paste-from-spec footgun. Internal
6865        // whitespace is rejected by every K8s name axis.
6866        let c = caixa_with_nome("my app");
6867        let err = c.validate_nome().unwrap_err();
6868        assert!(
6869            matches!(
6870                err,
6871                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
6872            ),
6873            "got {err:?}"
6874        );
6875    }
6876
6877    #[test]
6878    fn validate_nome_rejects_too_long() {
6879        // 64-byte boundary pin: the K8s apiserver rejects any
6880        // `metadata.name` over 63 bytes at admission; the diagnostic
6881        // names both the 63-byte cap and the actual length so the
6882        // author can shorten in one edit. Mirrors `_too_long` on the
6883        // peer member-/cluster-/child-name axes.
6884        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
6885        let c = caixa_with_nome(&over);
6886        let err = c.validate_nome().unwrap_err();
6887        let ManifestError::NomeInvalid { nome, reason } = err else {
6888            panic!("expected NomeInvalid for over-cap :nome");
6889        };
6890        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
6891        assert!(
6892            reason.contains("63") && reason.contains("64"),
6893            "diagnostic must name the cap + actual length, got {reason:?}"
6894        );
6895    }
6896
6897    #[test]
6898    fn nome_max_length_validates() {
6899        // The 63-byte cap exactly — the boundary-accepting case pinned
6900        // alongside `validate_nome_rejects_too_long` so a future cap
6901        // shift surfaces both arms simultaneously. Mirrors
6902        // `membro_caixa_max_length_validates`,
6903        // `placement_cluster_max_length_validates`,
6904        // `child_caixa_max_length_validates`.
6905        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
6906        caixa_with_nome(&at_cap).validate_nome().unwrap();
6907    }
6908
6909    #[test]
6910    fn nome_empty_takes_precedence_over_invalid() {
6911        // Order pin: the empty arm fires before the predicate is
6912        // consulted. Empty < invalid in self-locating-ness — the
6913        // narrower `NomeEmpty` diagnostic doesn't carry a useless
6914        // `nome: ""` reference into the parser-shaped reason. Mirrors
6915        // `membro_caixa_empty_takes_precedence_over_invalid` on the
6916        // peer axis (3f9d7a0).
6917        let c = caixa_with_nome("");
6918        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
6919    }
6920
6921    #[test]
6922    fn nome_invalid_diagnostic_carries_offending_nome() {
6923        // Diagnostic-shape pin: the error names the offending `:nome`
6924        // verbatim with a non-empty parser-shaped reason, so a `feira
6925        // lint` run can render the diagnostic without re-parsing.
6926        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
6927        let c = caixa_with_nome("MyApp");
6928        let err = c.validate_nome().unwrap_err();
6929        let ManifestError::NomeInvalid { nome, reason } = err else {
6930            panic!("expected NomeInvalid variant");
6931        };
6932        assert_eq!(nome, "MyApp");
6933        assert!(
6934            !reason.is_empty(),
6935            "NomeInvalid `reason` must carry the predicate's wording verbatim"
6936        );
6937    }
6938
6939    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
6940    //
6941    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
6942    // via DNS-1123; this second-axis gate caps the joint
6943    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
6944    // canonical [`crate::lareira_chart_name`] helper's doc comment
6945    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
6946    // "the M4 admission webhook will pin the joint-length invariant
6947    // when it lands". These tests pin it at the manifest-validate
6948    // layer instead, fail-before-pass-after on the 56-byte boundary.
6949
6950    #[test]
6951    fn validate_nome_chart_name_budget_accepts_canonical_template() {
6952        // Positive control: the bare `feira init`-style template's
6953        // `:nome` ("demo") sits far below the cap; the gate must not
6954        // regress this baseline. Same shape every peer
6955        // value-shape-gate baseline pin uses.
6956        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6957        c.validate_nome_chart_name_budget().unwrap();
6958    }
6959
6960    #[test]
6961    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
6962        // Positive-set sweep across the canonical author surface every
6963        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
6964        // `worker`, the `checkout-aplicacao` example members, the
6965        // `akeyless-attest` caixa-tatara fixture). Every value sits
6966        // far below the 55-byte per-`:nome` budget. Same shape every
6967        // peer per-axis baseline pin uses.
6968        for nome in [
6969            "hello-rio",
6970            "cart",
6971            "checkout",
6972            "worker",
6973            "akeyless-attest",
6974            "demo",
6975            "a",
6976        ] {
6977            caixa_with_nome(nome)
6978                .validate_nome_chart_name_budget()
6979                .unwrap_or_else(|e| {
6980                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
6981                });
6982        }
6983    }
6984
6985    #[test]
6986    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
6987        // Boundary-accepting case at the 55-byte per-`:nome` budget —
6988        // the joint chart name is exactly 63 bytes, the DNS-1123 label
6989        // cap. Pinned alongside the rejecting-arm test so a future cap
6990        // shift surfaces both arms simultaneously. Mirrors
6991        // `nome_max_length_validates` on the peer bare-`:nome` axis.
6992        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
6993        caixa_with_nome(&at_cap)
6994            .validate_nome_chart_name_budget()
6995            .unwrap();
6996    }
6997
6998    #[test]
6999    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
7000        // Fail-before-pass-after pin on the 56-byte boundary: the
7001        // smallest `:nome` length that overflows the joint chart-name
7002        // cap. The inner [`is_dns_1123_label`] gate
7003        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
7004        // this gate it silently passed the manifest-validate cascade
7005        // and surfaced as a `helm lint` / apiserver rejection on the
7006        // rendered chart name far from the source `caixa.lisp`, with
7007        // no field naming the overflow. With this gate the diagnostic
7008        // names the offending `:nome` verbatim alongside the rendered
7009        // chart name and the budget, so the author can shorten in one
7010        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
7011        // bare-`:nome` axis.
7012        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7013        let c = caixa_with_nome(&over);
7014        let err = c.validate_nome_chart_name_budget().unwrap_err();
7015        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7016            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
7017        };
7018        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7019        assert_eq!(nome, over);
7020        assert!(
7021            reason.contains("63") && reason.contains("64") && reason.contains("55"),
7022            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
7023             and the per-`:nome` budget (55), got {reason:?}"
7024        );
7025    }
7026
7027    #[test]
7028    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
7029        // The 63-byte `:nome` boundary — passes the bare-`:nome`
7030        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
7031        // joint chart name that overflows the DNS-1123 label cap
7032        // structurally. The most stringent fail-before-pass-after
7033        // surface: every `:nome` in the 56..=63-byte range passed the
7034        // prior cascade and broke at admission.
7035        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
7036        let c = caixa_with_nome(&bare_max);
7037        // The bare-`:nome` gate accepts the 63-byte length.
7038        c.validate_nome().unwrap();
7039        // The new joint-length gate rejects it.
7040        let err = c.validate_nome_chart_name_budget().unwrap_err();
7041        assert!(
7042            matches!(
7043                err,
7044                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
7045                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
7046            ),
7047            "got {err:?}"
7048        );
7049    }
7050
7051    #[test]
7052    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
7053        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
7054        // name appears verbatim in the diagnostic so the author sees
7055        // exactly the string the apiserver / `helm lint` would have
7056        // rejected — no re-derivation required to grep the source.
7057        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
7058        // on the bare-`:nome` axis.
7059        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
7060        let c = caixa_with_nome(&over);
7061        let err = c.validate_nome_chart_name_budget().unwrap_err();
7062        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7063            panic!("expected NomeChartNameBudgetExceeded variant");
7064        };
7065        assert_eq!(nome, over);
7066        let expected_chart = crate::lareira_chart_name(&over);
7067        assert!(
7068            reason.contains(&expected_chart),
7069            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
7070             got {reason:?}"
7071        );
7072        assert!(
7073            reason.contains("lareira-"),
7074            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
7075        );
7076    }
7077
7078    #[test]
7079    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
7080        // Order pin on the layout cascade: the narrower
7081        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
7082        // joint-length budget. A structurally-malformed `:nome` (here:
7083        // uppercase) surfaces its specific shape error rather than
7084        // the chart-name-budget error, even when the joint length
7085        // would also overflow — the narrower diagnostic is more
7086        // self-locating. Mirrors the cascade-precedence pins peer
7087        // gates already use (e.g. `EntradaParaEmpty` before
7088        // `EntradaParaInvalid`).
7089        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7090        let c = caixa_with_nome(&over);
7091        // The bare-shape gate fires first.
7092        let err = c.validate_nome().unwrap_err();
7093        assert!(
7094            matches!(err, ManifestError::NomeInvalid { .. }),
7095            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
7096        );
7097        // And the layout verify cascade surfaces that diagnostic, not
7098        // the budget arm. Inject a path-exists oracle so the cascade
7099        // gets past the manifest-presence check and into the
7100        // value-shape gates.
7101        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7102        let err = crate::LayoutInvariants::verify(
7103            &layout,
7104            &c,
7105            std::path::Path::new("/tmp/caixa-test-fake-root"),
7106        )
7107        .unwrap_err();
7108        let issue = err.to_string();
7109        assert!(
7110            issue.contains("DNS-1123") || issue.contains("uppercase"),
7111            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
7112             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
7113        );
7114    }
7115
7116    #[test]
7117    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
7118        // Cross-axis envelope pin: the layout cascade wraps both
7119        // bare-`:nome` and joint-length-`:nome` failures through the
7120        // same [`LayoutError::NomeViolation`] envelope, since both
7121        // arms are on the `:nome` axis. The user's diagnostic stays
7122        // self-locating ("which axis"), and a future consumer that
7123        // dispatches on the layout-error variant (e.g. a `feira lint`
7124        // exit-code mapping) sees a single per-axis envelope. The
7125        // wrapped `issue:` carries the full inner diagnostic.
7126        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7127        let c = caixa_with_nome(&over);
7128        // The bare-shape gate accepts.
7129        c.validate_nome().unwrap();
7130        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7131        let err = crate::LayoutInvariants::verify(
7132            &layout,
7133            &c,
7134            std::path::Path::new("/tmp/caixa-test-fake-root"),
7135        )
7136        .unwrap_err();
7137        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
7138            panic!("expected LayoutError::NomeViolation, got {err:?}");
7139        };
7140        assert_eq!(caixa, over);
7141        assert!(
7142            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
7143            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
7144        );
7145    }
7146
7147    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
7148
7149    fn caixa_with_versao(versao: &str) -> Caixa {
7150        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7151        c.versao = versao.to_string();
7152        c
7153    }
7154
7155    #[test]
7156    fn validate_versao_accepts_canonical_template() {
7157        // Positive control: the bare `feira init`-style template's
7158        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
7159        // must not regress this baseline shape. A future tightening of
7160        // the accepted set surfaces here as a test failure first.
7161        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7162        c.validate_versao().unwrap();
7163    }
7164
7165    #[test]
7166    fn validate_versao_accepts_canonical_forms() {
7167        // Positive-set sweep: each realistic SemVer-2 shape the
7168        // substrate's downstream consumers accept must pass — bare
7169        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
7170        // build metadata (`+build.42`), the combined form, and the
7171        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
7172        // the peer `:nome` axis (6c992f8).
7173        for versao in [
7174            "0.1.0",
7175            "0.0.0",
7176            "1.0.0",
7177            "0.2.0-rc.1",
7178            "1.0.0-alpha.0",
7179            "1.0.0+build.42",
7180            "1.0.0-rc.1+build.42",
7181            "10.20.30",
7182        ] {
7183            caixa_with_versao(versao)
7184                .validate_versao()
7185                .unwrap_or_else(|e| {
7186                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
7187                });
7188        }
7189    }
7190
7191    #[test]
7192    fn validate_versao_rejects_empty() {
7193        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
7194        // an empty `:versao` (the derive macro stores the raw String);
7195        // the gate's empty arm names the offending axis with a narrower
7196        // diagnostic than the `VersaoInvalid` parse arm would emit.
7197        // Mirrors `validate_nome_rejects_empty` (6c992f8).
7198        let c = caixa_with_versao("");
7199        let err = c.validate_versao().unwrap_err();
7200        assert_eq!(err, ManifestError::VersaoEmpty);
7201    }
7202
7203    #[test]
7204    fn validate_versao_rejects_git_tag_shape() {
7205        // The canonical "I copied the git tag verbatim" footgun —
7206        // `feira publish` *emits* `v<versao>` git tags, so a leaked
7207        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
7208        // shift every downstream consumer's version axis. `semver`
7209        // rejects the leading `v` at parse time; the gate moves the
7210        // diagnostic to the source `caixa.lisp`.
7211        let c = caixa_with_versao("v0.1.0");
7212        let err = c.validate_versao().unwrap_err();
7213        let ManifestError::VersaoInvalid { versao, reason } = err else {
7214            panic!("expected VersaoInvalid for git-tag-shape :versao");
7215        };
7216        assert_eq!(versao, "v0.1.0");
7217        assert!(
7218            !reason.is_empty(),
7219            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
7220        );
7221    }
7222
7223    #[test]
7224    fn validate_versao_rejects_missing_patch() {
7225        // The canonical "I shortened it" footgun — SemVer-2 requires
7226        // three parts. Cargo's `version =` field accepts the shortened
7227        // form as a requirement, conflating the two leaks across the
7228        // typed `:deps :versao` vs top-level `:versao` axes; the gate
7229        // pins the top-level axis to the strict three-part shape.
7230        let c = caixa_with_versao("0.1");
7231        let err = c.validate_versao().unwrap_err();
7232        assert!(
7233            matches!(
7234                err,
7235                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
7236            ),
7237            "got {err:?}"
7238        );
7239    }
7240
7241    #[test]
7242    fn validate_versao_rejects_requirement_shape() {
7243        // The canonical "I leaked a requirement into a version" footgun —
7244        // the typed `:deps :versao` / `:membros :versao` axes accept
7245        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
7246        // concrete `Version`. Without this gate the two typed surfaces
7247        // would silently overlap, and a top-level `^0.1` would surface
7248        // at `helm install` time as a Chart.yaml version rejection far
7249        // from the source `caixa.lisp`.
7250        let c = caixa_with_versao("^0.1");
7251        let err = c.validate_versao().unwrap_err();
7252        assert!(
7253            matches!(
7254                err,
7255                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
7256            ),
7257            "got {err:?}"
7258        );
7259    }
7260
7261    #[test]
7262    fn validate_versao_rejects_docker_tag_shape() {
7263        // The "I confused it with a docker tag" footgun — `latest`,
7264        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
7265        // SemVer rejects at parse time; the gate moves the diagnostic
7266        // to the source `caixa.lisp`.
7267        for bad in ["latest", "main", "stable"] {
7268            let c = caixa_with_versao(bad);
7269            let err = c.validate_versao().unwrap_err();
7270            assert!(
7271                matches!(
7272                    err,
7273                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
7274                ),
7275                "got {err:?} for {bad:?}"
7276            );
7277        }
7278    }
7279
7280    #[test]
7281    fn validate_versao_rejects_four_part_form() {
7282        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
7283        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
7284        // semver crate rejects the extra `.0` at parse time.
7285        let c = caixa_with_versao("0.1.0.0");
7286        let err = c.validate_versao().unwrap_err();
7287        assert!(
7288            matches!(
7289                err,
7290                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
7291            ),
7292            "got {err:?}"
7293        );
7294    }
7295
7296    #[test]
7297    fn versao_empty_takes_precedence_over_invalid() {
7298        // Order pin: the empty arm fires before the parser is consulted.
7299        // Empty < invalid in self-locating-ness — the narrower
7300        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
7301        // reference into the parser-shaped reason. Mirrors
7302        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
7303        // peer axis.
7304        let c = caixa_with_versao("");
7305        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
7306    }
7307
7308    #[test]
7309    fn versao_invalid_diagnostic_carries_offending_versao() {
7310        // Diagnostic-shape pin: the error names the offending `:versao`
7311        // verbatim with a non-empty parser-shaped reason, so a `feira
7312        // lint` run can render the diagnostic without re-parsing.
7313        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
7314        let c = caixa_with_versao("v0.1.0");
7315        let err = c.validate_versao().unwrap_err();
7316        let ManifestError::VersaoInvalid { versao, reason } = err else {
7317            panic!("expected VersaoInvalid variant");
7318        };
7319        assert_eq!(versao, "v0.1.0");
7320        assert!(
7321            !reason.is_empty(),
7322            "VersaoInvalid `reason` must carry the parser's wording verbatim"
7323        );
7324    }
7325
7326    #[test]
7327    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
7328        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
7329        // for `:upgrade-from :from` must also pass `validate_versao` —
7330        // the two `:versao`-typed surfaces (top-level `:versao`,
7331        // `:upgrade-from :from`) consume the *same* `semver::Version`
7332        // parser, so they must agree on the accepted set. Without this
7333        // pin, a future tightening of one axis could silently diverge
7334        // from the other. Mirrors the `:versao` requirement-axis
7335        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
7336        // commits established.
7337        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
7338            // From the canonical UpgradeFromEntry round-trip fixture
7339            // (`upgrade::tests::round_trip_load_module` peers).
7340            let entry = crate::UpgradeFromEntry {
7341                from: versao.to_string(),
7342                instructions: Vec::new(),
7343            };
7344            entry
7345                .validate()
7346                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
7347            caixa_with_versao(versao)
7348                .validate_versao()
7349                .unwrap_or_else(|e| {
7350                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
7351                });
7352        }
7353    }
7354
7355    // ── Caixa::validate_restart_window — supervisor restart-window
7356    //    folds through the shared `supervisor::duration_codec` ────────
7357
7358    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
7359        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
7360        c.kind = CaixaKind::Supervisor;
7361        c.restart_window = window.map(str::to_string);
7362        c
7363    }
7364
7365    #[test]
7366    fn validate_restart_window_accepts_none() {
7367        // The canonical "omit the slot to express no reset" shape — a
7368        // `None` raw string is the absence of the typed
7369        // `:restart-window` slot, which is exactly the SupervisorSpec
7370        // "never reset" semantics. The gate must be a no-op here; a
7371        // future tightening that rejected `None` would force every
7372        // supervisor caixa to authoring-time pin a window even when
7373        // the OTP semantics call for none.
7374        caixa_with_restart_window(None)
7375            .validate_restart_window()
7376            .unwrap();
7377    }
7378
7379    #[test]
7380    fn validate_restart_window_accepts_canonical_forms() {
7381        // Positive-set sweep across the canonical authoring units the
7382        // shared `supervisor::duration_codec::parse` accepts —
7383        // matches the codec-side `parse_accepts_integer_canonical_units`
7384        // pin in supervisor::tests so a future codec-side tightening
7385        // surfaces simultaneously on both axes.
7386        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
7387            caixa_with_restart_window(Some(window))
7388                .validate_restart_window()
7389                .unwrap_or_else(|e| {
7390                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
7391                });
7392        }
7393    }
7394
7395    #[test]
7396    fn validate_restart_window_rejects_fractional_seconds() {
7397        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
7398        // as f64 to 1.5 → renders back as `"1500ms"` on first
7399        // serialize). Prior to the fold + this gate, the inline
7400        // `parse_window_inline` accepted f64 magnitudes and silently
7401        // produced a `Duration::from_secs_f64(1.5)`, divergent from
7402        // the shared codec's integer-magnitude discipline on the
7403        // serde-routed siblings. The gate now surfaces a self-locating
7404        // diagnostic at the manifest layer.
7405        let err = caixa_with_restart_window(Some("1.5s"))
7406            .validate_restart_window()
7407            .unwrap_err();
7408        let ManifestError::RestartWindowMalformed {
7409            restart_window,
7410            reason,
7411        } = err
7412        else {
7413            panic!("expected RestartWindowMalformed for fractional seconds");
7414        };
7415        assert_eq!(restart_window, "1.5s");
7416        assert!(
7417            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
7418            "diagnostic must carry shared-codec wording, got {reason:?}"
7419        );
7420    }
7421
7422    #[test]
7423    fn validate_restart_window_rejects_decimal_shaped_integer() {
7424        // The `"1.0s"` class — numerically `1s` exactly, but the
7425        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
7426        // gets the same canonical-form diagnostic.
7427        let err = caixa_with_restart_window(Some("1.0s"))
7428            .validate_restart_window()
7429            .unwrap_err();
7430        assert!(
7431            matches!(
7432                err,
7433                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7434                    if restart_window == "1.0s"
7435            ),
7436            "got {err:?}"
7437        );
7438    }
7439
7440    #[test]
7441    fn validate_restart_window_rejects_half_unit_minute() {
7442        // `"0.5m"` is the unit-fraction footgun — author writes a
7443        // human-readable half-minute, the prior inline parser silently
7444        // produced `Duration::from_secs_f64(30.0)` and serde
7445        // re-emitted as `"30s"`, rewriting author intent. The gate
7446        // closes the loop at the manifest layer.
7447        let err = caixa_with_restart_window(Some("0.5m"))
7448            .validate_restart_window()
7449            .unwrap_err();
7450        let ManifestError::RestartWindowMalformed {
7451            restart_window,
7452            reason,
7453        } = err
7454        else {
7455            panic!("expected RestartWindowMalformed");
7456        };
7457        assert_eq!(restart_window, "0.5m");
7458        assert!(
7459            reason.contains("\"30s\""),
7460            "diagnostic must point at the canonical-form remediation, got {reason:?}"
7461        );
7462    }
7463
7464    #[test]
7465    fn validate_restart_window_rejects_leading_sign() {
7466        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
7467        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
7468        // and was caught by the `num < 0.0` arm which silently
7469        // returned `None`, dropping the author-supplied window). The
7470        // shared codec's digit-only gate rejects both with a unified
7471        // canonical-form diagnostic; the manifest-layer wrapper names
7472        // the offending value.
7473        for bad in ["+30s", "-30s"] {
7474            let err = caixa_with_restart_window(Some(bad))
7475                .validate_restart_window()
7476                .unwrap_err();
7477            assert!(
7478                matches!(
7479                    err,
7480                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
7481                        if restart_window == bad
7482                ),
7483                "got {err:?} for {bad:?}"
7484            );
7485        }
7486    }
7487
7488    #[test]
7489    fn validate_restart_window_rejects_unknown_unit() {
7490        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
7491        // unit dispatch surfaces an `unknown duration unit` reason;
7492        // the manifest-layer wrapper names the offending value.
7493        let err = caixa_with_restart_window(Some("30x"))
7494            .validate_restart_window()
7495            .unwrap_err();
7496        let ManifestError::RestartWindowMalformed {
7497            restart_window,
7498            reason,
7499        } = err
7500        else {
7501            panic!("expected RestartWindowMalformed for unknown unit");
7502        };
7503        assert_eq!(restart_window, "30x");
7504        assert!(
7505            reason.contains("unknown duration unit"),
7506            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
7507        );
7508    }
7509
7510    #[test]
7511    fn validate_restart_window_rejects_garbage() {
7512        // Pure non-numeric magnitude (`"abc"`) falls through to the
7513        // shared codec's narrower `"bad duration magnitude"` arm. Same
7514        // diagnostic shape as the codec-side
7515        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
7516        let err = caixa_with_restart_window(Some("abc"))
7517            .validate_restart_window()
7518            .unwrap_err();
7519        let ManifestError::RestartWindowMalformed {
7520            restart_window,
7521            reason,
7522        } = err
7523        else {
7524            panic!("expected RestartWindowMalformed for garbage");
7525        };
7526        assert_eq!(restart_window, "abc");
7527        assert!(
7528            reason.contains("bad duration magnitude"),
7529            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
7530        );
7531    }
7532
7533    #[test]
7534    fn validate_restart_window_rejects_empty_string() {
7535        // The empty-after-trim edge case — distinct from the `None`
7536        // canonical "omit the slot" shape. The shared codec's
7537        // digit-only gate refuses an empty magnitude; the manifest
7538        // layer names the offending `""` so the author can grep for
7539        // the literal empty value in their `caixa.lisp` and either
7540        // remove the slot (the canonical "no reset" shape) or pin a
7541        // positive duration.
7542        let err = caixa_with_restart_window(Some(""))
7543            .validate_restart_window()
7544            .unwrap_err();
7545        assert!(
7546            matches!(
7547                err,
7548                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7549                    if restart_window.is_empty()
7550            ),
7551            "got {err:?}"
7552        );
7553    }
7554
7555    #[test]
7556    fn validate_restart_window_diagnostic_carries_offending_value() {
7557        // Diagnostic-shape pin (peer with
7558        // `nome_invalid_diagnostic_carries_offending_nome` /
7559        // `versao_invalid_diagnostic_carries_offending_versao`): the
7560        // error names the offending raw `:restart-window` verbatim
7561        // with a non-empty shared-codec-shaped reason, so a `feira
7562        // lint` run can render the diagnostic without re-parsing.
7563        let err = caixa_with_restart_window(Some("1.5s"))
7564            .validate_restart_window()
7565            .unwrap_err();
7566        let ManifestError::RestartWindowMalformed {
7567            restart_window,
7568            reason,
7569        } = err
7570        else {
7571            panic!("expected RestartWindowMalformed variant");
7572        };
7573        assert_eq!(restart_window, "1.5s");
7574        assert!(
7575            !reason.is_empty(),
7576            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
7577        );
7578    }
7579
7580    #[test]
7581    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
7582        // Behavioral parity pin after the fold (`parse_window_inline`
7583        // deletion): the canonical `"60s"` still produces
7584        // `Duration::from_secs(60)` on the typed view — the fold is
7585        // semantically equivalent to the prior inline parser on the
7586        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
7587        // pin, narrowed to the parser-side contract.
7588        let c = caixa_with_restart_window(Some("60s"));
7589        let view = c.supervisor_view().expect("Supervisor kind has a view");
7590        assert_eq!(
7591            view.restart_window,
7592            Some(std::time::Duration::from_secs(60))
7593        );
7594    }
7595
7596    #[test]
7597    fn supervisor_view_soft_swallows_what_validate_rejects() {
7598        // Parity pin between the view-construction path and the
7599        // manifest-level validator: the same `"1.5s"` that surfaces
7600        // `RestartWindowMalformed` at `validate_restart_window` time
7601        // becomes `restart_window: None` on the typed view (the fold
7602        // preserves the existing best-effort shape of `supervisor_view`).
7603        // The contract is: a layout-verifier / `feira lint` flow that
7604        // cares about the malformed-window axis MUST consult
7605        // `validate_restart_window` — relying solely on the view's
7606        // `None` swallows the diagnostic silently. This pin makes the
7607        // expectation a typed invariant.
7608        let c = caixa_with_restart_window(Some("1.5s"));
7609        let view = c.supervisor_view().expect("Supervisor kind has a view");
7610        assert_eq!(
7611            view.restart_window, None,
7612            "view-construction path soft-swallows the parse error to None"
7613        );
7614        // And the manifest-level validator does NOT soft-swallow:
7615        assert!(
7616            matches!(
7617                c.validate_restart_window().unwrap_err(),
7618                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7619                    if restart_window == "1.5s"
7620            ),
7621            "validator must surface the offending value",
7622        );
7623    }
7624
7625    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
7626
7627    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
7628        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7629        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
7630        c.exe = exe.into_iter().map(String::from).collect();
7631        c.servicos = servicos.into_iter().map(String::from).collect();
7632        c
7633    }
7634
7635    #[test]
7636    fn validate_code_paths_accepts_canonical_template() {
7637        // The bare `Caixa::template` shape is the gate's identity element
7638        // on the canonical authoring shape — `:bibliotecas
7639        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
7640        // that the gate is non-disruptive against every existing caixa.
7641        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7642        c.validate_code_paths().unwrap();
7643    }
7644
7645    #[test]
7646    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
7647        // Positive control sweep: a canonical-shaped path on every slot
7648        // passes. Mirrors the peer
7649        // `behavior::validate_every_slot_relative_is_ok` pin.
7650        let c = caixa_with_code_paths(
7651            vec!["lib/demo.lisp", "lib/helpers.lisp"],
7652            vec!["exe/demo", "exe/tool"],
7653            vec!["servicos/demo.computeunit.yaml"],
7654        );
7655        c.validate_code_paths().unwrap();
7656    }
7657
7658    #[test]
7659    fn validate_code_paths_accepts_all_empty_lists() {
7660        // The empty-list identity element: every Caixa with no declared
7661        // code paths trivially passes (Supervisor / Aplicacao kinds rely
7662        // on this — the OwnCode gate already rejected them before the
7663        // path-shape gate runs in the layout, but the validator itself
7664        // must accept the empty shape).
7665        let c = caixa_with_code_paths(vec![], vec![], vec![]);
7666        c.validate_code_paths().unwrap();
7667    }
7668
7669    #[test]
7670    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
7671        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
7672        let err = c.validate_code_paths().unwrap_err();
7673        assert!(
7674            matches!(
7675                err,
7676                ManifestError::CodePathEmpty {
7677                    slot: ":bibliotecas"
7678                }
7679            ),
7680            "got {err:?}",
7681        );
7682    }
7683
7684    #[test]
7685    fn validate_code_paths_rejects_empty_exe_entry() {
7686        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
7687        let err = c.validate_code_paths().unwrap_err();
7688        assert!(
7689            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
7690            "got {err:?}",
7691        );
7692    }
7693
7694    #[test]
7695    fn validate_code_paths_rejects_empty_servicos_entry() {
7696        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
7697        let err = c.validate_code_paths().unwrap_err();
7698        assert!(
7699            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
7700            "got {err:?}",
7701        );
7702    }
7703
7704    #[test]
7705    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
7706        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
7707        // so an absolute path that resolves on disk silently passes the
7708        // layout's existence check — the canonical sandbox-escape on
7709        // the biblioteca axis.
7710        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7711        let err = c.validate_code_paths().unwrap_err();
7712        let ManifestError::CodePathAbsolute { slot, path } = err else {
7713            panic!("expected CodePathAbsolute, got {err:?}");
7714        };
7715        assert_eq!(slot, ":bibliotecas");
7716        assert_eq!(path, PathBuf::from("/etc/passwd"));
7717    }
7718
7719    #[test]
7720    fn validate_code_paths_rejects_absolute_exe_entry() {
7721        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
7722        let err = c.validate_code_paths().unwrap_err();
7723        let ManifestError::CodePathAbsolute { slot, path } = err else {
7724            panic!("expected CodePathAbsolute, got {err:?}");
7725        };
7726        assert_eq!(slot, ":exe");
7727        assert_eq!(path, PathBuf::from("/usr/bin/env"));
7728    }
7729
7730    #[test]
7731    fn validate_code_paths_rejects_absolute_servicos_entry() {
7732        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
7733        let err = c.validate_code_paths().unwrap_err();
7734        let ManifestError::CodePathAbsolute { slot, path } = err else {
7735            panic!("expected CodePathAbsolute, got {err:?}");
7736        };
7737        assert_eq!(slot, ":servicos");
7738        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
7739    }
7740
7741    #[test]
7742    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
7743        // Canonical "I want a lib from a sibling caixa" footgun on the
7744        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
7745        // downstream, so a leading `..` traverses to the parent of the
7746        // caixa root with no diagnostic at layout time if the resolved
7747        // target exists.
7748        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
7749        let err = c.validate_code_paths().unwrap_err();
7750        let ManifestError::CodePathParentEscape { slot, path } = err else {
7751            panic!("expected CodePathParentEscape, got {err:?}");
7752        };
7753        assert_eq!(slot, ":bibliotecas");
7754        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
7755    }
7756
7757    #[test]
7758    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
7759        // Mid-path `..` defeats the layout's component-aware
7760        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
7761        // `starts_with(<root>/exe)` is true, but the canonical resolution
7762        // lives outside the caixa root. Caught regardless of where the
7763        // `..` sits — mirrors the peer
7764        // `behavior::validate_rejects_parent_escape_mid_path` pin.
7765        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
7766        let err = c.validate_code_paths().unwrap_err();
7767        let ManifestError::CodePathParentEscape { slot, path } = err else {
7768            panic!("expected CodePathParentEscape, got {err:?}");
7769        };
7770        assert_eq!(slot, ":exe");
7771        assert_eq!(path, PathBuf::from("exe/../../escape"));
7772    }
7773
7774    #[test]
7775    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
7776        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
7777        let err = c.validate_code_paths().unwrap_err();
7778        let ManifestError::CodePathParentEscape { slot, path } = err else {
7779            panic!("expected CodePathParentEscape, got {err:?}");
7780        };
7781        assert_eq!(slot, ":servicos");
7782        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
7783    }
7784
7785    #[test]
7786    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
7787        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
7788        // `:servicos`. A manifest with malformed entries on all three
7789        // surfaces surfaces the `:bibliotecas` defect first, mirroring
7790        // the canonical declaration order
7791        // `Caixa::declared_foreign_code_slots` already establishes for
7792        // the foreign-code-slot diagnostic.
7793        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
7794        let err = c.validate_code_paths().unwrap_err();
7795        assert!(
7796            matches!(
7797                err,
7798                ManifestError::CodePathEmpty {
7799                    slot: ":bibliotecas"
7800                }
7801            ),
7802            "got {err:?}",
7803        );
7804    }
7805
7806    #[test]
7807    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
7808        // Within-slot precedence pin: empty → absolute → parent-escape,
7809        // matching the [`PathShapeViolation`] arm-ordering every peer
7810        // `is_sandboxed_relative_path` caller follows (b0c8389
7811        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
7812        // `:bibliotecas` list whose first entry is empty *and* whose
7813        // later entries are absolute/parent-escape surfaces the empty
7814        // arm first, on the lexicographically-earliest offending entry.
7815        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
7816        let err = c.validate_code_paths().unwrap_err();
7817        assert!(
7818            matches!(
7819                err,
7820                ManifestError::CodePathEmpty {
7821                    slot: ":bibliotecas"
7822                }
7823            ),
7824            "got {err:?}",
7825        );
7826    }
7827
7828    #[test]
7829    fn validate_code_paths_first_offender_per_slot_wins() {
7830        // Within a single slot, the first declaration-order offender
7831        // surfaces — pins that the gate is left-to-right deterministic
7832        // (peer of every `*_first_collision_*` pin on duplicate gates).
7833        let c = caixa_with_code_paths(
7834            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
7835            vec![],
7836            vec![],
7837        );
7838        let err = c.validate_code_paths().unwrap_err();
7839        let ManifestError::CodePathAbsolute { slot, path } = err else {
7840            panic!("expected CodePathAbsolute, got {err:?}");
7841        };
7842        assert_eq!(slot, ":bibliotecas");
7843        assert_eq!(path, PathBuf::from("/etc/escape"));
7844    }
7845
7846    #[test]
7847    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
7848        // Diagnostic-shape pin (peer with
7849        // `nome_invalid_diagnostic_carries_offending_nome` /
7850        // `versao_invalid_diagnostic_carries_offending_versao`): the
7851        // error's Display surfaces both the offending `:slot` tag and
7852        // the offending path verbatim, so a `feira lint` run can render
7853        // the diagnostic without re-parsing.
7854        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7855        let rendered = c.validate_code_paths().unwrap_err().to_string();
7856        assert!(
7857            rendered.contains(":bibliotecas"),
7858            "diagnostic must name the offending slot: {rendered}",
7859        );
7860        assert!(
7861            rendered.contains("/etc/passwd"),
7862            "diagnostic must quote the offending path: {rendered}",
7863        );
7864    }
7865
7866    #[test]
7867    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
7868        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
7869        // axis. Without the gate `feira build` re-parses the same lib
7870        // twice, wasting work and silently masking the author's intent
7871        // to declare a *second* biblioteca.
7872        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
7873        let err = c.validate_code_paths().unwrap_err();
7874        let ManifestError::CodePathDuplicate { slot, path } = err else {
7875            panic!("expected CodePathDuplicate, got {err:?}");
7876        };
7877        assert_eq!(slot, ":bibliotecas");
7878        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
7879    }
7880
7881    #[test]
7882    fn validate_code_paths_rejects_duplicate_exe_entry() {
7883        // Same footgun on the Binario surface. The future `caixa-flake`
7884        // emitter that materializes each `:exe` entry as a flake
7885        // `packages.<name>` derivation would collide on the duplicate
7886        // package key — surfaced here at the typed-validate layer with a
7887        // self-locating diagnostic instead.
7888        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
7889        let err = c.validate_code_paths().unwrap_err();
7890        let ManifestError::CodePathDuplicate { slot, path } = err else {
7891            panic!("expected CodePathDuplicate, got {err:?}");
7892        };
7893        assert_eq!(slot, ":exe");
7894        assert_eq!(path, PathBuf::from("exe/cli"));
7895    }
7896
7897    #[test]
7898    fn validate_code_paths_rejects_duplicate_servicos_entry() {
7899        // Same footgun on the Servico surface. The peer caixa-helm /
7900        // caixa-flux renderers refuse `:servicos.len() != 1` with the
7901        // narrower `UnsupportedServicoCount` diagnostic, but that
7902        // diagnostic surfaces "too many servicos" without naming
7903        // "duplicate entry" — the typed self-locating framing only lands
7904        // at this gate.
7905        let c = caixa_with_code_paths(
7906            vec![],
7907            vec![],
7908            vec![
7909                "servicos/demo.computeunit.yaml",
7910                "servicos/demo.computeunit.yaml",
7911            ],
7912        );
7913        let err = c.validate_code_paths().unwrap_err();
7914        let ManifestError::CodePathDuplicate { slot, path } = err else {
7915            panic!("expected CodePathDuplicate, got {err:?}");
7916        };
7917        assert_eq!(slot, ":servicos");
7918        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
7919    }
7920
7921    #[test]
7922    fn validate_code_paths_accepts_same_path_across_slots() {
7923        // Per-list scope pin: a `:bibliotecas` entry that happens to
7924        // collide with an `:exe` or `:servicos` entry as a *string* is
7925        // not a duplicate by this gate (each list gets its own HashSet),
7926        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
7927        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
7928        // shape on the dep axis). The structural `starts_with(<exe |
7929        // servicos>_dir)` fence at layout time prevents the realistic
7930        // cross-slot collision case from existing on disk, but the gate's
7931        // per-list scope is correct independent of that downstream fence.
7932        let c = caixa_with_code_paths(
7933            vec!["lib/x.lisp"],
7934            vec!["exe/x"],
7935            vec!["servicos/x.computeunit.yaml"],
7936        );
7937        c.validate_code_paths().unwrap();
7938    }
7939
7940    #[test]
7941    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
7942        // Within-slot ordering pin: structural defects (empty / absolute
7943        // / parent-escape) fire before the duplicate gate on the same
7944        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
7945        // surfaces the narrower `CodePathEmpty` for the empty entry
7946        // first, not the duplicate on the later pair — same arm-ordering
7947        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
7948        // `:autores` 86c769b, `:deps` 359fba5).
7949        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
7950        let err = c.validate_code_paths().unwrap_err();
7951        assert!(
7952            matches!(
7953                err,
7954                ManifestError::CodePathEmpty {
7955                    slot: ":bibliotecas"
7956                }
7957            ),
7958            "got {err:?}",
7959        );
7960    }
7961
7962    #[test]
7963    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
7964        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
7965        // duplicates surface before `:exe` duplicates, matching the
7966        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
7967        // order every peer per-slot diagnostic on this surface follows.
7968        let c = caixa_with_code_paths(
7969            vec!["lib/x.lisp", "lib/x.lisp"],
7970            vec!["exe/y", "exe/y"],
7971            vec![],
7972        );
7973        let err = c.validate_code_paths().unwrap_err();
7974        let ManifestError::CodePathDuplicate { slot, path } = err else {
7975            panic!("expected CodePathDuplicate, got {err:?}");
7976        };
7977        assert_eq!(slot, ":bibliotecas");
7978        assert_eq!(path, PathBuf::from("lib/x.lisp"));
7979    }
7980
7981    #[test]
7982    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
7983        // Diagnostic-shape pin (peer with
7984        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
7985        // on the structural arm): the duplicate-arm Display surfaces both
7986        // the offending `:slot` tag and the offending path verbatim, so a
7987        // `feira lint` run can render the diagnostic without re-parsing.
7988        let c = caixa_with_code_paths(
7989            vec![],
7990            vec![],
7991            vec![
7992                "servicos/demo.computeunit.yaml",
7993                "servicos/demo.computeunit.yaml",
7994            ],
7995        );
7996        let rendered = c.validate_code_paths().unwrap_err().to_string();
7997        assert!(
7998            rendered.contains(":servicos"),
7999            "diagnostic must name the offending slot: {rendered}",
8000        );
8001        assert!(
8002            rendered.contains("servicos/demo.computeunit.yaml"),
8003            "diagnostic must quote the offending path: {rendered}",
8004        );
8005    }
8006
8007    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
8008    //
8009    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
8010    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
8011    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
8012    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
8013    // at parse time — the same downstream consumer the peer `:behavior
8014    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
8015    // `:upgrade-from :state-change :script` (33cc830,
8016    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
8017    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
8018    // nix-built executable surface (`"exe/<name>"` shape per the canonical
8019    // [`crate::LayoutError::ExeOutsideDir`] error message and every
8020    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
8021    // is the `.computeunit.yaml` ComputeUnit-CR axis.
8022
8023    #[test]
8024    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
8025        // Canonical "I dragged the wrong file from the workspace tree"
8026        // footgun on the biblioteca axis. Without the gate `feira build`
8027        // hands the extensionless path to `tatara_lisp::read` and fails
8028        // with a parser-shaped diagnostic far from the source caixa.lisp,
8029        // with no field naming the offending `:bibliotecas` entry.
8030        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
8031            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8032            let err = c.validate_code_paths().unwrap_err();
8033            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8034                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8035            };
8036            assert_eq!(slot, ":bibliotecas");
8037            assert_eq!(path, PathBuf::from(relpath));
8038        }
8039    }
8040
8041    #[test]
8042    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
8043        // Wrong-extension sweep across common authoring footguns. Same
8044        // sweep posture as the peer
8045        // `behavior::validate_rejects_wrong_extension` (c97815a) and
8046        // `upgrade::tests::state_change_rejects_wrong_extension_script`
8047        // (33cc830) cases.
8048        for relpath in [
8049            "lib/demo.rs",
8050            "lib/demo.txt",
8051            "lib/demo.md",
8052            "lib/demo.json",
8053            "lib/demo.yaml",
8054            "lib/demo.toml",
8055            "lib/demo.lisp.bak",
8056            "lib/demo.lispx",
8057            "lib/demo.lis",
8058        ] {
8059            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8060            let err = c.validate_code_paths().unwrap_err();
8061            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8062                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8063            };
8064            assert_eq!(slot, ":bibliotecas");
8065            assert_eq!(path, PathBuf::from(relpath));
8066        }
8067    }
8068
8069    #[test]
8070    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
8071        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
8072        // contract. An uppercase `.LISP` shape that the layout's existence
8073        // check would (case-insensitively, on case-insensitive volumes)
8074        // match the on-disk file still mismatches the canonical form the
8075        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
8076        // contract. Mirrors the peer
8077        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
8078        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
8079        // (33cc830) sweeps.
8080        for relpath in [
8081            "lib/demo.LISP",
8082            "lib/demo.Lisp",
8083            "lib/demo.LiSp",
8084            "lib/demo.lISP",
8085        ] {
8086            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8087            let err = c.validate_code_paths().unwrap_err();
8088            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8089                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8090            };
8091            assert_eq!(slot, ":bibliotecas");
8092            assert_eq!(path, PathBuf::from(relpath));
8093        }
8094    }
8095
8096    #[test]
8097    fn validate_code_paths_accepts_canonical_lisp_shapes() {
8098        // Positive-control sweep through every canonical authoring shape
8099        // every in-tree fixture and the `Caixa::template` scaffold use.
8100        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
8101        // (c97815a) and the lifted predicate's own
8102        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
8103        // (33cc830).
8104        for relpath in [
8105            "lib/demo.lisp",
8106            "lib/handlers.lisp",
8107            "lib/migrations/v01-to-v02.lisp",
8108            "demo.lisp",
8109            "a.lisp",
8110            "./lib/demo.lisp",
8111            "lib/./handlers.lisp",
8112            "lib/migrations/v.0.1.lisp",
8113        ] {
8114            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8115            c.validate_code_paths()
8116                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8117        }
8118    }
8119
8120    #[test]
8121    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
8122        // The file-type gate is per-slot — only `:bibliotecas` carries the
8123        // tatara-lisp-source contract. An extensionless `:exe` entry
8124        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
8125        // canonical shapes every in-tree fixture uses, and must continue
8126        // to pass validate. Pins that a future tightening that broadens
8127        // the `.lisp` gate to either axis surfaces as a test failure
8128        // rather than as a silent breaking change to existing valid
8129        // manifests.
8130        let c = caixa_with_code_paths(
8131            vec![],
8132            vec!["exe/demo", "exe/tool"],
8133            vec!["servicos/demo.computeunit.yaml"],
8134        );
8135        c.validate_code_paths().unwrap();
8136    }
8137
8138    #[test]
8139    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
8140        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
8141        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
8142        // sandbox-shape diagnostic first (the `.lisp` remediation would
8143        // be misleading when the offending path can never resolve under
8144        // the caixa root anyway). Mirrors the peer
8145        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
8146        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
8147        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
8148        // on `:upgrade-from :state-change :script` (33cc830).
8149        //
8150        // Empty wins (the strictly-smaller-scope structural arm).
8151        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
8152        assert!(
8153            matches!(
8154                c.validate_code_paths().unwrap_err(),
8155                ManifestError::CodePathEmpty {
8156                    slot: ":bibliotecas"
8157                }
8158            ),
8159            "empty must win over non-lisp-extension",
8160        );
8161        // Absolute wins (the path can't resolve under the caixa root).
8162        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8163        let err = c.validate_code_paths().unwrap_err();
8164        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8165            panic!("absolute must win over non-lisp-extension, got {err:?}");
8166        };
8167        assert_eq!(slot, ":bibliotecas");
8168        // ParentEscape wins (the path escapes the caixa root).
8169        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
8170        let err = c.validate_code_paths().unwrap_err();
8171        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8172            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
8173        };
8174        assert_eq!(slot, ":bibliotecas");
8175    }
8176
8177    #[test]
8178    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
8179        // Within-slot precedence pin: the per-entry file-type shape gate
8180        // fires before the cross-entry duplicate gate, so the narrower
8181        // structural defect dominates the uniqueness diagnostic. A
8182        // `("lib/x.txt" "lib/x.txt")` shape surfaces
8183        // `CodePathNonLispExtension` on the first entry rather than
8184        // `CodePathDuplicate` on the pair — same posture every per-entry
8185        // shape-gate-precedes-duplicate cascade follows on this surface
8186        // (the empty / absolute / parent-escape arms already precede the
8187        // duplicate arm; the lifted file-type arm joins that set).
8188        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
8189        let err = c.validate_code_paths().unwrap_err();
8190        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8191            panic!("expected CodePathNonLispExtension, got {err:?}");
8192        };
8193        assert_eq!(slot, ":bibliotecas");
8194        assert_eq!(path, PathBuf::from("lib/x.txt"));
8195    }
8196
8197    #[test]
8198    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
8199        // Diagnostic-shape pin (peer with
8200        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8201        // on the sandbox-shape arms and
8202        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
8203        // on the duplicate arm): the file-type-arm Display surfaces both
8204        // the offending `:slot` tag, the offending path verbatim, and the
8205        // expected `.lisp` extension named in the remediation text, so a
8206        // `feira lint` run can render the diagnostic without re-parsing.
8207        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
8208        let rendered = c.validate_code_paths().unwrap_err().to_string();
8209        assert!(
8210            rendered.contains(":bibliotecas"),
8211            "diagnostic must name the offending slot: {rendered}",
8212        );
8213        assert!(
8214            rendered.contains("lib/demo.rs"),
8215            "diagnostic must quote the offending path: {rendered}",
8216        );
8217        assert!(
8218            rendered.contains(".lisp"),
8219            "diagnostic must name the expected extension: {rendered}",
8220        );
8221    }
8222
8223    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
8224    //
8225    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
8226    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
8227    // contract. The peer caixa-helm / caixa-flux renderers consume each
8228    // `:servicos` entry through `serde_yaml::from_str` as a typed
8229    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
8230    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
8231    // axis `Path::extension` can't express on its own.
8232
8233    #[test]
8234    fn validate_code_paths_rejects_no_extension_servicos_entry() {
8235        // Canonical "I dragged the wrong file from the workspace tree"
8236        // footgun on the Servico axis. Without the gate the peer
8237        // caixa-helm / caixa-flux renderers hand the extensionless path
8238        // to `serde_yaml::from_str` and fail with a parser-shaped
8239        // diagnostic far from the source caixa.lisp, with no field
8240        // naming the offending `:servicos` entry.
8241        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
8242            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8243            let err = c.validate_code_paths().unwrap_err();
8244            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8245                panic!(
8246                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8247                     got {err:?}"
8248                );
8249            };
8250            assert_eq!(slot, ":servicos");
8251            assert_eq!(path, PathBuf::from(relpath));
8252        }
8253    }
8254
8255    #[test]
8256    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
8257        // Wrong-extension sweep across common authoring footguns on the
8258        // Servico axis. Bare `.yaml` is the canonical "I forgot the
8259        // `.computeunit` segment" typo; the off-by-one-segment shapes
8260        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
8261        // bare `Path::extension` view but mismatch the typed compound
8262        // suffix the renderers' `serde_yaml::from_str` consumer demands.
8263        // Same sweep-posture as the peer
8264        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
8265        // (64772a9) on the sibling tatara-lisp-source axis.
8266        for relpath in [
8267            "servicos/demo.yaml",
8268            "servicos/demo.yml",
8269            "servicos/demo.json",
8270            "servicos/demo.toml",
8271            "servicos/demo.txt",
8272            "servicos/demo.computeunit.yaml.bak",
8273            "servicos/demo.computeunit.yam",
8274            "servicos/demo.computeunit",
8275            "servicos/demo-computeunit.yaml",
8276            "servicos/demo_computeunit.yaml",
8277        ] {
8278            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8279            let err = c.validate_code_paths().unwrap_err();
8280            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8281                panic!(
8282                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8283                     got {err:?}"
8284                );
8285            };
8286            assert_eq!(slot, ":servicos");
8287            assert_eq!(path, PathBuf::from(relpath));
8288        }
8289    }
8290
8291    #[test]
8292    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
8293        // Case-sensitivity sweep — pins the strict lowercase
8294        // `.computeunit.yaml` contract. A case-folded shape that the
8295        // layout's existence check would (case-insensitively, on
8296        // case-insensitive volumes) match the on-disk file still
8297        // mismatches the canonical form the codec emits, breaking the
8298        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
8299        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
8300        // (64772a9) sweep on the sibling tatara-lisp-source axis.
8301        for relpath in [
8302            "servicos/demo.ComputeUnit.yaml",
8303            "servicos/demo.COMPUTEUNIT.yaml",
8304            "servicos/demo.computeunit.YAML",
8305            "servicos/demo.computeunit.Yaml",
8306            "servicos/demo.COMPUTEUNIT.YAML",
8307        ] {
8308            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8309            let err = c.validate_code_paths().unwrap_err();
8310            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8311                panic!(
8312                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8313                     got {err:?}"
8314                );
8315            };
8316            assert_eq!(slot, ":servicos");
8317            assert_eq!(path, PathBuf::from(relpath));
8318        }
8319    }
8320
8321    #[test]
8322    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
8323        // Degenerate hidden-file shape: a file name exactly equal to the
8324        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
8325        // the structural "Servico declared with no identity" footgun.
8326        // The substrate identifies each ComputeUnit by the file-stem
8327        // segment that precedes `.computeunit.yaml` (the rendered
8328        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
8329        // the M3 `:contratos` membership lookup), so an empty stem
8330        // leaves the Servico unidentifiable. Pinned at the typed-axis
8331        // level so a future regression that drops the `name.len() >
8332        // SUFFIX.len()` bound at the predicate surfaces here, not
8333        // piecemeal as a `lareira-` chart-name collision at render time.
8334        for relpath in ["servicos/.computeunit.yaml"] {
8335            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8336            let err = c.validate_code_paths().unwrap_err();
8337            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8338                panic!(
8339                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8340                     got {err:?}"
8341                );
8342            };
8343            assert_eq!(slot, ":servicos");
8344            assert_eq!(path, PathBuf::from(relpath));
8345        }
8346    }
8347
8348    #[test]
8349    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
8350        // Positive-control sweep through every canonical authoring shape
8351        // every in-tree fixture and the `Caixa::template` scaffold use.
8352        // Mirrors the peer
8353        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
8354        // and the lifted predicate's own
8355        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
8356        // render.rs.
8357        for relpath in [
8358            "servicos/demo.computeunit.yaml",
8359            "servicos/hello-rio.computeunit.yaml",
8360            "servicos/my-service.computeunit.yaml",
8361            "servicos/a.computeunit.yaml",
8362            "./servicos/demo.computeunit.yaml",
8363            "servicos/./demo.computeunit.yaml",
8364            "servicos/sub/nested.computeunit.yaml",
8365            "servicos/v0.1.computeunit.yaml",
8366        ] {
8367            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8368            c.validate_code_paths()
8369                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8370        }
8371    }
8372
8373    #[test]
8374    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
8375        // The file-type gate is per-slot — only `:servicos` carries the
8376        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
8377        // entry and an extensionless `:exe` entry are the canonical
8378        // shapes every in-tree fixture uses, and must continue to pass
8379        // validate. Peer of
8380        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
8381        // (64772a9) — together pin that the typed
8382        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
8383        // cross-axis leakage in either direction.
8384        let c = caixa_with_code_paths(
8385            vec!["lib/demo.lisp"],
8386            vec!["exe/demo", "exe/tool"],
8387            vec!["servicos/demo.computeunit.yaml"],
8388        );
8389        c.validate_code_paths().unwrap();
8390    }
8391
8392    #[test]
8393    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
8394        // Cross-arm precedence pin: a `:servicos` entry that is *both*
8395        // sandbox-escaping and wrong-extension surfaces the more
8396        // fundamental sandbox-shape diagnostic first (the
8397        // `.computeunit.yaml` remediation would be misleading when the
8398        // offending path can never resolve under the caixa root
8399        // anyway). Mirrors the peer
8400        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
8401        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
8402        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
8403        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
8404        // table establishes.
8405        //
8406        // Empty wins (the strictly-smaller-scope structural arm).
8407        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
8408        assert!(
8409            matches!(
8410                c.validate_code_paths().unwrap_err(),
8411                ManifestError::CodePathEmpty { slot: ":servicos" }
8412            ),
8413            "empty must win over non-computeunit-yaml-extension",
8414        );
8415        // Absolute wins (the path can't resolve under the caixa root).
8416        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
8417        let err = c.validate_code_paths().unwrap_err();
8418        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8419            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
8420        };
8421        assert_eq!(slot, ":servicos");
8422        // ParentEscape wins (the path escapes the caixa root).
8423        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
8424        let err = c.validate_code_paths().unwrap_err();
8425        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8426            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
8427        };
8428        assert_eq!(slot, ":servicos");
8429    }
8430
8431    #[test]
8432    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
8433        // Within-slot precedence pin: the per-entry file-type shape gate
8434        // fires before the cross-entry duplicate gate, so the narrower
8435        // structural defect dominates the uniqueness diagnostic. A
8436        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
8437        // `CodePathNonComputeUnitYamlExtension` on the first entry
8438        // rather than `CodePathDuplicate` on the pair — same posture
8439        // every per-entry shape-gate-precedes-duplicate cascade follows
8440        // on this surface, peer of the 64772a9 `:bibliotecas`
8441        // `("lib/x.txt" "lib/x.txt")` ordering.
8442        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
8443        let err = c.validate_code_paths().unwrap_err();
8444        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8445            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
8446        };
8447        assert_eq!(slot, ":servicos");
8448        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
8449    }
8450
8451    #[test]
8452    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
8453     {
8454        // Diagnostic-shape pin (peer with
8455        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
8456        // on the sibling tatara-lisp-source axis): the file-type-arm
8457        // Display surfaces both the offending `:slot` tag, the
8458        // offending path verbatim, and the expected
8459        // `.computeunit.yaml` compound suffix named in the remediation
8460        // text, so a `feira lint` run can render the diagnostic without
8461        // re-parsing.
8462        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
8463        let rendered = c.validate_code_paths().unwrap_err().to_string();
8464        assert!(
8465            rendered.contains(":servicos"),
8466            "diagnostic must name the offending slot: {rendered}",
8467        );
8468        assert!(
8469            rendered.contains("servicos/demo.yaml"),
8470            "diagnostic must quote the offending path: {rendered}",
8471        );
8472        assert!(
8473            rendered.contains(".computeunit.yaml"),
8474            "diagnostic must name the expected compound suffix: {rendered}",
8475        );
8476    }
8477
8478    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
8479
8480    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
8481        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8482        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
8483        c
8484    }
8485
8486    #[test]
8487    fn validate_etiquetas_accepts_empty_list() {
8488        // The empty-list identity: every caixa with no declared tags
8489        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
8490        // so the gate is non-disruptive against every existing manifest.
8491        let c = caixa_with_etiquetas(vec![]);
8492        c.validate_etiquetas().unwrap();
8493    }
8494
8495    #[test]
8496    fn validate_etiquetas_accepts_canonical_forms() {
8497        // Positive control sweep: a canonical-shaped non-empty distinct
8498        // tag list passes, mirroring the example checkout-aplicacao
8499        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
8500        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
8501        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
8502        c.validate_etiquetas().unwrap();
8503    }
8504
8505    #[test]
8506    fn validate_etiquetas_rejects_empty_entry() {
8507        // Canonical paste-from-blank-doc footgun. Without the gate the
8508        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
8509        // no-op tag indexing nothing in the future caixa-registry.
8510        let c = caixa_with_etiquetas(vec![""]);
8511        let err = c.validate_etiquetas().unwrap_err();
8512        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8513    }
8514
8515    #[test]
8516    fn validate_etiquetas_rejects_duplicate_entry() {
8517        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
8518        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
8519        // collect at chart render — a "second wins / one silently
8520        // disappears" shape divergent from every peer typed-graph set
8521        // gate. The duplicate-arm names the offending tag verbatim.
8522        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8523        let err = c.validate_etiquetas().unwrap_err();
8524        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8525            panic!("expected EtiquetaDuplicate, got {err:?}");
8526        };
8527        assert_eq!(etiqueta, "demo");
8528    }
8529
8530    #[test]
8531    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
8532        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
8533        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
8534        // structural "this entry has no value" defect dominates the
8535        // cross-entry uniqueness diagnostic. Mirrors the peer
8536        // empty-before-duplicate cascades on `:caracteristicas`
8537        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8538        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
8539        // `MembroDuplicate`).
8540        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
8541        let err = c.validate_etiquetas().unwrap_err();
8542        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8543    }
8544
8545    #[test]
8546    fn validate_etiquetas_duplicate_reports_first_collision() {
8547        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8548        // duplicate (the lexicographically-earliest offending position
8549        // — the second `"a"` at index 2 collides with the first `"a"`
8550        // at index 0), not the later `"b"` collision at index 3,
8551        // peer with every other first-collision diagnostic posture on
8552        // this surface (`validate_load_singularity_reports_first_collision`,
8553        // `validate_cleanup_singularity_reports_first_collision`).
8554        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
8555        let err = c.validate_etiquetas().unwrap_err();
8556        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8557            panic!("expected EtiquetaDuplicate, got {err:?}");
8558        };
8559        assert_eq!(etiqueta, "a");
8560    }
8561
8562    #[test]
8563    fn validate_etiquetas_case_sensitive() {
8564        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
8565        // mirroring the peer `:membros :caixa` / `:children :caixa`
8566        // exact-string-match discipline. The shape gate this routine
8567        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
8568        // grammar) accepts mixed case — crates.io's keyword rule is
8569        // "case-insensitive" at the index layer but admits mixed case
8570        // at the entry layer (the canonical Helm chart `keywords:`
8571        // shape is lowercase by convention, but the grammar admits
8572        // uppercase). Case-sensitivity at the duplicate-set layer
8573        // remains structural — two distinct strings are two distinct
8574        // entries.
8575        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
8576        c.validate_etiquetas().unwrap();
8577    }
8578
8579    #[test]
8580    fn validate_etiquetas_diagnostic_carries_offending_tag() {
8581        // Diagnostic-shape pin (peer with
8582        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
8583        // the error's Display surfaces the offending tag verbatim, so a
8584        // `feira lint` run can render the diagnostic without re-parsing
8585        // and the author can grep their caixa.lisp for the offending
8586        // value.
8587        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8588        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8589        assert!(
8590            rendered.contains(":etiquetas"),
8591            "diagnostic must name the offending slot: {rendered}",
8592        );
8593        assert!(
8594            rendered.contains("demo"),
8595            "diagnostic must quote the offending tag: {rendered}",
8596        );
8597    }
8598
8599    #[test]
8600    fn validate_etiquetas_rejects_leading_whitespace_entry() {
8601        // Canonical paste-from-aligned-doc footgun. Without the shape
8602        // gate `" mesh"` silently passed validate and landed as a
8603        // YAML plain-style scalar with leading whitespace in the
8604        // rendered Chart.yaml `keywords:` array — every YAML 1.2
8605        // dumper trims leading whitespace from plain-style scalars,
8606        // so the authored space round-tripped inconsistently back
8607        // through `caixa.lisp`. Mirrors the peer
8608        // `validate_autores_rejects_leading_whitespace_entry`.
8609        let c = caixa_with_etiquetas(vec![" mesh"]);
8610        let err = c.validate_etiquetas().unwrap_err();
8611        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8612            panic!("expected EtiquetaInvalid, got {err:?}");
8613        };
8614        assert_eq!(etiqueta, " mesh");
8615        assert!(reason.contains("whitespace"), "got: {reason}");
8616    }
8617
8618    #[test]
8619    fn validate_etiquetas_rejects_embedded_newline_entry() {
8620        // Canonical paste-from-multiline-doc footgun — the author
8621        // pasted a multi-tag block into one `:etiquetas` entry
8622        // instead of splitting into one entry per tag. Without the
8623        // shape gate `"mesh\nhttp"` silently passed validate and
8624        // landed as a YAML-illegal multi-line scalar in the rendered
8625        // Chart.yaml `keywords:` array.
8626        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8627        let err = c.validate_etiquetas().unwrap_err();
8628        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8629            panic!("expected EtiquetaInvalid, got {err:?}");
8630        };
8631        assert_eq!(etiqueta, "mesh\nhttp");
8632        assert!(reason.contains("newline"), "got: {reason}");
8633    }
8634
8635    #[test]
8636    fn validate_etiquetas_rejects_embedded_comma_entry() {
8637        // Canonical CSV-list-separator-confusion footgun: the author
8638        // confused the CSV-style separator convention with the
8639        // `:etiquetas` list grammar. Without the shape gate
8640        // `"mesh,http,grpc"` silently passed validate and landed as a
8641        // single malformed search tag in the rendered Chart.yaml
8642        // `keywords:` array — Artifact Hub's keyword index would
8643        // either silently drop the tag or index it as
8644        // `mesh,http,grpc` instead of three separate tags.
8645        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
8646        let err = c.validate_etiquetas().unwrap_err();
8647        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8648            panic!("expected EtiquetaInvalid, got {err:?}");
8649        };
8650        assert_eq!(etiqueta, "mesh,http,grpc");
8651        assert!(reason.contains('`'), "got: {reason}");
8652        assert!(reason.contains(','), "got: {reason}");
8653    }
8654
8655    #[test]
8656    fn validate_etiquetas_rejects_embedded_slash_entry() {
8657        // Canonical path-separator-confusion footgun: the author
8658        // confused namespace-path notation with the keyword grammar.
8659        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
8660        let err = c.validate_etiquetas().unwrap_err();
8661        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8662            panic!("expected EtiquetaInvalid, got {err:?}");
8663        };
8664        assert_eq!(etiqueta, "caixa/servico");
8665        assert!(reason.contains('/'), "got: {reason}");
8666    }
8667
8668    #[test]
8669    fn validate_etiquetas_rejects_leading_digit_entry() {
8670        // Canonical paste-from-numbered-list footgun: the author
8671        // copied `1. mesh` from a numbered doc and the `1` leaked
8672        // into the tag.
8673        let c = caixa_with_etiquetas(vec!["1mesh"]);
8674        let err = c.validate_etiquetas().unwrap_err();
8675        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8676            panic!("expected EtiquetaInvalid, got {err:?}");
8677        };
8678        assert_eq!(etiqueta, "1mesh");
8679        assert!(reason.contains("digit"), "got: {reason}");
8680    }
8681
8682    #[test]
8683    fn validate_etiquetas_rejects_leading_hyphen_entry() {
8684        // Canonical kebab-leak footgun.
8685        let c = caixa_with_etiquetas(vec!["-foo"]);
8686        let err = c.validate_etiquetas().unwrap_err();
8687        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8688            panic!("expected EtiquetaInvalid, got {err:?}");
8689        };
8690        assert_eq!(etiqueta, "-foo");
8691        assert!(reason.contains('-'), "got: {reason}");
8692    }
8693
8694    #[test]
8695    fn validate_etiquetas_rejects_non_ascii_entry() {
8696        // Canonical paste-from-Unicode-doc footgun. Every legitimate
8697        // search tag is strict ASCII; raw non-ASCII silently
8698        // round-trips inconsistently across NFC/NFD normalization on
8699        // APFS / case-folding filesystems and breaks the Artifact Hub
8700        // keyword search index lookup.
8701        let c = caixa_with_etiquetas(vec!["café"]);
8702        let err = c.validate_etiquetas().unwrap_err();
8703        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8704            panic!("expected EtiquetaInvalid, got {err:?}");
8705        };
8706        assert_eq!(etiqueta, "café");
8707        assert!(reason.contains("non-ASCII"), "got: {reason}");
8708    }
8709
8710    #[test]
8711    fn validate_etiquetas_rejects_period_entry() {
8712        // Canonical namespace-confusion / version-suffix footgun
8713        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
8714        // excludes `.` from the continuation set even though the
8715        // sibling `:caracteristicas` axis (Cargo's feature-name
8716        // grammar) admits it. Tighter than the sibling axis, peer
8717        // with Cargo's own crates.io keyword shape.
8718        let c = caixa_with_etiquetas(vec!["http.1"]);
8719        let err = c.validate_etiquetas().unwrap_err();
8720        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8721            panic!("expected EtiquetaInvalid, got {err:?}");
8722        };
8723        assert_eq!(etiqueta, "http.1");
8724        assert!(reason.contains('.'), "got: {reason}");
8725    }
8726
8727    #[test]
8728    fn validate_etiquetas_empty_takes_precedence_over_shape() {
8729        // Per-entry empty-first cascade pin: an entry that is both
8730        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
8731        // narrower "this entry has no value" structural defect
8732        // dominates the broader shape-predicate diagnostic). The
8733        // empty arm fires before the shape predicate is consulted,
8734        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
8735        // cascade established on the sibling universal-axis Vec<String>
8736        // surface.
8737        let c = caixa_with_etiquetas(vec![""]);
8738        let err = c.validate_etiquetas().unwrap_err();
8739        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8740    }
8741
8742    #[test]
8743    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
8744        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
8745        // entry that is malformed surfaces `EtiquetaInvalid` even when
8746        // a later entry would have collided on duplicate. The
8747        // per-entry shape arm fires inside the same loop iteration as
8748        // the empty arm, before the seen-set insert at end-of-iteration
8749        // — structural per-entry defects dominate the cross-entry
8750        // uniqueness diagnostic. Mirrors the peer
8751        // `validate_autores_shape_takes_precedence_over_duplicate`.
8752        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
8753        let err = c.validate_etiquetas().unwrap_err();
8754        assert!(
8755            matches!(err, ManifestError::EtiquetaInvalid { .. }),
8756            "got {err:?}",
8757        );
8758    }
8759
8760    #[test]
8761    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
8762        // Diagnostic-shape pin on the new shape arm (peer with
8763        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
8764        // the rendered Display surfaces both the offending slot name
8765        // and the offending value verbatim, so a `feira lint` run
8766        // points the author at the exact `:etiquetas` entry to fix.
8767        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8768        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8769        assert!(
8770            rendered.contains(":etiquetas"),
8771            "diagnostic must name the offending slot: {rendered}",
8772        );
8773        assert!(
8774            rendered.contains("mesh\\nhttp"),
8775            "diagnostic must quote the offending value (debug-escaped): {rendered}",
8776        );
8777    }
8778
8779    #[test]
8780    fn validate_etiquetas_rejects_at_21_byte_boundary() {
8781        // The 20-byte cap pin — boundary-exceeding case rejected,
8782        // boundary-accepting case passes. Mirrors the peer
8783        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
8784        // side pin, surfaced at the per-axis caller so the cap
8785        // propagates through validate end-to-end. Constructed as a
8786        // single all-`a` token so only the cap arm fires.
8787        let max_ok = "a".repeat(20);
8788        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
8789        c.validate_etiquetas().unwrap();
8790        let too_long = "a".repeat(21);
8791        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
8792        let err = c.validate_etiquetas().unwrap_err();
8793        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
8794            panic!("expected EtiquetaInvalid, got {err:?}");
8795        };
8796        assert!(reason.contains("20"), "got: {reason}");
8797        assert!(reason.contains("21"), "got: {reason}");
8798    }
8799
8800    #[test]
8801    fn validate_etiquetas_accepts_canonical_shaped_forms() {
8802        // Positive control sweep: every canonical-shaped tag from the
8803        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
8804        // example fixtures plus the substrate-fixed tags caixa-helm
8805        // unions in at chart render. Drift between this list and the
8806        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
8807        // sweep surfaces here — one source of truth for the rule.
8808        let c = caixa_with_etiquetas(vec![
8809            "example",
8810            "aplicacao",
8811            "mesh",
8812            "ecommerce",
8813            "demo",
8814            "infrastructure",
8815            "aws",
8816            "akeyless",
8817            "pangea-native",
8818            "hello-world",
8819            "wasm",
8820            "rust",
8821            "tatara-lisp",
8822            "caixa-servico",
8823            "lareira",
8824        ]);
8825        c.validate_etiquetas().unwrap();
8826    }
8827
8828    // ── validate_autores — universal-axis maintainer shape ────────────
8829
8830    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
8831        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8832        c.autores = autores.into_iter().map(String::from).collect();
8833        c
8834    }
8835
8836    #[test]
8837    fn validate_autores_accepts_empty_list() {
8838        // The empty-list identity: `Caixa::template` emits `:autores ()`,
8839        // so the gate is non-disruptive against every existing manifest.
8840        let c = caixa_with_autores(vec![]);
8841        c.validate_autores().unwrap();
8842    }
8843
8844    #[test]
8845    fn validate_autores_accepts_canonical_forms() {
8846        // Positive control sweep: every canonical-shaped non-empty
8847        // distinct maintainer list passes — the hello-rio / checkout-
8848        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
8849        // multi-author shape downstream packaging surfaces emit.
8850        let c = caixa_with_autores(vec!["pleme-io"]);
8851        c.validate_autores().unwrap();
8852        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
8853        c.validate_autores().unwrap();
8854    }
8855
8856    #[test]
8857    fn validate_autores_rejects_empty_entry() {
8858        // Canonical paste-from-blank-doc footgun. Without the gate the
8859        // empty entry rendered as `maintainers: [{name: "", email: null}]`
8860        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
8861        // to.
8862        let c = caixa_with_autores(vec![""]);
8863        let err = c.validate_autores().unwrap_err();
8864        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8865    }
8866
8867    #[test]
8868    fn validate_autores_rejects_duplicate_entry() {
8869        // Canonical copy-paste-the-wrong-author footgun. Unlike the
8870        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
8871        // dedups the rendered `keywords:` array), the `maintainers:`
8872        // rendering has *no* dedup — duplicates stack verbatim. The
8873        // duplicate-arm names the offending author verbatim.
8874        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
8875        let err = c.validate_autores().unwrap_err();
8876        let ManifestError::AutorDuplicate { autor } = err else {
8877            panic!("expected AutorDuplicate, got {err:?}");
8878        };
8879        assert_eq!(autor, "pleme-io");
8880    }
8881
8882    #[test]
8883    fn validate_autores_empty_takes_precedence_over_duplicate() {
8884        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
8885        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
8886        // "this entry has no value" defect dominates the cross-entry
8887        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
8888        // cascades on `:etiquetas` (`EtiquetaEmpty` before
8889        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
8890        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8891        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
8892        // `MembroDuplicate`).
8893        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
8894        let err = c.validate_autores().unwrap_err();
8895        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8896    }
8897
8898    #[test]
8899    fn validate_autores_duplicate_reports_first_collision() {
8900        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8901        // duplicate (the lexicographically-earliest offending position
8902        // — the second `"a"` at index 2 collides with the first `"a"`
8903        // at index 0), not the later `"b"` collision at index 3,
8904        // peer with every other first-collision diagnostic posture on
8905        // this surface.
8906        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
8907        let err = c.validate_autores().unwrap_err();
8908        let ManifestError::AutorDuplicate { autor } = err else {
8909            panic!("expected AutorDuplicate, got {err:?}");
8910        };
8911        assert_eq!(autor, "a");
8912    }
8913
8914    #[test]
8915    fn validate_autores_case_sensitive() {
8916        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
8917        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
8918        // / `:children :caixa` exact-string-match discipline.
8919        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
8920        c.validate_autores().unwrap();
8921    }
8922
8923    #[test]
8924    fn validate_autores_diagnostic_carries_offending_author() {
8925        // Diagnostic-shape pin (peer with
8926        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
8927        // error's Display surfaces the offending author verbatim, so a
8928        // `feira lint` run can render the diagnostic without re-parsing
8929        // and the author can grep their caixa.lisp for the offending
8930        // value.
8931        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
8932        let rendered = c.validate_autores().unwrap_err().to_string();
8933        assert!(
8934            rendered.contains(":autores"),
8935            "diagnostic must name the offending slot: {rendered}",
8936        );
8937        assert!(
8938            rendered.contains("pleme-io"),
8939            "diagnostic must quote the offending author: {rendered}",
8940        );
8941    }
8942
8943    #[test]
8944    fn validate_autores_rejects_leading_whitespace_entry() {
8945        // Canonical paste-from-aligned-doc footgun. Without the shape
8946        // gate `" pleme-io"` silently passed validate and landed as a
8947        // YAML plain-style scalar with leading whitespace in the
8948        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
8949        // dumper trims leading whitespace from plain-style scalars, so
8950        // the authored space round-tripped inconsistently back through
8951        // `caixa.lisp`. Mirrors the peer
8952        // `validate_descricao_rejects_leading_whitespace`.
8953        let c = caixa_with_autores(vec![" pleme-io"]);
8954        let err = c.validate_autores().unwrap_err();
8955        let ManifestError::AutorInvalid { autor, reason } = err else {
8956            panic!("expected AutorInvalid, got {err:?}");
8957        };
8958        assert_eq!(autor, " pleme-io");
8959        assert!(reason.contains("whitespace"), "got: {reason}");
8960    }
8961
8962    #[test]
8963    fn validate_autores_rejects_trailing_whitespace_entry() {
8964        // Canonical paste-from-doc footgun.
8965        let c = caixa_with_autores(vec!["pleme-io "]);
8966        let err = c.validate_autores().unwrap_err();
8967        let ManifestError::AutorInvalid { autor, reason } = err else {
8968            panic!("expected AutorInvalid, got {err:?}");
8969        };
8970        assert_eq!(autor, "pleme-io ");
8971        assert!(reason.contains("whitespace"), "got: {reason}");
8972    }
8973
8974    #[test]
8975    fn validate_autores_rejects_embedded_newline_entry() {
8976        // Canonical paste-from-multiline-doc footgun — the author
8977        // pasted a multi-line block of author records into one
8978        // `:autores` entry instead of splitting into one entry per
8979        // author. Without the shape gate `"alice\nbob"` silently
8980        // passed validate and landed as a YAML-illegal multi-line
8981        // scalar in the rendered Chart.yaml `maintainers:` array.
8982        let c = caixa_with_autores(vec!["alice\nbob"]);
8983        let err = c.validate_autores().unwrap_err();
8984        let ManifestError::AutorInvalid { autor, reason } = err else {
8985            panic!("expected AutorInvalid, got {err:?}");
8986        };
8987        assert_eq!(autor, "alice\nbob");
8988        assert!(reason.contains("newline"), "got: {reason}");
8989    }
8990
8991    #[test]
8992    fn validate_autores_rejects_embedded_carriage_return_entry() {
8993        // Canonical paste-from-Windows-CRLF-doc footgun.
8994        let c = caixa_with_autores(vec!["alice\rbob"]);
8995        let err = c.validate_autores().unwrap_err();
8996        let ManifestError::AutorInvalid { autor, reason } = err else {
8997            panic!("expected AutorInvalid, got {err:?}");
8998        };
8999        assert_eq!(autor, "alice\rbob");
9000        assert!(reason.contains("carriage return"), "got: {reason}");
9001    }
9002
9003    #[test]
9004    fn validate_autores_rejects_embedded_tab_entry() {
9005        // Canonical tab-from-aligned-doc footgun.
9006        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
9007        let err = c.validate_autores().unwrap_err();
9008        let ManifestError::AutorInvalid { autor, reason } = err else {
9009            panic!("expected AutorInvalid, got {err:?}");
9010        };
9011        assert_eq!(autor, "Pleme\tContributors");
9012        assert!(reason.contains("tab"), "got: {reason}");
9013    }
9014
9015    #[test]
9016    fn validate_autores_rejects_embedded_control_bytes_entry() {
9017        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
9018        // surface the same control-byte arm.
9019        for entry in [
9020            "alice\x00bob",
9021            "alice\x07bob",
9022            "alice\x1bbob",
9023            "alice\x7fbob",
9024        ] {
9025            let c = caixa_with_autores(vec![entry]);
9026            let err = c.validate_autores().unwrap_err();
9027            let ManifestError::AutorInvalid { autor, reason } = err else {
9028                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
9029            };
9030            assert_eq!(autor, entry);
9031            assert!(
9032                reason.contains("control character"),
9033                "{entry:?} reason: {reason}",
9034            );
9035        }
9036    }
9037
9038    #[test]
9039    fn validate_autores_accepts_unicode_entry() {
9040        // Unicode positive control: realistic maintainer names carry
9041        // Unicode (`François`, `日本語`, `naïve`). The predicate must
9042        // round-trip Unicode losslessly, peer with the
9043        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
9044        // sweep.
9045        let c = caixa_with_autores(vec![
9046            "François Dupont",
9047            "日本語の名前",
9048            "naïve <naive@example.com>",
9049        ]);
9050        c.validate_autores().unwrap();
9051    }
9052
9053    #[test]
9054    fn validate_autores_empty_takes_precedence_over_shape() {
9055        // Per-entry empty-first cascade pin: an entry that is both
9056        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
9057        // "this entry has no value" structural defect dominates the
9058        // broader shape-predicate diagnostic). The empty arm fires
9059        // before the shape predicate is consulted, mirroring the peer
9060        // `validate_repositorio_empty_takes_precedence_over_shape`
9061        // cascade on the universal `Option<String>` siblings — and now
9062        // established on the Vec<String> per-entry surface.
9063        let c = caixa_with_autores(vec![""]);
9064        let err = c.validate_autores().unwrap_err();
9065        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9066    }
9067
9068    #[test]
9069    fn validate_autores_shape_takes_precedence_over_duplicate() {
9070        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
9071        // entry that is malformed surfaces `AutorInvalid` even when a
9072        // later entry would have collided on duplicate. The per-entry
9073        // shape arm fires inside the same loop iteration as the empty
9074        // arm, before the seen-set insert at end-of-iteration —
9075        // structural per-entry defects dominate the cross-entry
9076        // uniqueness diagnostic.
9077        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
9078        let err = c.validate_autores().unwrap_err();
9079        assert!(
9080            matches!(err, ManifestError::AutorInvalid { .. }),
9081            "got {err:?}",
9082        );
9083    }
9084
9085    #[test]
9086    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
9087        // Diagnostic-shape pin on the new shape arm (peer with
9088        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
9089        // the rendered Display surfaces both the offending slot name
9090        // and the offending value verbatim, so a `feira lint` run
9091        // points the author at the exact `:autores` entry to fix.
9092        let c = caixa_with_autores(vec!["alice\nbob"]);
9093        let rendered = c.validate_autores().unwrap_err().to_string();
9094        assert!(
9095            rendered.contains(":autores"),
9096            "diagnostic must name the offending slot: {rendered}",
9097        );
9098        assert!(
9099            rendered.contains("alice\\nbob"),
9100            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9101        );
9102    }
9103
9104    #[test]
9105    fn validate_autores_rejects_at_129_byte_boundary() {
9106        // The 128-byte cap pin — boundary-exceeding case rejected,
9107        // boundary-accepting case passes. Mirrors the peer
9108        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
9109        // substrate-side pin, surfaced at the per-axis caller so the
9110        // cap propagates through validate end-to-end. Constructed as
9111        // a single all-`a` token so only the cap arm fires.
9112        let max_ok = "a".repeat(128);
9113        let c = caixa_with_autores(vec![max_ok.as_str()]);
9114        c.validate_autores().unwrap();
9115        let too_long = "a".repeat(129);
9116        let c = caixa_with_autores(vec![too_long.as_str()]);
9117        let err = c.validate_autores().unwrap_err();
9118        let ManifestError::AutorInvalid { reason, .. } = err else {
9119            panic!("expected AutorInvalid, got {err:?}");
9120        };
9121        assert!(reason.contains("128"), "got: {reason}");
9122        assert!(reason.contains("129"), "got: {reason}");
9123    }
9124
9125    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
9126
9127    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
9128        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9129        c.repositorio = repositorio.map(String::from);
9130        c
9131    }
9132
9133    #[test]
9134    fn validate_repositorio_accepts_none() {
9135        // The omit-the-slot identity: `:repositorio` is optional. The
9136        // gate is a no-op when the author didn't declare a value —
9137        // every caixa without a `:repositorio` line trivially passes,
9138        // and the substrate-side renderers fall back to their
9139        // documented placeholder (`caixa-helm`'s `home: None`,
9140        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
9141        // URL). Mirrors the peer `validate_restart_window_accepts_none`
9142        // posture on the other `Option<String>` Caixa slot.
9143        let c = caixa_with_repositorio(None);
9144        c.validate_repositorio().unwrap();
9145    }
9146
9147    #[test]
9148    fn validate_repositorio_accepts_canonical_forms() {
9149        // Positive control sweep across every documented `:repositorio`
9150        // authoring shape — the same union the shared
9151        // `crate::render::is_git_repo_url` predicate accepts and the
9152        // peer `:deps :fonte :repo` axis already routes through.
9153        // Covers the `github:` shorthand (the canonical pleme-io
9154        // convention used in the `:repositorio` field of every
9155        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
9156        // `examples/`), the `https://…` URL the README quickstart uses,
9157        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
9158        // `file://` URL schemes the shared predicate documents.
9159        for repo in [
9160            "github:pleme-io/hello-rio",
9161            "github:pleme-io/checkout",
9162            "https://github.com/pleme-io/hello-rio",
9163            "ssh://git@github.com/pleme-io/hello-rio.git",
9164            "git://github.com/pleme-io/hello-rio.git",
9165            "git@github.com:pleme-io/hello-rio.git",
9166            "file:///srv/pleme/hello-rio",
9167        ] {
9168            let c = caixa_with_repositorio(Some(repo));
9169            c.validate_repositorio()
9170                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
9171        }
9172    }
9173
9174    #[test]
9175    fn validate_repositorio_rejects_empty_some() {
9176        // Canonical paste-from-blank-doc footgun. The narrower
9177        // [`ManifestError::RepositorioEmpty`] arm fires before the
9178        // shape predicate is consulted, mirroring the empty-first
9179        // cascade every peer per-axis identity gate uses
9180        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9181        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
9182        // the empty `Some("")` silently passed the renderer's
9183        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9184        // on `None`) and landed as `home: ""` in `Chart.yaml` /
9185        // `url: ""` in the FluxCD `GitRepository`.
9186        let c = caixa_with_repositorio(Some(""));
9187        let err = c.validate_repositorio().unwrap_err();
9188        assert!(
9189            matches!(err, ManifestError::RepositorioEmpty),
9190            "got {err:?}",
9191        );
9192    }
9193
9194    #[test]
9195    fn validate_repositorio_rejects_whitespace() {
9196        // Paste-from-doc whitespace footgun. The shared
9197        // `is_git_repo_url` predicate refuses any whitespace byte; a
9198        // trailing space in a `:repositorio` value silently broke
9199        // `git clone '<value> '` at clone time. The diagnostic names
9200        // the offending value verbatim.
9201        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
9202        let err = c.validate_repositorio().unwrap_err();
9203        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
9204            panic!("expected RepositorioInvalid, got {err:?}");
9205        };
9206        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
9207    }
9208
9209    #[test]
9210    fn validate_repositorio_rejects_control_char() {
9211        // Paste-from-multiline-doc CRLF footgun — control characters
9212        // at the URL boundary are a class of subprocess-arg injection
9213        // and break git's URL parser at every porcelain entry point.
9214        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
9215        let err = c.validate_repositorio().unwrap_err();
9216        assert!(
9217            matches!(err, ManifestError::RepositorioInvalid { .. }),
9218            "got {err:?}",
9219        );
9220    }
9221
9222    #[test]
9223    fn validate_repositorio_rejects_leading_dash() {
9224        // Canonical CLI-argument-injection footgun: `git clone <repo>`
9225        // interprets a leading `-` as a CLI flag, so a
9226        // `-upload-pack=…` value escapes the subprocess argument
9227        // boundary. The shared predicate refuses every leading-`-`
9228        // shape at validate time.
9229        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
9230        let err = c.validate_repositorio().unwrap_err();
9231        assert!(
9232            matches!(err, ManifestError::RepositorioInvalid { .. }),
9233            "got {err:?}",
9234        );
9235    }
9236
9237    #[test]
9238    fn validate_repositorio_rejects_missing_colon_separator() {
9239        // The bare `org/repo` ambiguity footgun — `git clone` reads
9240        // a no-`:` form as a relative filesystem path rather than the
9241        // GitHub-shorthand expansion the author probably intended.
9242        // The shared predicate refuses every shape without a `:`
9243        // separator.
9244        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9245        let err = c.validate_repositorio().unwrap_err();
9246        assert!(
9247            matches!(err, ManifestError::RepositorioInvalid { .. }),
9248            "got {err:?}",
9249        );
9250    }
9251
9252    #[test]
9253    fn validate_repositorio_rejects_fragment_anchor() {
9254        // Paste-from-browser-address-bar footgun on the
9255        // `:repositorio` axis — an author copies a GitHub permalink
9256        // to a README section / line-permalink and forgets to trim
9257        // the `#fragment` tail. The shared `is_git_repo_url`
9258        // predicate refuses the byte at the URL-grammar layer
9259        // (libcurl strips the fragment before opening the
9260        // transport, so the byte rides verbatim into the rendered
9261        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
9262        // fields but is silently dropped on the wire — two
9263        // manifest variants whose values differ only in their
9264        // fragment anchor lock to two distinct rendered artifacts
9265        // for the byte-identical clone, defeating the THEORY.md
9266        // §V.2 render-determinism contract on the `:repositorio`
9267        // axis the peer `:fonte :repo` axis already closes).
9268        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
9269        let err = c.validate_repositorio().unwrap_err();
9270        let ManifestError::RepositorioInvalid {
9271            repositorio,
9272            reason,
9273        } = err
9274        else {
9275            panic!("expected RepositorioInvalid, got {err:?}");
9276        };
9277        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
9278        assert!(
9279            reason.contains("must not contain `#`"),
9280            "reason must surface the fragment-`#` arm, got {reason:?}"
9281        );
9282    }
9283
9284    #[test]
9285    fn validate_repositorio_rejects_query_string() {
9286        // Paste-from-browser-address-bar footgun on the
9287        // `:repositorio` axis (peer with the a68f818 fragment-`#`
9288        // arm on the same axis). An author copies a GitHub tab
9289        // deep-link out of the address bar and forgets to trim
9290        // the `?tab=…` query tail. The shared `is_git_repo_url`
9291        // predicate refuses the byte at the URL-grammar layer
9292        // (GitHub / GitLab / Bitbucket silently ignore the
9293        // `?query` tail and serve the same repo regardless, so
9294        // the byte rides verbatim into the rendered `Chart.yaml`
9295        // `home:` and FluxCD `GitRepository` `url:` fields but
9296        // is silently masked at the wire — two manifest variants
9297        // whose values differ only in their query tail lock to
9298        // two distinct rendered artifacts for the byte-identical
9299        // clone, defeating the THEORY.md §V.2 render-determinism
9300        // contract on the `:repositorio` axis the peer `:fonte
9301        // :repo` axis already closes).
9302        let c = caixa_with_repositorio(Some(
9303            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
9304        ));
9305        let err = c.validate_repositorio().unwrap_err();
9306        let ManifestError::RepositorioInvalid {
9307            repositorio,
9308            reason,
9309        } = err
9310        else {
9311            panic!("expected RepositorioInvalid, got {err:?}");
9312        };
9313        assert_eq!(
9314            repositorio,
9315            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
9316        );
9317        assert!(
9318            reason.contains("must not contain `?`"),
9319            "reason must surface the query-`?` arm, got {reason:?}"
9320        );
9321    }
9322
9323    #[test]
9324    fn validate_repositorio_rejects_embedded_backslash() {
9325        // Windows-file-path-confusion footgun on the `:repositorio`
9326        // axis (peer with the prior fragment-`#` / query-`?` arms on
9327        // the same axis, and peer with the new dep-level `:fonte :repo`
9328        // backslash arm on the URL-grammar trajectory). An author
9329        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
9330        // hello-rio` into the `:repositorio` slot, expecting the
9331        // `lareira-<nome>` chart's `home:` field and the FluxCD
9332        // `GitRepository` `url:` field to render the canonical local
9333        // file-URI. The shared `is_git_repo_url` predicate refuses
9334        // the byte at the URL-grammar layer (libcurl silently
9335        // translates `\` → `/` on some platforms and refuses it on
9336        // others, so the byte rides verbatim into the rendered
9337        // artifacts but is silently rewritten or rejected at the wire
9338        // — two manifest variants whose values differ only in
9339        // backslash-vs-forward-slash lock to two distinct rendered
9340        // artifacts for the byte-identical clone, defeating the
9341        // THEORY.md §V.2 render-determinism contract on the
9342        // `:repositorio` axis the peer `:fonte :repo` axis already
9343        // closes).
9344        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
9345        let err = c.validate_repositorio().unwrap_err();
9346        let ManifestError::RepositorioInvalid {
9347            repositorio,
9348            reason,
9349        } = err
9350        else {
9351            panic!("expected RepositorioInvalid, got {err:?}");
9352        };
9353        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
9354        assert!(
9355            reason.contains("must not contain `\\`"),
9356            "reason must surface the backslash-`\\` arm, got {reason:?}"
9357        );
9358    }
9359
9360    #[test]
9361    fn validate_repositorio_rejects_uri_template_placeholder() {
9362        // URI Template (RFC 6570) placeholder footgun on the
9363        // `:repositorio` axis (peer with the prior fragment-`#` /
9364        // query-`?` / backslash-`\` arms on the same axis, and peer
9365        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
9366        // URL-grammar trajectory). An author pastes a quick-start
9367        // README snippet / OpenAPI `servers:` URL / Helm chart
9368        // `home:` template carrying unresolved `{org}` / `{repo}`
9369        // placeholders into the `:repositorio` slot, expecting the
9370        // substrate to resolve the placeholder downstream. The
9371        // shared `is_git_repo_url` predicate refuses the byte at the
9372        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
9373        // `%7B` / `%7D` on the wire, so the byte round-trips
9374        // inconsistently between the rendered `Chart.yaml home:` /
9375        // FluxCD `GitRepository url:` and the resolver's `git clone`
9376        // invocation, defeating the THEORY.md §V.2 render-
9377        // determinism contract on the `:repositorio` axis the peer
9378        // `:fonte :repo` axis already closes; every git porcelain
9379        // entry-point additionally fetches a nonexistent literal-
9380        // `{placeholder}`-named path far from the source caixa.lisp).
9381        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
9382        let err = c.validate_repositorio().unwrap_err();
9383        let ManifestError::RepositorioInvalid {
9384            repositorio,
9385            reason,
9386        } = err
9387        else {
9388            panic!("expected RepositorioInvalid, got {err:?}");
9389        };
9390        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
9391        assert!(
9392            reason.contains("must not contain `{`"),
9393            "reason must surface the open-brace `{{` arm, got {reason:?}"
9394        );
9395        assert!(
9396            reason.contains("URI Template") || reason.contains("RFC 6570"),
9397            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
9398        );
9399    }
9400
9401    #[test]
9402    fn validate_repositorio_empty_takes_precedence_over_shape() {
9403        // Empty-first cascade pin: the empty `Some("")` surfaces the
9404        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
9405        // `RepositorioInvalid`, mirroring the peer
9406        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9407        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
9408        // `is_git_repo_url` predicate also rejects the empty input
9409        // (defensively, with its own `"must not be empty"` reason),
9410        // but the manifest-layer empty arm runs first to surface the
9411        // narrower diagnostic verbatim.
9412        let c = caixa_with_repositorio(Some(""));
9413        let err = c.validate_repositorio().unwrap_err();
9414        assert!(
9415            matches!(err, ManifestError::RepositorioEmpty),
9416            "got {err:?}",
9417        );
9418    }
9419
9420    #[test]
9421    fn validate_repositorio_diagnostic_carries_offending_value() {
9422        // Diagnostic-shape pin (peer with
9423        // `validate_autores_diagnostic_carries_offending_author`): the
9424        // error's Display surfaces the offending value + slot name
9425        // verbatim, so a `feira lint` run can render the diagnostic
9426        // without re-parsing and the author can grep their caixa.lisp
9427        // for the offending `:repositorio` value.
9428        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9429        let rendered = c.validate_repositorio().unwrap_err().to_string();
9430        assert!(
9431            rendered.contains(":repositorio"),
9432            "diagnostic must name the offending slot: {rendered}",
9433        );
9434        assert!(
9435            rendered.contains("pleme-io/hello-rio"),
9436            "diagnostic must quote the offending value: {rendered}",
9437        );
9438    }
9439
9440    // ── validate_descricao — universal-axis Chart.yaml description shape ──
9441
9442    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
9443        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9444        c.descricao = descricao.map(String::from);
9445        c
9446    }
9447
9448    #[test]
9449    fn validate_descricao_accepts_none() {
9450        // The omit-the-slot identity: `:descricao` is optional. The
9451        // gate is a no-op when the author didn't declare a value —
9452        // every caixa without a `:descricao` line trivially passes,
9453        // and the substrate-side renderers fall back to their
9454        // documented `caixa.nome`-derived placeholder. Mirrors the
9455        // peer `validate_repositorio_accepts_none` posture on the
9456        // sibling `Option<String>` Caixa slot.
9457        let c = caixa_with_descricao(None);
9458        c.validate_descricao().unwrap();
9459    }
9460
9461    #[test]
9462    fn validate_descricao_accepts_canonical_summary() {
9463        // Positive control: the canonical pleme-io descricao shape —
9464        // a short free-form prose summary — passes the gate. Covers
9465        // the fixture shapes the `caixa-helm` / `caixa-flux` /
9466        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
9467        // wasip2 caixa Servico."`, `"Checkout flow."`).
9468        for desc in [
9469            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9470            "Checkout flow.",
9471            "AWS provider caixa for tatara-lisp",
9472            "FIXME — describe this caixa",
9473            "x",
9474        ] {
9475            let c = caixa_with_descricao(Some(desc));
9476            c.validate_descricao()
9477                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
9478        }
9479    }
9480
9481    #[test]
9482    fn validate_descricao_rejects_empty_some() {
9483        // Canonical paste-from-blank-doc footgun. Without this gate
9484        // the empty `Some("")` silently passed the renderer's
9485        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9486        // on `None`) and landed as `description: ""` in `Chart.yaml`
9487        // and a blank `README.md` header. Mirrors the peer
9488        // [`ManifestError::RepositorioEmpty`] empty-arm on the
9489        // sibling `Option<String>` Caixa slot.
9490        let c = caixa_with_descricao(Some(""));
9491        let err = c.validate_descricao().unwrap_err();
9492        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9493    }
9494
9495    #[test]
9496    fn validate_descricao_rejects_leading_whitespace() {
9497        // Paste-from-aligned-doc footgun: a leading ASCII space the
9498        // bare empty-arm gate accepted, the shape predicate now
9499        // refuses. The diagnostic carries the offending value
9500        // verbatim (with the leading space preserved) so the author
9501        // can grep their caixa.lisp for the exact `:descricao` line
9502        // and fix the round-trip-inconsistent leading whitespace.
9503        // Mirrors the peer
9504        // `validate_licenca_rejects_leading_whitespace` arm on the
9505        // sibling `:licenca` axis.
9506        let c = caixa_with_descricao(Some(" Checkout flow."));
9507        let err = c.validate_descricao().unwrap_err();
9508        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9509            panic!("expected DescricaoInvalid, got {err:?}");
9510        };
9511        assert_eq!(descricao, " Checkout flow.");
9512        assert!(reason.contains("whitespace"), "got: {reason:?}");
9513    }
9514
9515    #[test]
9516    fn validate_descricao_rejects_trailing_whitespace() {
9517        // Paste-from-doc footgun: a trailing ASCII space the bare
9518        // empty-arm gate accepted, the shape predicate now refuses.
9519        let c = caixa_with_descricao(Some("Checkout flow. "));
9520        let err = c.validate_descricao().unwrap_err();
9521        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9522            panic!("expected DescricaoInvalid, got {err:?}");
9523        };
9524        assert_eq!(descricao, "Checkout flow. ");
9525        assert!(reason.contains("whitespace"), "got: {reason:?}");
9526    }
9527
9528    #[test]
9529    fn validate_descricao_rejects_embedded_newline() {
9530        // Paste-from-multiline-doc footgun: an embedded LF the bare
9531        // empty-arm gate accepted, the shape predicate now refuses.
9532        // Without this gate the embedded newline silently landed in
9533        // the rendered Chart.yaml as a multi-line YAML block scalar,
9534        // and every chart-aware UI (`helm list`, `helm search`,
9535        // Artifact Hub) renders the description in a single-line
9536        // column so the embedded newline is silently dropped at
9537        // every downstream consumer.
9538        let c = caixa_with_descricao(Some("Checkout\nflow."));
9539        let err = c.validate_descricao().unwrap_err();
9540        assert!(
9541            matches!(err, ManifestError::DescricaoInvalid { .. }),
9542            "got {err:?}",
9543        );
9544        assert!(err.to_string().contains("newline"), "got {err}");
9545    }
9546
9547    #[test]
9548    fn validate_descricao_rejects_embedded_carriage_return() {
9549        // Paste-from-Windows-CRLF-doc footgun.
9550        let c = caixa_with_descricao(Some("Checkout\rflow."));
9551        let err = c.validate_descricao().unwrap_err();
9552        assert!(
9553            matches!(err, ManifestError::DescricaoInvalid { .. }),
9554            "got {err:?}",
9555        );
9556        assert!(err.to_string().contains("carriage return"), "got {err}");
9557    }
9558
9559    #[test]
9560    fn validate_descricao_rejects_embedded_tab() {
9561        // Tab-from-aligned-doc footgun.
9562        let c = caixa_with_descricao(Some("Checkout\tflow."));
9563        let err = c.validate_descricao().unwrap_err();
9564        assert!(
9565            matches!(err, ManifestError::DescricaoInvalid { .. }),
9566            "got {err:?}",
9567        );
9568        assert!(err.to_string().contains("tab"), "got {err}");
9569    }
9570
9571    #[test]
9572    fn validate_descricao_rejects_embedded_control_bytes() {
9573        // Paste-from-binary-blob footgun: every other control byte
9574        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
9575        // the peer SPDX-expression control-byte arm.
9576        for s in [
9577            "Checkout\x00flow.",
9578            "Checkout\x07flow.",
9579            "Checkout\x1bflow.",
9580            "Checkout\x7fflow.",
9581        ] {
9582            let c = caixa_with_descricao(Some(s));
9583            let err = c.validate_descricao().unwrap_err();
9584            assert!(
9585                matches!(err, ManifestError::DescricaoInvalid { .. }),
9586                "{s:?} got {err:?}",
9587            );
9588            assert!(
9589                err.to_string().contains("control character"),
9590                "{s:?} got {err}",
9591            );
9592        }
9593    }
9594
9595    #[test]
9596    fn validate_descricao_accepts_unicode_prose() {
9597        // Positive control: Unicode prose is accepted — the
9598        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
9599        // and `Caixa::template`'s `"FIXME — describe this caixa"`
9600        // scaffold every `feira init` emits must continue to pass.
9601        for s in [
9602            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9603            "FIXME — describe this caixa",
9604            "Caixa pour le projet tâche",
9605            "日本語の説明",
9606        ] {
9607            let c = caixa_with_descricao(Some(s));
9608            c.validate_descricao()
9609                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
9610        }
9611    }
9612
9613    #[test]
9614    fn validate_descricao_empty_takes_precedence_over_shape() {
9615        // Cascade pin: a `Some("")` surfaces the narrower
9616        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
9617        // shape-predicate arm. Mirrors the peer
9618        // `validate_licenca_empty_takes_precedence_over_shape` pin
9619        // on the sibling `:licenca` axis.
9620        let c = caixa_with_descricao(Some(""));
9621        let err = c.validate_descricao().unwrap_err();
9622        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9623    }
9624
9625    #[test]
9626    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
9627        // Diagnostic-shape pin: the error's Display surfaces both
9628        // the `:descricao` slot name and the offending value
9629        // verbatim, so a `feira lint` run can render the diagnostic
9630        // without re-parsing and the author can grep their caixa.lisp
9631        // for the offending `:descricao` line. Mirrors the peer
9632        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
9633        // pin (ee2e888) on the sibling `:licenca` axis.
9634        // The `{descricao:?}` Debug format escapes embedded control
9635        // bytes; the quoted offending value surfaces as
9636        // `"Checkout\nflow."` (literal backslash-n) in the rendered
9637        // diagnostic. The author can grep their caixa.lisp for the
9638        // literal `Checkout` summary prefix.
9639        let c = caixa_with_descricao(Some("Checkout\nflow."));
9640        let rendered = c.validate_descricao().unwrap_err().to_string();
9641        assert!(
9642            rendered.contains(":descricao"),
9643            "diagnostic must name the offending slot: {rendered}",
9644        );
9645        assert!(
9646            rendered.contains("Checkout\\nflow."),
9647            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9648        );
9649    }
9650
9651    #[test]
9652    fn validate_descricao_template_passes() {
9653        // Round-trip pin: the bare `Caixa::template` shape carries
9654        // `:descricao "FIXME — describe this caixa"` (a non-empty
9655        // sentinel), so the template-derived Caixa passes the gate by
9656        // construction. A future template-shape change that omits or
9657        // empties `:descricao` would surface here as a regression.
9658        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9659        c.validate_descricao().unwrap();
9660    }
9661
9662    #[test]
9663    fn validate_descricao_diagnostic_names_offending_slot() {
9664        // Diagnostic-shape pin (peer with
9665        // `validate_repositorio_diagnostic_carries_offending_value`):
9666        // the error's Display surfaces the `:descricao` slot name
9667        // verbatim, so a `feira lint` run can render the diagnostic
9668        // without re-parsing and the author can grep their caixa.lisp
9669        // for the offending `:descricao` line.
9670        let c = caixa_with_descricao(Some(""));
9671        let rendered = c.validate_descricao().unwrap_err().to_string();
9672        assert!(
9673            rendered.contains(":descricao"),
9674            "diagnostic must name the offending slot: {rendered}",
9675        );
9676    }
9677
9678    // ── validate_licenca — universal-axis chart README license shape ──
9679
9680    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
9681        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9682        c.licenca = licenca.map(String::from);
9683        c
9684    }
9685
9686    #[test]
9687    fn validate_licenca_accepts_none() {
9688        // The omit-the-slot identity: `:licenca` is optional. The
9689        // gate is a no-op when the author didn't declare a value —
9690        // every caixa without a `:licenca` line trivially passes,
9691        // and the substrate-side `caixa-helm` renderer falls back to
9692        // the documented `"MIT"` placeholder. Mirrors the peer
9693        // `validate_descricao_accepts_none` posture on the sibling
9694        // `Option<String>` Caixa slot.
9695        let c = caixa_with_licenca(None);
9696        c.validate_licenca().unwrap();
9697    }
9698
9699    #[test]
9700    fn validate_licenca_accepts_canonical_expressions() {
9701        // Positive control: every canonical SPDX expression shape
9702        // pleme-io carries in its existing fixtures + the canonical
9703        // SPDX dual-license / with-exception / `+`-suffix / grouped /
9704        // user-defined-reference shapes all pass the gate. Covers
9705        // the single-license, `OR`-compound, `AND`-compound,
9706        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
9707        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
9708        // production the SPDX 2.1 expression grammar admits that
9709        // sits within the alphabet floor the
9710        // `is_spdx_expression_shape` predicate enforces.
9711        for lic in [
9712            "MIT",
9713            "Apache-2.0",
9714            "Apache-2.0 OR MIT",
9715            "Apache-2.0 AND MIT",
9716            "BSD-3-Clause",
9717            "MPL-2.0",
9718            "GPL-3.0-or-later",
9719            "GPL-2.0+",
9720            "Apache-2.0 WITH LLVM-exception",
9721            "(MIT OR Apache-2.0) AND BSD-3-Clause",
9722            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
9723            "LicenseRef-MyLicense",
9724            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
9725            "x",
9726        ] {
9727            let c = caixa_with_licenca(Some(lic));
9728            c.validate_licenca()
9729                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
9730        }
9731    }
9732
9733    #[test]
9734    fn validate_licenca_rejects_trailing_whitespace() {
9735        // Paste-from-doc whitespace footgun. A trailing space in the
9736        // `:licenca` value would silently break a downstream SPDX
9737        // parser that splits on exact `AND` / `OR` / `WITH` keyword
9738        // boundaries. The shape predicate refuses every trailing
9739        // whitespace byte by construction. Peer with
9740        // `validate_repositorio_rejects_whitespace` and
9741        // `validate_edicao_rejects_trailing_whitespace`.
9742        let c = caixa_with_licenca(Some("MIT "));
9743        let err = c.validate_licenca().unwrap_err();
9744        let ManifestError::LicencaInvalid { licenca, .. } = err else {
9745            panic!("expected LicencaInvalid, got {err:?}");
9746        };
9747        assert_eq!(licenca, "MIT ");
9748    }
9749
9750    #[test]
9751    fn validate_licenca_rejects_leading_whitespace() {
9752        // Symmetric paste-from-doc whitespace footgun on the leading
9753        // boundary — the gate refuses every shape that starts with a
9754        // space byte by construction. Peer with
9755        // `validate_edicao_rejects_leading_whitespace`.
9756        let c = caixa_with_licenca(Some(" MIT"));
9757        let err = c.validate_licenca().unwrap_err();
9758        assert!(
9759            matches!(err, ManifestError::LicencaInvalid { .. }),
9760            "got {err:?}",
9761        );
9762    }
9763
9764    #[test]
9765    fn validate_licenca_rejects_control_char() {
9766        // Paste-from-multiline-doc CRLF footgun — control characters
9767        // at the value boundary land as a malformed line in the
9768        // rendered chart `README.md` `## License` section. Peer with
9769        // `validate_repositorio_rejects_control_char` and
9770        // `validate_edicao_rejects_control_char`.
9771        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
9772            let c = caixa_with_licenca(Some(lic));
9773            let err = c.validate_licenca().unwrap_err();
9774            assert!(
9775                matches!(err, ManifestError::LicencaInvalid { .. }),
9776                "expected LicencaInvalid on {lic:?}, got {err:?}",
9777            );
9778        }
9779    }
9780
9781    #[test]
9782    fn validate_licenca_rejects_tab() {
9783        // Tab-from-aligned-doc footgun — SPDX expressions use a
9784        // single ASCII space between tokens; a tab breaks every
9785        // downstream SPDX parser that splits on exact `" "`
9786        // boundaries.
9787        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
9788        let err = c.validate_licenca().unwrap_err();
9789        assert!(
9790            matches!(err, ManifestError::LicencaInvalid { .. }),
9791            "got {err:?}",
9792        );
9793    }
9794
9795    #[test]
9796    fn validate_licenca_rejects_non_ascii() {
9797        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
9798        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
9799        // ".")` production. The shape predicate refuses every
9800        // non-ASCII byte by construction; peer with
9801        // `validate_edicao_rejects_non_ascii_lookalike`.
9802        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
9803            let c = caixa_with_licenca(Some(lic));
9804            let err = c.validate_licenca().unwrap_err();
9805            assert!(
9806                matches!(err, ManifestError::LicencaInvalid { .. }),
9807                "expected LicencaInvalid on {lic:?}, got {err:?}",
9808            );
9809        }
9810    }
9811
9812    #[test]
9813    fn validate_licenca_rejects_underscore() {
9814        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
9815        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
9816        // snake-case identifier conventions that don't apply to the
9817        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
9818        // "-" / "."`). The shape predicate refuses every underscore
9819        // byte by construction.
9820        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
9821            let c = caixa_with_licenca(Some(lic));
9822            let err = c.validate_licenca().unwrap_err();
9823            assert!(
9824                matches!(err, ManifestError::LicencaInvalid { .. }),
9825                "expected LicencaInvalid on {lic:?}, got {err:?}",
9826            );
9827        }
9828    }
9829
9830    #[test]
9831    fn validate_licenca_rejects_comma_separator() {
9832        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
9833        // SPDX expressions compose multiple licenses via `AND` / `OR`
9834        // keywords, not the comma separator. The shape predicate
9835        // refuses every comma byte by construction.
9836        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
9837            let c = caixa_with_licenca(Some(lic));
9838            let err = c.validate_licenca().unwrap_err();
9839            assert!(
9840                matches!(err, ManifestError::LicencaInvalid { .. }),
9841                "expected LicencaInvalid on {lic:?}, got {err:?}",
9842            );
9843        }
9844    }
9845
9846    #[test]
9847    fn validate_licenca_rejects_slash_dual_license() {
9848        // Slash-dual-license colloquial idiom footgun — the
9849        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
9850        // `package.license` field but non-SPDX; the SPDX equivalent
9851        // is `MIT OR Apache-2.0`. The shape predicate refuses every
9852        // forward-slash byte by construction.
9853        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
9854            let c = caixa_with_licenca(Some(lic));
9855            let err = c.validate_licenca().unwrap_err();
9856            assert!(
9857                matches!(err, ManifestError::LicencaInvalid { .. }),
9858                "expected LicencaInvalid on {lic:?}, got {err:?}",
9859            );
9860        }
9861    }
9862
9863    #[test]
9864    fn validate_licenca_rejects_semicolon_separator() {
9865        // Semicolon-list-separator confusion footgun — adjacent to
9866        // the comma-separator idiom, every list-separator-belongs-
9867        // to-list-grammar confusion lands here.
9868        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
9869        let err = c.validate_licenca().unwrap_err();
9870        assert!(
9871            matches!(err, ManifestError::LicencaInvalid { .. }),
9872            "got {err:?}",
9873        );
9874    }
9875
9876    #[test]
9877    fn validate_licenca_empty_takes_precedence_over_shape() {
9878        // Empty-first cascade pin: the empty `Some("")` surfaces the
9879        // narrower `LicencaEmpty` not the shape-predicate-wrapped
9880        // `LicencaInvalid`, mirroring the peer
9881        // `validate_edicao_empty_takes_precedence_over_shape` and
9882        // `validate_repositorio_empty_takes_precedence_over_shape`
9883        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
9884        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
9885        // The shape predicate also refuses the empty input
9886        // (defensively — `"must not be empty"`), but the manifest-
9887        // layer empty arm runs first to surface the narrower
9888        // diagnostic verbatim.
9889        let c = caixa_with_licenca(Some(""));
9890        let err = c.validate_licenca().unwrap_err();
9891        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
9892    }
9893
9894    #[test]
9895    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
9896        // Diagnostic-shape pin on the shape-predicate arm (peer with
9897        // `validate_edicao_invalid_diagnostic_carries_offending_value`
9898        // and `validate_repositorio_diagnostic_carries_offending_value`):
9899        // the error's Display surfaces the offending value + slot
9900        // name verbatim, so a `feira lint` run can render the
9901        // diagnostic without re-parsing and the author can grep
9902        // their caixa.lisp for the offending `:licenca` value.
9903        let c = caixa_with_licenca(Some("Apache_2.0"));
9904        let rendered = c.validate_licenca().unwrap_err().to_string();
9905        assert!(
9906            rendered.contains(":licenca"),
9907            "diagnostic must name the offending slot: {rendered}",
9908        );
9909        assert!(
9910            rendered.contains("Apache_2.0"),
9911            "diagnostic must quote the offending value: {rendered}",
9912        );
9913    }
9914
9915    #[test]
9916    fn validate_licenca_rejects_empty_some() {
9917        // Canonical paste-from-blank-doc footgun. Without this gate
9918        // the empty `Some("")` silently passed the renderer's
9919        // `Option::unwrap_or_else(|| "MIT".into())` (which only
9920        // fires on `None`) and landed as a bare trailing period in
9921        // the rendered chart `README.md` `## License` section.
9922        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
9923        // arm on the sibling `Option<String>` Caixa slot.
9924        let c = caixa_with_licenca(Some(""));
9925        let err = c.validate_licenca().unwrap_err();
9926        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
9927    }
9928
9929    #[test]
9930    fn validate_licenca_template_passes() {
9931        // Round-trip pin: the bare `Caixa::template` shape (whether
9932        // it carries `:licenca` or omits it) passes the gate by
9933        // construction. A future template-shape change that
9934        // introduced `(:licenca "")` would surface here as a
9935        // regression. Mirrors the peer
9936        // `validate_descricao_template_passes` pin.
9937        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9938        c.validate_licenca().unwrap();
9939    }
9940
9941    #[test]
9942    fn validate_licenca_diagnostic_names_offending_slot() {
9943        // Diagnostic-shape pin (peer with
9944        // `validate_descricao_diagnostic_names_offending_slot`):
9945        // the error's Display surfaces the `:licenca` slot name
9946        // verbatim, so a `feira lint` run can render the diagnostic
9947        // without re-parsing and the author can grep their caixa.lisp
9948        // for the offending `:licenca` line.
9949        let c = caixa_with_licenca(Some(""));
9950        let rendered = c.validate_licenca().unwrap_err().to_string();
9951        assert!(
9952            rendered.contains(":licenca"),
9953            "diagnostic must name the offending slot: {rendered}",
9954        );
9955    }
9956
9957    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
9958
9959    #[test]
9960    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
9961        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
9962        // pin: [`Caixa::licenca`] must return the `:licenca` typed
9963        // byte-string verbatim as an `Option<&str>`, byte-equal to the
9964        // raw `self.licenca.as_deref()` access across every
9965        // representative value in the accept-set — `None` (the "omit
9966        // the slot to defer to the caixa-helm renderer's `MIT`
9967        // fallback" arm every existing fixture without a `:licenca`
9968        // line carries), `Some("")` (a past-the-guard sentinel that
9969        // pins the accessor doesn't perform a silent
9970        // `Some("") → None` collapse on the empty arm — validate
9971        // rejects `Some("")` through `LicencaEmpty` but the accessor
9972        // must ship the raw slot verbatim so a validate-time gate
9973        // regression surfaces at the caixa-helm emit boundary rather
9974        // than being silently absorbed into the fallback), `Some("MIT")`
9975        // (the canonical single-license shape every `feira init`
9976        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
9977        // canonical `OR`-compound shape the peer
9978        // `validate_licenca_accepts_canonical_expressions` positive
9979        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
9980        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
9981        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
9982        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
9983        // guard sentinels — validate rejects each through
9984        // `LicencaInvalid` but the accessor must ship the raw slot
9985        // verbatim).
9986        //
9987        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
9988        // accessor pin on the substrate primitive — opens the "outer
9989        // [`Caixa`] `Option<&str>` scalar" projection pattern the
9990        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
9991        // future lifts fold on. Sibling in shape to the peer per-`:placement`
9992        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
9993        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
9994        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
9995        // axes, extended onto the outer top-level [`Caixa`] universal-
9996        // axis surface. Pins against a future silent detour that
9997        // returned an owned `Option<String>` (which would type-check
9998        // but silently allocate on every accessor call, breaking the
9999        // zero-cost projection every peer sibling accessor carries), a
10000        // `Some("") → None` collapse (which would silently absorb the
10001        // `LicencaEmpty` refusal case at the accessor boundary and the
10002        // caixa-helm emit path would silently fall back to `"MIT"` on
10003        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
10004        // `None → Some("MIT")` collapse (which would silently reify
10005        // the caixa-helm renderer's `"MIT"` fallback at the accessor
10006        // boundary and every downstream consumer keying off the
10007        // `Option::is_none()` discriminator would lose the "author
10008        // omitted the slot" signal).
10009        for licenca in [
10010            None,
10011            Some(""),
10012            Some("MIT"),
10013            Some("Apache-2.0 OR MIT"),
10014            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
10015            Some("MIT "),
10016            Some(" MIT"),
10017            Some("MIT\n"),
10018            Some("Apache_2.0"),
10019            Some("MIT,Apache-2.0"),
10020        ] {
10021            let c = caixa_with_licenca(licenca);
10022            assert_eq!(
10023                c.licenca(),
10024                licenca,
10025                "Caixa::licenca must return :licenca verbatim (got {:?}, \
10026                 expected {licenca:?})",
10027                c.licenca(),
10028            );
10029            assert_eq!(
10030                c.licenca(),
10031                c.licenca.as_deref(),
10032                "Caixa::licenca must byte-equal the raw \
10033                 `self.licenca.as_deref()` field access across every \
10034                 value in the Option<&str> accept-set",
10035            );
10036        }
10037    }
10038
10039    #[test]
10040    fn validate_licenca_empty_arm_routes_through_accessor() {
10041        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
10042        // must key off [`Caixa::licenca`], not the raw
10043        // `self.licenca.as_deref()` field access. Structurally: a
10044        // `Caixa { licenca: Some(""), .. }` must surface the
10045        // `LicencaEmpty` refusal exactly, and a
10046        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
10047        // single-license form) must pass validate. The pair jointly
10048        // pins the accessor + validate-gate composition: any future
10049        // silent detour that had the accessor return `None` on the
10050        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10051        // silently absorb the `LicencaEmpty` refusal at the accessor
10052        // boundary and the validate gate would accept a struct-literal
10053        // `Caixa { licenca: Some(""), .. }` — the composition pin
10054        // catches that at caixa-core build time.
10055        //
10056        // Peer of the per-`:politicas :circuit-breaker`
10057        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
10058        // accessor-composition pin
10059        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
10060        // on the sibling per-M3-mesh-slot required-`u32` axis — same
10061        // "the validate / shape-gate predicate must route through the
10062        // substrate-primitive typed dispatch" discipline extended onto
10063        // the outer top-level [`Caixa`] universal-axis
10064        // `Option<&str>`-composition surface.
10065        let c = caixa_with_licenca(Some(""));
10066        assert!(
10067            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
10068            "validate_licenca must reject licenca == Some(\"\") with \
10069             LicencaEmpty — the accessor and the validate gate must \
10070             route through the same substrate-primitive typed dispatch \
10071             on the :licenca empty arm",
10072        );
10073        let c = caixa_with_licenca(Some("MIT"));
10074        assert!(
10075            c.validate_licenca().is_ok(),
10076            "validate_licenca must accept licenca == Some(\"MIT\") \
10077             (the canonical single-license SPDX shape)",
10078        );
10079    }
10080
10081    #[test]
10082    fn licenca_projects_option_str_by_borrow() {
10083        // The by-borrow pin: [`Caixa::licenca`] returns
10084        // `Option<&str>` by borrow — the `&str` borrows the underlying
10085        // `String` storage of the `Option<String>` slot and the
10086        // accessor must not allocate a fresh `String` on every call.
10087        // Peer of the per-`:placement`
10088        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10089        // borrow pin on the peer per-M3-mesh-slot
10090        // `Option<&str>`-return axis, extended onto the outer top-
10091        // level [`Caixa`] universal-axis `Option<&str>` shape — the
10092        // accessor's returned `&str` must borrow from `&self` (the
10093        // returned reference's lifetime is tied to `&self`), and
10094        // calling the accessor twice on the same [`Caixa`] must yield
10095        // the same `Option<&str>` verbatim (idempotent, no side
10096        // effects on `&self`).
10097        //
10098        // Pins against a future silent detour that returned an owned
10099        // `Option<String>` (which would type-check but silently
10100        // allocate on every call, breaking the zero-cost projection
10101        // every peer sibling accessor carries), or a one-arm-only
10102        // accessor that returned a saturating value on some sentinel
10103        // input (breaking the pass-through invariant the sibling
10104        // required-scalar accessors carry).
10105        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
10106            let c = caixa_with_licenca(licenca);
10107            let first = c.licenca();
10108            let second = c.licenca();
10109            assert_eq!(
10110                first, second,
10111                "Caixa::licenca must be idempotent — two successive \
10112                 calls on the same &self must return the same \
10113                 Option<&str>",
10114            );
10115            assert_eq!(
10116                first, licenca,
10117                "Caixa::licenca must return :licenca verbatim by \
10118                 borrow — got {first:?}, expected {licenca:?}",
10119            );
10120        }
10121    }
10122
10123    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
10124
10125    #[test]
10126    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
10127        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
10128        // pin: [`Caixa::repositorio`] must return the `:repositorio`
10129        // typed byte-string verbatim as an `Option<&str>`, byte-equal
10130        // to the raw `self.repositorio.as_deref()` access across every
10131        // representative value in the accept-set — `None` (the "omit
10132        // the slot to defer to the per-renderer placeholder" arm every
10133        // existing fixture without a `:repositorio` line carries),
10134        // `Some("")` (a past-the-guard sentinel that pins the accessor
10135        // doesn't perform a silent `Some("") → None` collapse on the
10136        // empty arm — validate rejects `Some("")` through
10137        // `RepositorioEmpty` but the accessor must ship the raw slot
10138        // verbatim so a validate-time gate regression surfaces at the
10139        // caixa-helm / caixa-flux emit boundary rather than being
10140        // silently absorbed into the per-renderer fallback),
10141        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
10142        // shorthand every existing manifest fixture across
10143        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
10144        // `Some("https://github.com/pleme-io/checkout")` (the canonical
10145        // `https://` URL the README quickstart uses),
10146        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
10147        // `Some("git://github.com/pleme-io/checkout.git")` /
10148        // `Some("git@github.com:pleme-io/checkout.git")` /
10149        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
10150        // github scheme the shared `is_git_repo_url` predicate
10151        // documents), and five past-the-guard sentinels for the
10152        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
10153        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
10154        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
10155        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
10156        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
10157        // sentinels pin the accessor doesn't silently absorb the
10158        // refusal cases into a fallback).
10159        //
10160        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
10161        // accessor pin on the substrate primitive — sibling of the peer
10162        // [`Caixa::licenca`] (6d5bc28) pin
10163        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
10164        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
10165        // projection pin pattern this pin folds on. Sibling in shape to
10166        // the peer per-`:placement`
10167        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10168        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10169        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10170        // axes, extended onto the outer top-level [`Caixa`] universal-
10171        // axis surface. Pins against a future silent detour that
10172        // returned an owned `Option<String>` (which would type-check
10173        // but silently allocate on every accessor call, breaking the
10174        // zero-cost projection every peer sibling accessor carries), a
10175        // `Some("") → None` collapse (which would silently absorb the
10176        // `RepositorioEmpty` refusal case at the accessor boundary and
10177        // the caixa-helm `Chart.yaml` `home:` fold would silently
10178        // render a `home: null` / omitted field on a struct-literal
10179        // `Caixa { repositorio: Some(""), .. }`), or a
10180        // `None → Some(<default>)` collapse (which would silently reify
10181        // the per-renderer fallback at the accessor boundary and every
10182        // downstream consumer keying off the `Option::is_none()`
10183        // discriminator would lose the "author omitted the slot"
10184        // signal).
10185        for repositorio in [
10186            None,
10187            Some(""),
10188            Some("github:pleme-io/hello-rio"),
10189            Some("https://github.com/pleme-io/checkout"),
10190            Some("ssh://git@github.com/pleme-io/checkout.git"),
10191            Some("git://github.com/pleme-io/checkout.git"),
10192            Some("git@github.com:pleme-io/checkout.git"),
10193            Some("file:///opt/mirrors/pleme-io/checkout"),
10194            Some("pleme-io/checkout"),
10195            Some("-upload-pack=evil"),
10196            Some("github:pleme-io/checkout?ref=main"),
10197            Some("github:pleme-io/checkout#main"),
10198            Some("github:pleme-io/{tpl}"),
10199        ] {
10200            let c = caixa_with_repositorio(repositorio);
10201            assert_eq!(
10202                c.repositorio(),
10203                repositorio,
10204                "Caixa::repositorio must return :repositorio verbatim \
10205                 (got {:?}, expected {repositorio:?})",
10206                c.repositorio(),
10207            );
10208            assert_eq!(
10209                c.repositorio(),
10210                c.repositorio.as_deref(),
10211                "Caixa::repositorio must byte-equal the raw \
10212                 `self.repositorio.as_deref()` field access across every \
10213                 value in the Option<&str> accept-set",
10214            );
10215        }
10216    }
10217
10218    #[test]
10219    fn validate_repositorio_empty_arm_routes_through_accessor() {
10220        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
10221        // gate must key off [`Caixa::repositorio`], not the raw
10222        // `self.repositorio.as_deref()` field access. Structurally: a
10223        // `Caixa { repositorio: Some(""), .. }` must surface the
10224        // `RepositorioEmpty` refusal exactly, and a
10225        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
10226        // (the canonical `github:` shorthand form) must pass validate.
10227        // The pair jointly pins the accessor + validate-gate
10228        // composition: any future silent detour that had the accessor
10229        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
10230        // collapse) would silently absorb the `RepositorioEmpty` refusal
10231        // at the accessor boundary and the validate gate would accept a
10232        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
10233        // composition pin catches that at caixa-core build time.
10234        //
10235        // Peer of the [`Caixa::licenca`] (6d5bc28)
10236        // `validate_licenca_empty_arm_routes_through_accessor`
10237        // composition pin on the sibling outer top-level [`Caixa`]
10238        // `Option<&str>` universal-axis surface — same "the validate /
10239        // shape-gate predicate must route through the substrate-
10240        // primitive typed dispatch" discipline extended onto the second
10241        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10242        // composition surface.
10243        let c = caixa_with_repositorio(Some(""));
10244        assert!(
10245            matches!(
10246                c.validate_repositorio(),
10247                Err(ManifestError::RepositorioEmpty),
10248            ),
10249            "validate_repositorio must reject repositorio == Some(\"\") \
10250             with RepositorioEmpty — the accessor and the validate gate \
10251             must route through the same substrate-primitive typed \
10252             dispatch on the :repositorio empty arm",
10253        );
10254        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
10255        assert!(
10256            c.validate_repositorio().is_ok(),
10257            "validate_repositorio must accept repositorio == \
10258             Some(\"github:pleme-io/hello-rio\") (the canonical \
10259             `github:` shorthand git-repo-URL shape)",
10260        );
10261    }
10262
10263    #[test]
10264    fn repositorio_projects_option_str_by_borrow() {
10265        // The by-borrow pin: [`Caixa::repositorio`] returns
10266        // `Option<&str>` by borrow — the `&str` borrows the underlying
10267        // `String` storage of the `Option<String>` slot and the
10268        // accessor must not allocate a fresh `String` on every call.
10269        // Peer of the per-`:placement`
10270        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
10271        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
10272        // `Option<&str>`-return axes, extended onto the second outer
10273        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
10274        // the accessor's returned `&str` must borrow from `&self` (the
10275        // returned reference's lifetime is tied to `&self`), and
10276        // calling the accessor twice on the same [`Caixa`] must yield
10277        // the same `Option<&str>` verbatim (idempotent, no side effects
10278        // on `&self`).
10279        //
10280        // Pins against a future silent detour that returned an owned
10281        // `Option<String>` (which would type-check but silently
10282        // allocate on every call, breaking the zero-cost projection
10283        // every peer sibling accessor carries), or a one-arm-only
10284        // accessor that returned a saturating value on some sentinel
10285        // input (breaking the pass-through invariant the sibling
10286        // required-scalar accessors carry).
10287        for repositorio in [
10288            None,
10289            Some(""),
10290            Some("github:pleme-io/hello-rio"),
10291            Some("https://github.com/pleme-io/checkout"),
10292        ] {
10293            let c = caixa_with_repositorio(repositorio);
10294            let first = c.repositorio();
10295            let second = c.repositorio();
10296            assert_eq!(
10297                first, second,
10298                "Caixa::repositorio must be idempotent — two successive \
10299                 calls on the same &self must return the same \
10300                 Option<&str>",
10301            );
10302            assert_eq!(
10303                first, repositorio,
10304                "Caixa::repositorio must return :repositorio verbatim by \
10305                 borrow — got {first:?}, expected {repositorio:?}",
10306            );
10307        }
10308    }
10309
10310    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
10311
10312    #[test]
10313    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
10314        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
10315        // pin: [`Caixa::descricao`] must return the `:descricao` typed
10316        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10317        // raw `self.descricao.as_deref()` access across every
10318        // representative value in the accept-set — `None` (the "omit
10319        // the slot to defer to the per-renderer `caixa.nome`-derived
10320        // fallback" arm every existing fixture without a `:descricao`
10321        // line carries), `Some("")` (a past-the-guard sentinel that
10322        // pins the accessor doesn't perform a silent `Some("") → None`
10323        // collapse on the empty arm — validate rejects `Some("")`
10324        // through `DescricaoEmpty` but the accessor must ship the raw
10325        // slot verbatim so a validate-time gate regression surfaces at
10326        // the caixa-helm / caixa-feira emit boundary rather than being
10327        // silently absorbed into the per-renderer `caixa.nome`-derived
10328        // fallback), `Some("Checkout flow.")` (the canonical one-line
10329        // prose descriptor the peer
10330        // `validate_descricao_accepts_canonical_value` positive sweep
10331        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
10332        // Servico.")` (the multi-byte Unicode continuation-byte shape
10333        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
10334        // multi-glyph Unicode shape the peer
10335        // `is_chart_description_shape` predicate accepts), and five
10336        // past-the-guard sentinels for the `DescricaoInvalid` refusal
10337        // cases (`Some(" Checkout flow.")` leading-whitespace,
10338        // `Some("Checkout flow. ")` trailing-whitespace,
10339        // `Some("Checkout\nflow.")` embedded-LF,
10340        // `Some("Checkout\tflow.")` embedded-TAB, and
10341        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
10342        // the accessor doesn't silently absorb the refusal cases into
10343        // a fallback).
10344        //
10345        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
10346        // accessor pin on the substrate primitive — sibling of the peer
10347        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
10348        // (cc7332d) pins that opened the "outer [`Caixa`]
10349        // `Option<&str>` scalar" projection pin pattern this pin folds
10350        // on. Sibling in shape to the peer per-`:placement`
10351        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10352        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10353        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10354        // axes, extended onto the outer top-level [`Caixa`] universal-
10355        // axis surface. Pins against a future silent detour that
10356        // returned an owned `Option<String>` (which would type-check
10357        // but silently allocate on every accessor call, breaking the
10358        // zero-cost projection every peer sibling accessor carries), a
10359        // `Some("") → None` collapse (which would silently absorb the
10360        // `DescricaoEmpty` refusal case at the accessor boundary and
10361        // the caixa-helm `Chart.yaml` `description:` fold would
10362        // silently render a `caixa.nome`-derived fallback on a
10363        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
10364        // `None → Some(<default>)` collapse (which would silently
10365        // reify the per-renderer `caixa.nome`-derived fallback at the
10366        // accessor boundary and every downstream consumer keying off
10367        // the `Option::is_none()` discriminator would lose the "author
10368        // omitted the slot" signal).
10369        for descricao in [
10370            None,
10371            Some(""),
10372            Some("Checkout flow."),
10373            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
10374            Some("→ — · ✓"),
10375            Some(" Checkout flow."),
10376            Some("Checkout flow. "),
10377            Some("Checkout\nflow."),
10378            Some("Checkout\tflow."),
10379            Some("Checkout\x00flow."),
10380        ] {
10381            let c = caixa_with_descricao(descricao);
10382            assert_eq!(
10383                c.descricao(),
10384                descricao,
10385                "Caixa::descricao must return :descricao verbatim (got \
10386                 {:?}, expected {descricao:?})",
10387                c.descricao(),
10388            );
10389            assert_eq!(
10390                c.descricao(),
10391                c.descricao.as_deref(),
10392                "Caixa::descricao must byte-equal the raw \
10393                 `self.descricao.as_deref()` field access across every \
10394                 value in the Option<&str> accept-set",
10395            );
10396        }
10397    }
10398
10399    #[test]
10400    fn validate_descricao_empty_arm_routes_through_accessor() {
10401        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
10402        // gate must key off [`Caixa::descricao`], not the raw
10403        // `self.descricao.as_deref()` field access. Structurally: a
10404        // `Caixa { descricao: Some(""), .. }` must surface the
10405        // `DescricaoEmpty` refusal exactly, and a
10406        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
10407        // canonical one-line-prose form) must pass validate. The pair
10408        // jointly pins the accessor + validate-gate composition: any
10409        // future silent detour that had the accessor return `None` on
10410        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10411        // silently absorb the `DescricaoEmpty` refusal at the accessor
10412        // boundary and the validate gate would accept a struct-literal
10413        // `Caixa { descricao: Some(""), .. }` — the composition pin
10414        // catches that at caixa-core build time.
10415        //
10416        // Peer of the [`Caixa::licenca`] (6d5bc28)
10417        // `validate_licenca_empty_arm_routes_through_accessor` and
10418        // [`Caixa::repositorio`] (cc7332d)
10419        // `validate_repositorio_empty_arm_routes_through_accessor`
10420        // composition pins on the sibling outer top-level [`Caixa`]
10421        // `Option<&str>` universal-axis surface — same "the validate /
10422        // shape-gate predicate must route through the substrate-
10423        // primitive typed dispatch" discipline extended onto the third
10424        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10425        // composition surface.
10426        let c = caixa_with_descricao(Some(""));
10427        assert!(
10428            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
10429            "validate_descricao must reject descricao == Some(\"\") \
10430             with DescricaoEmpty — the accessor and the validate gate \
10431             must route through the same substrate-primitive typed \
10432             dispatch on the :descricao empty arm",
10433        );
10434        let c = caixa_with_descricao(Some("Checkout flow."));
10435        assert!(
10436            c.validate_descricao().is_ok(),
10437            "validate_descricao must accept descricao == \
10438             Some(\"Checkout flow.\") (the canonical one-line-prose \
10439             chart-description shape)",
10440        );
10441    }
10442
10443    #[test]
10444    fn descricao_projects_option_str_by_borrow() {
10445        // The by-borrow pin: [`Caixa::descricao`] returns
10446        // `Option<&str>` by borrow — the `&str` borrows the underlying
10447        // `String` storage of the `Option<String>` slot and the
10448        // accessor must not allocate a fresh `String` on every call.
10449        // Peer of the [`Caixa::licenca`] (6d5bc28) and
10450        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
10451        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
10452        // the per-`:placement`
10453        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10454        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
10455        // return axis, extended onto the third outer top-level
10456        // [`Caixa`] universal-axis `Option<&str>` shape — the
10457        // accessor's returned `&str` must borrow from `&self` (the
10458        // returned reference's lifetime is tied to `&self`), and
10459        // calling the accessor twice on the same [`Caixa`] must yield
10460        // the same `Option<&str>` verbatim (idempotent, no side
10461        // effects on `&self`).
10462        //
10463        // Pins against a future silent detour that returned an owned
10464        // `Option<String>` (which would type-check but silently
10465        // allocate on every call, breaking the zero-cost projection
10466        // every peer sibling accessor carries), or a one-arm-only
10467        // accessor that returned a saturating value on some sentinel
10468        // input (breaking the pass-through invariant the sibling
10469        // required-scalar accessors carry).
10470        for descricao in [
10471            None,
10472            Some(""),
10473            Some("Checkout flow."),
10474            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
10475        ] {
10476            let c = caixa_with_descricao(descricao);
10477            let first = c.descricao();
10478            let second = c.descricao();
10479            assert_eq!(
10480                first, second,
10481                "Caixa::descricao must be idempotent — two successive \
10482                 calls on the same &self must return the same \
10483                 Option<&str>",
10484            );
10485            assert_eq!(
10486                first, descricao,
10487                "Caixa::descricao must return :descricao verbatim by \
10488                 borrow — got {first:?}, expected {descricao:?}",
10489            );
10490        }
10491    }
10492
10493    // ── validate_edicao — universal-axis language-edition shape ──
10494
10495    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
10496        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10497        c.edicao = edicao.map(String::from);
10498        c
10499    }
10500
10501    #[test]
10502    fn validate_edicao_accepts_none() {
10503        // The omit-the-slot identity: `:edicao` is optional. The
10504        // gate is a no-op when the author didn't declare a value —
10505        // every caixa without an `:edicao` line trivially passes,
10506        // and the substrate-side build pipeline falls back to the
10507        // documented default edition. Mirrors the peer
10508        // `validate_licenca_accepts_none` posture on the sibling
10509        // `Option<String>` Caixa slot.
10510        let c = caixa_with_edicao(None);
10511        c.validate_edicao().unwrap();
10512    }
10513
10514    #[test]
10515    fn validate_edicao_accepts_canonical_value() {
10516        // Positive control: the canonical `"2026"` edition every
10517        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
10518        // `caixa-mesh`) carries by construction passes the gate.
10519        // Future-introduced sibling editions (`"2027"`, `"2030"`,
10520        // `"2049"`) that match the same 4-digit ASCII decimal year
10521        // shape must also trivially pass — the structural shape
10522        // predicate accepts every well-formed year regardless of
10523        // whether the substrate yet understands the specific value
10524        // (a future known-edition allowlist tightens that).
10525        for ed in ["2026", "2027", "2030", "2049"] {
10526            let c = caixa_with_edicao(Some(ed));
10527            c.validate_edicao()
10528                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
10529        }
10530    }
10531
10532    #[test]
10533    fn validate_edicao_rejects_empty_some() {
10534        // Canonical paste-from-blank-doc footgun. Without this gate
10535        // the empty `Some("")` silently lands as `(:edicao "")` in
10536        // the rendered caixa.lisp and a future renderer-side
10537        // consumer's `Option::unwrap_or_else` (which only fires on
10538        // `None`) skips its fallback. Mirrors the peer
10539        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
10540        // `Option<String>` Caixa slot.
10541        let c = caixa_with_edicao(Some(""));
10542        let err = c.validate_edicao().unwrap_err();
10543        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
10544    }
10545
10546    #[test]
10547    fn validate_edicao_rejects_free_form_non_year() {
10548        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
10549        // `"nightly"` shapes carry no operational meaning on the
10550        // substrate's build-time edition selector. Until this gate
10551        // landed the bare empty-arm check let every such value
10552        // through and broke far from the source caixa.lisp. Peer
10553        // with the shape-predicate cascade
10554        // `validate_repositorio_rejects_missing_colon_separator`
10555        // establishes past its own empty arm.
10556        for ed in ["x", "latest", "nightly", "stable"] {
10557            let c = caixa_with_edicao(Some(ed));
10558            let err = c.validate_edicao().unwrap_err();
10559            assert!(
10560                matches!(err, ManifestError::EdicaoInvalid { .. }),
10561                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10562            );
10563        }
10564    }
10565
10566    #[test]
10567    fn validate_edicao_rejects_trailing_whitespace() {
10568        // Paste-from-doc whitespace footgun. A trailing space in
10569        // the `:edicao` value would silently break the substrate's
10570        // build-time edition match-table lookup at the rendered
10571        // artifact's edition-selector consumer. The shape predicate
10572        // refuses every whitespace byte by construction (any byte
10573        // outside `0-9` fails `is_ascii_digit`). Peer with
10574        // `validate_repositorio_rejects_whitespace`.
10575        let c = caixa_with_edicao(Some("2026 "));
10576        let err = c.validate_edicao().unwrap_err();
10577        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
10578            panic!("expected EdicaoInvalid, got {err:?}");
10579        };
10580        assert_eq!(edicao, "2026 ");
10581    }
10582
10583    #[test]
10584    fn validate_edicao_rejects_leading_whitespace() {
10585        // Symmetric paste-from-doc whitespace footgun on the leading
10586        // boundary — the gate refuses every shape with a non-digit
10587        // byte by construction.
10588        let c = caixa_with_edicao(Some(" 2026"));
10589        let err = c.validate_edicao().unwrap_err();
10590        assert!(
10591            matches!(err, ManifestError::EdicaoInvalid { .. }),
10592            "got {err:?}",
10593        );
10594    }
10595
10596    #[test]
10597    fn validate_edicao_rejects_control_char() {
10598        // Paste-from-multiline-doc CRLF footgun — control characters
10599        // at the value boundary break the substrate's build-time
10600        // edition-selector parser. Peer with
10601        // `validate_repositorio_rejects_control_char`.
10602        let c = caixa_with_edicao(Some("2026\n"));
10603        let err = c.validate_edicao().unwrap_err();
10604        assert!(
10605            matches!(err, ManifestError::EdicaoInvalid { .. }),
10606            "got {err:?}",
10607        );
10608    }
10609
10610    #[test]
10611    fn validate_edicao_rejects_non_ascii_lookalike() {
10612        // Fullwidth-keyboard look-alike footgun — `"2026"` is
10613        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
10614        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
10615        // edition selector wants an ASCII year, and the gate
10616        // refuses every non-ASCII shape by construction (length in
10617        // bytes is 12 ≠ 4, *and* every byte falls outside
10618        // `is_ascii_digit`'s `0-9` range).
10619        let c = caixa_with_edicao(Some("2026"));
10620        let err = c.validate_edicao().unwrap_err();
10621        assert!(
10622            matches!(err, ManifestError::EdicaoInvalid { .. }),
10623            "got {err:?}",
10624        );
10625    }
10626
10627    #[test]
10628    fn validate_edicao_rejects_version_tag_prefix() {
10629        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
10630        // / `"r2026"` are familiar shapes from git-tag / Rust
10631        // edition / release-tag conventions that don't apply to
10632        // the year-shaped edition axis. The shape predicate refuses
10633        // every leading non-digit prefix.
10634        for ed in ["v2026", "e2026", "r2026"] {
10635            let c = caixa_with_edicao(Some(ed));
10636            let err = c.validate_edicao().unwrap_err();
10637            assert!(
10638                matches!(err, ManifestError::EdicaoInvalid { .. }),
10639                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10640            );
10641        }
10642    }
10643
10644    #[test]
10645    fn validate_edicao_rejects_decimal_shape() {
10646        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
10647        // `"2026.0"` are familiar shapes from semver / float
10648        // conventions that don't apply to the year-shaped edition
10649        // axis. The shape predicate refuses every non-digit byte
10650        // (`.` falls outside `is_ascii_digit`).
10651        for ed in ["2026.1", "2026.0", "2026.0.1"] {
10652            let c = caixa_with_edicao(Some(ed));
10653            let err = c.validate_edicao().unwrap_err();
10654            assert!(
10655                matches!(err, ManifestError::EdicaoInvalid { .. }),
10656                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10657            );
10658        }
10659    }
10660
10661    #[test]
10662    fn validate_edicao_rejects_wrong_length_numeric() {
10663        // Wrong-length numeric footgun — `"26"` (truncated) /
10664        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
10665        // (zero-padded too wide) all parse as integers but don't
10666        // name a 4-digit year. The shape predicate refuses every
10667        // value whose length isn't exactly 4 bytes.
10668        for ed in ["26", "202", "20260", "00026", "9"] {
10669            let c = caixa_with_edicao(Some(ed));
10670            let err = c.validate_edicao().unwrap_err();
10671            assert!(
10672                matches!(err, ManifestError::EdicaoInvalid { .. }),
10673                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10674            );
10675        }
10676    }
10677
10678    #[test]
10679    fn validate_edicao_empty_takes_precedence_over_shape() {
10680        // Empty-first cascade pin: the empty `Some("")` surfaces
10681        // the narrower `EdicaoEmpty` not the shape-predicate-
10682        // wrapped `EdicaoInvalid`, mirroring the peer
10683        // `validate_repositorio_empty_takes_precedence_over_shape`
10684        // (`RepositorioEmpty` → `RepositorioInvalid`),
10685        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
10686        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
10687        // cascades. The shape predicate also refuses the empty
10688        // input (defensively — `s.len() != 4`), but the
10689        // manifest-layer empty arm runs first to surface the
10690        // narrower diagnostic verbatim.
10691        let c = caixa_with_edicao(Some(""));
10692        let err = c.validate_edicao().unwrap_err();
10693        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
10694    }
10695
10696    #[test]
10697    fn validate_edicao_template_passes() {
10698        // Round-trip pin: the bare `Caixa::template` shape (which
10699        // carries `:edicao "2026"` verbatim) passes the gate by
10700        // construction. A future template-shape change that
10701        // introduced `(:edicao "")` or a non-year value would
10702        // surface here as a regression. Mirrors the peer
10703        // `validate_licenca_template_passes` pin.
10704        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10705        c.validate_edicao().unwrap();
10706    }
10707
10708    #[test]
10709    fn validate_edicao_diagnostic_names_offending_slot() {
10710        // Diagnostic-shape pin (peer with
10711        // `validate_licenca_diagnostic_names_offending_slot`): the
10712        // error's Display surfaces the `:edicao` slot name verbatim,
10713        // so a `feira lint` run can render the diagnostic without
10714        // re-parsing and the author can grep their caixa.lisp for
10715        // the offending `:edicao` line.
10716        let c = caixa_with_edicao(Some(""));
10717        let rendered = c.validate_edicao().unwrap_err().to_string();
10718        assert!(
10719            rendered.contains(":edicao"),
10720            "diagnostic must name the offending slot: {rendered}",
10721        );
10722    }
10723
10724    #[test]
10725    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
10726        // Diagnostic-shape pin on the shape-predicate arm (peer
10727        // with `validate_repositorio_diagnostic_carries_offending_value`):
10728        // the error's Display surfaces the offending value + slot
10729        // name verbatim, so a `feira lint` run can render the
10730        // diagnostic without re-parsing and the author can grep
10731        // their caixa.lisp for the offending `:edicao` value.
10732        let c = caixa_with_edicao(Some("v2026"));
10733        let rendered = c.validate_edicao().unwrap_err().to_string();
10734        assert!(
10735            rendered.contains(":edicao"),
10736            "diagnostic must name the offending slot: {rendered}",
10737        );
10738        assert!(
10739            rendered.contains("v2026"),
10740            "diagnostic must quote the offending value: {rendered}",
10741        );
10742    }
10743
10744    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
10745
10746    #[test]
10747    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
10748        // The canonical per-`Caixa` `:edicao` language-edition scalar
10749        // pin: [`Caixa::edicao`] must return the `:edicao` typed
10750        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10751        // raw `self.edicao.as_deref()` access across every representative
10752        // value in the accept-set — `None` (the "omit the slot to defer
10753        // to the substrate's default edition" arm every existing
10754        // [`caixa-resolver`] fixture without an `:edicao` line carries),
10755        // `Some("")` (a past-the-guard sentinel that pins the accessor
10756        // doesn't perform a silent `Some("") → None` collapse on the
10757        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
10758        // but the accessor must ship the raw slot verbatim so a
10759        // validate-time gate regression surfaces at any future edition-
10760        // aware consumer's boundary rather than being silently absorbed
10761        // into the substrate's default edition), `Some("2026")` (the
10762        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
10763        // template scaffolds via [`Caixa::template`] and every
10764        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
10765        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
10766        // carries by construction), `Some("2018")` / `Some("2021")` /
10767        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
10768        // peer with Cargo's `[package] edition` grammar every future-
10769        // introduced sibling to `"2026"` will follow), and eight
10770        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
10771        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
10772        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
10773        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
10774        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
10775        // length-numeric, `Some("latest")` free-form-non-year — the
10776        // sentinels pin the accessor doesn't silently absorb the
10777        // refusal cases into a substrate-default-edition fallback).
10778        //
10779        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
10780        // return scalar accessor pin on the substrate primitive —
10781        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
10782        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
10783        // (3f16e2f) pins that opened the "outer [`Caixa`]
10784        // `Option<&str>` scalar" projection pin pattern this pin folds
10785        // on. Sibling in shape to the peer per-`:placement`
10786        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10787        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10788        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10789        // axes, extended onto the outer top-level [`Caixa`] universal-
10790        // axis surface's last unlifted `Option<String>` slot. Pins
10791        // against a future silent detour that returned an owned
10792        // `Option<String>` (which would type-check but silently
10793        // allocate on every accessor call, breaking the zero-cost
10794        // projection every peer sibling accessor carries), a
10795        // `Some("") → None` collapse (which would silently absorb the
10796        // `EdicaoEmpty` refusal case at the accessor boundary and any
10797        // future edition-aware consumer would silently fall back to
10798        // the substrate's default edition on a struct-literal
10799        // `Caixa { edicao: Some(""), .. }`), or a
10800        // `None → Some("2026")` collapse (which would silently reify
10801        // the substrate's default edition at the accessor boundary
10802        // and every downstream consumer keying off the
10803        // `Option::is_none()` discriminator would lose the "author
10804        // omitted the slot" signal).
10805        for edicao in [
10806            None,
10807            Some(""),
10808            Some("2026"),
10809            Some("2018"),
10810            Some("2021"),
10811            Some("2024"),
10812            Some("2026 "),
10813            Some(" 2026"),
10814            Some("2026\n"),
10815            Some("2026"),
10816            Some("v2026"),
10817            Some("2026.1"),
10818            Some("26"),
10819            Some("latest"),
10820        ] {
10821            let c = caixa_with_edicao(edicao);
10822            assert_eq!(
10823                c.edicao(),
10824                edicao,
10825                "Caixa::edicao must return :edicao verbatim (got {:?}, \
10826                 expected {edicao:?})",
10827                c.edicao(),
10828            );
10829            assert_eq!(
10830                c.edicao(),
10831                c.edicao.as_deref(),
10832                "Caixa::edicao must byte-equal the raw \
10833                 `self.edicao.as_deref()` field access across every \
10834                 value in the Option<&str> accept-set",
10835            );
10836        }
10837    }
10838
10839    #[test]
10840    fn validate_edicao_empty_arm_routes_through_accessor() {
10841        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
10842        // must key off [`Caixa::edicao`], not the raw
10843        // `self.edicao.as_deref()` field access. Structurally: a
10844        // `Caixa { edicao: Some(""), .. }` must surface the
10845        // `EdicaoEmpty` refusal exactly, and a
10846        // `Caixa { edicao: Some("2026"), .. }` (the canonical
10847        // 4-digit-ASCII-decimal-year form) must pass validate. The
10848        // pair jointly pins the accessor + validate-gate composition:
10849        // any future silent detour that had the accessor return `None`
10850        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
10851        // would silently absorb the `EdicaoEmpty` refusal at the
10852        // accessor boundary and the validate gate would accept a
10853        // struct-literal `Caixa { edicao: Some(""), .. }` — the
10854        // composition pin catches that at caixa-core build time.
10855        //
10856        // Peer of the [`Caixa::licenca`] (6d5bc28)
10857        // `validate_licenca_empty_arm_routes_through_accessor`,
10858        // [`Caixa::repositorio`] (cc7332d)
10859        // `validate_repositorio_empty_arm_routes_through_accessor`,
10860        // and [`Caixa::descricao`] (3f16e2f)
10861        // `validate_descricao_empty_arm_routes_through_accessor`
10862        // composition pins on the sibling outer top-level [`Caixa`]
10863        // `Option<&str>` universal-axis surface — same "the validate /
10864        // shape-gate predicate must route through the substrate-
10865        // primitive typed dispatch" discipline extended onto the
10866        // fourth and final outer top-level [`Caixa`] universal-axis
10867        // `Option<&str>`-composition surface, closing the accessor-
10868        // composition family.
10869        let c = caixa_with_edicao(Some(""));
10870        assert!(
10871            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
10872            "validate_edicao must reject edicao == Some(\"\") with \
10873             EdicaoEmpty — the accessor and the validate gate must \
10874             route through the same substrate-primitive typed dispatch \
10875             on the :edicao empty arm",
10876        );
10877        let c = caixa_with_edicao(Some("2026"));
10878        assert!(
10879            c.validate_edicao().is_ok(),
10880            "validate_edicao must accept edicao == Some(\"2026\") \
10881             (the canonical 4-digit-ASCII-decimal-year shape)",
10882        );
10883    }
10884
10885    #[test]
10886    fn edicao_projects_option_str_by_borrow() {
10887        // The by-borrow pin: [`Caixa::edicao`] returns
10888        // `Option<&str>` by borrow — the `&str` borrows the underlying
10889        // `String` storage of the `Option<String>` slot and the
10890        // accessor must not allocate a fresh `String` on every call.
10891        // Peer of the [`Caixa::licenca`] (6d5bc28),
10892        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
10893        // (3f16e2f) by-borrow pins on the peer outer top-level
10894        // [`Caixa`] `Option<&str>`-return axes, and of the
10895        // per-`:placement`
10896        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10897        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
10898        // return axis, extended onto the fourth and final outer top-
10899        // level [`Caixa`] universal-axis `Option<&str>` shape — the
10900        // accessor's returned `&str` must borrow from `&self` (the
10901        // returned reference's lifetime is tied to `&self`), and
10902        // calling the accessor twice on the same [`Caixa`] must yield
10903        // the same `Option<&str>` verbatim (idempotent, no side
10904        // effects on `&self`).
10905        //
10906        // Pins against a future silent detour that returned an owned
10907        // `Option<String>` (which would type-check but silently
10908        // allocate on every call, breaking the zero-cost projection
10909        // every peer sibling accessor carries), or a one-arm-only
10910        // accessor that returned a saturating value on some sentinel
10911        // input (breaking the pass-through invariant the sibling
10912        // required-scalar accessors carry).
10913        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
10914            let c = caixa_with_edicao(edicao);
10915            let first = c.edicao();
10916            let second = c.edicao();
10917            assert_eq!(
10918                first, second,
10919                "Caixa::edicao must be idempotent — two successive \
10920                 calls on the same &self must return the same \
10921                 Option<&str>",
10922            );
10923            assert_eq!(
10924                first, edicao,
10925                "Caixa::edicao must return :edicao verbatim by \
10926                 borrow — got {first:?}, expected {edicao:?}",
10927            );
10928        }
10929    }
10930
10931    #[test]
10932    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
10933        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
10934        // label caixa-identity scalar pin: [`Caixa::nome`] must return
10935        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
10936        // the raw field access across every representative value in
10937        // the accept-set — the canonical `"demo"` template baseline
10938        // (the same `feira init`-scaffolded default the sibling
10939        // `validate_nome_accepts_canonical_template` positive-control
10940        // gate pins), plus every sibling per-typed-slot atom accessor's
10941        // canonical positive-arm byte-string (`"catalog"` per
10942        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
10943        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
10944        // `caixa-helm`/`caixa-flux` cross-crate integration-test
10945        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
10946        // canonical example), plus every past-the-guard sentinel for
10947        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
10948        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
10949        // the bare DNS-1123 63-byte cap but overflows the joint
10950        // `lareira-<nome>` chart-name budget the sibling
10951        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
10952        //
10953        // The past-the-guard sentinels pin the accessor doesn't
10954        // silently absorb the refusal cases into a template-derived
10955        // fallback (a future `.nome().is_empty().then(|| "demo")`
10956        // collapse would silently absorb the `NomeEmpty` refusal at
10957        // the accessor boundary and the validate gate would accept a
10958        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
10959        // catches that at caixa-core build time).
10960        //
10961        // First outer top-level [`Caixa`] `&str`-return required-
10962        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
10963        // required-scalar" projection pattern the sibling per-`Caixa`
10964        // `:versao` future lift folds on. Sibling in shape to the peer
10965        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
10966        // required-`String`-carry accessor pin on the sibling per-
10967        // sub-struct required-axis, extended onto the outer top-level
10968        // [`Caixa`] universal-axis required-`String`-carry axis.
10969        for nome in [
10970            "demo",
10971            "catalog",
10972            "cart",
10973            "hello-rio",
10974            "checkout",
10975            "",
10976            "Bad_Name",
10977            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
10978        ] {
10979            let c = caixa_with_nome(nome);
10980            assert_eq!(
10981                c.nome(),
10982                nome,
10983                "Caixa::nome must return :nome verbatim (got {}, \
10984                 expected {nome})",
10985                c.nome(),
10986            );
10987            assert_eq!(
10988                c.nome(),
10989                c.nome.as_str(),
10990                "Caixa::nome must byte-equal the raw .nome field \
10991                 access across every value in the String accept-set",
10992            );
10993        }
10994    }
10995
10996    #[test]
10997    fn validate_nome_empty_arm_routes_through_accessor() {
10998        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
10999        // key off [`Caixa::nome`], not the raw `.nome` field access.
11000        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
11001        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
11002        // template baseline (the peer positive-arm the sibling
11003        // `validate_nome_accepts_canonical_template` gate carves out)
11004        // must pass validate. The pair jointly pins the accessor +
11005        // validate-gate composition: any future silent detour that
11006        // had the accessor return a fresh `"demo"` on the empty arm
11007        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
11008        // would silently absorb the `NomeEmpty` refusal at the
11009        // accessor boundary and the validate gate would accept a
11010        // struct-literal `Caixa { nome: "".into(), .. }` — the
11011        // composition pin catches that at caixa-core build time.
11012        //
11013        // Peer of the sibling per-`Caixa`
11014        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
11015        // / `validate_repositorio_empty_arm_routes_through_accessor`
11016        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
11017        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
11018        // (2641cbd) composition pins on the sibling outer top-level
11019        // [`Caixa`] `Option<&str>` axes — same "the validate /
11020        // shape-gate predicate must route through the substrate-
11021        // primitive typed dispatch" discipline extended onto the peer
11022        // outer top-level [`Caixa`] required-`&str` composition axis.
11023        let c = caixa_with_nome("");
11024        assert!(
11025            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
11026            "validate_nome must reject nome == \"\" with NomeEmpty — \
11027             the accessor and the validate gate must route through the \
11028             same substrate-primitive typed dispatch on the :nome \
11029             empty-arm",
11030        );
11031        let c = caixa_with_nome("demo");
11032        assert!(
11033            c.validate_nome().is_ok(),
11034            "validate_nome must accept nome == \"demo\" (the canonical \
11035             DNS-1123-label template baseline)",
11036        );
11037    }
11038
11039    #[test]
11040    fn nome_projects_str_by_borrow() {
11041        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
11042        // — the `&str` borrows the underlying `String` storage of the
11043        // required `nome` slot and the accessor must not allocate a
11044        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
11045        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
11046        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
11047        // by-borrow pins on the peer outer top-level [`Caixa`]
11048        // `Option<&str>`-return axes, extended onto the first outer
11049        // top-level [`Caixa`] required-`&str`-return axis — the
11050        // accessor's returned `&str` must borrow from `&self` (the
11051        // returned reference's lifetime is tied to `&self`), and
11052        // calling the accessor twice on the same [`Caixa`] must yield
11053        // the same `&str` verbatim (idempotent, no side effects on
11054        // `&self`).
11055        //
11056        // Pins against a future silent detour that returned an owned
11057        // `String` (which would type-check but silently allocate on
11058        // every call, breaking the zero-cost projection every peer
11059        // sibling accessor carries), an accidental
11060        // `.nome.to_lowercase()` detour that returned a fresh
11061        // allocation through an already-DNS-1123-lowercase-only
11062        // string (breaking a future `const fn` regression), or a
11063        // one-arm-only accessor that returned a canonicalized value
11064        // on some sentinel input (breaking the pass-through invariant
11065        // the sibling required-scalar accessors carry).
11066        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
11067            let c = caixa_with_nome(nome);
11068            let first = c.nome();
11069            let second = c.nome();
11070            assert_eq!(
11071                first, second,
11072                "Caixa::nome must be idempotent — two successive calls \
11073                 on the same &self must return the same &str",
11074            );
11075            assert_eq!(
11076                first, nome,
11077                "Caixa::nome must return :nome verbatim by borrow — \
11078                 got {first}, expected {nome}",
11079            );
11080        }
11081    }
11082
11083    #[test]
11084    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
11085        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
11086        // pinned-version scalar pin: [`Caixa::versao`] must return the
11087        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
11088        // raw `.versao` field access across every representative value
11089        // in the accept-set — the canonical `"0.1.0"` template baseline
11090        // (the same `feira init`-scaffolded default the sibling
11091        // `validate_versao_accepts_canonical_template` positive-control
11092        // gate pins), plus every canonical SemVer-2 shape the sibling
11093        // `validate_versao_accepts_canonical_forms` positive-arm sweep
11094        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
11095        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
11096        // `"10.20.30"`), plus every past-the-guard sentinel for the
11097        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
11098        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
11099        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
11100        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
11101        // `"latest"` the docker-tag-shape footgun — the sentinels pin
11102        // the accessor doesn't silently absorb the refusal cases into a
11103        // template-derived fallback like `"0.1.0"`).
11104        //
11105        // The past-the-guard sentinels pin the accessor doesn't silently
11106        // absorb the refusal cases into a template-derived fallback (a
11107        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
11108        // silently absorb the `VersaoEmpty` refusal at the accessor
11109        // boundary and the validate gate would accept a struct-literal
11110        // `Caixa { versao: "".into(), .. }` — the pin catches that at
11111        // caixa-core build time).
11112        //
11113        // Second outer top-level [`Caixa`] `&str`-return required-scalar
11114        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
11115        // scalar" projection pattern the sibling per-`Caixa`
11116        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
11117        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
11118        // (4127bb6) / per-`:children`
11119        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
11120        // / per-`:upgrade-from`
11121        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
11122        // struct `:versao`-shaped `&str`-return accessor pins on the
11123        // sibling per-typed-slot version-carrier axes, extended onto the
11124        // second outer top-level [`Caixa`] universal-axis required-
11125        // `String`-carry axis so the two universal-axis identity-
11126        // carrying scalars every `defcaixa` form supplies (`:nome` +
11127        // `:versao`) share the same "one typed dispatch per axis" pin
11128        // discipline.
11129        for versao in [
11130            "0.1.0",
11131            "0.0.0",
11132            "1.0.0",
11133            "0.2.0-rc.1",
11134            "1.0.0-alpha.0",
11135            "1.0.0+build.42",
11136            "1.0.0-rc.1+build.42",
11137            "10.20.30",
11138            "",
11139            "v0.1.0",
11140            "0.1",
11141            "^0.1",
11142            "0.1.0.0",
11143            "latest",
11144        ] {
11145            let c = caixa_with_versao(versao);
11146            assert_eq!(
11147                c.versao(),
11148                versao,
11149                "Caixa::versao must return :versao verbatim (got {}, \
11150                 expected {versao})",
11151                c.versao(),
11152            );
11153            assert_eq!(
11154                c.versao(),
11155                c.versao.as_str(),
11156                "Caixa::versao must byte-equal the raw .versao field \
11157                 access across every value in the String accept-set",
11158            );
11159        }
11160    }
11161
11162    #[test]
11163    fn validate_versao_empty_arm_routes_through_accessor() {
11164        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
11165        // must key off [`Caixa::versao`], not the raw `.versao` field
11166        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
11167        // surface the `VersaoEmpty` refusal exactly, and the canonical
11168        // `"0.1.0"` template baseline (the peer positive-arm the sibling
11169        // `validate_versao_accepts_canonical_template` gate carves out)
11170        // must pass validate. The pair jointly pins the accessor +
11171        // validate-gate composition: any future silent detour that had
11172        // the accessor return a fresh `"0.1.0"` on the empty arm
11173        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
11174        // would silently absorb the `VersaoEmpty` refusal at the
11175        // accessor boundary and the validate gate would accept a
11176        // struct-literal `Caixa { versao: "".into(), .. }` — the
11177        // composition pin catches that at caixa-core build time.
11178        //
11179        // Peer of the sibling per-`Caixa`
11180        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
11181        // composition pin on the sibling outer top-level [`Caixa`]
11182        // required-`&str` universal-axis surface — same "the validate /
11183        // shape-gate predicate must route through the substrate-
11184        // primitive typed dispatch" discipline extended onto the peer
11185        // outer top-level [`Caixa`] required-`&str` universal-axis
11186        // pinned-version composition axis, closing the second
11187        // coordinate of the "one canonical typed dispatch per per-Caixa
11188        // required-`&str` universal-axis" discipline.
11189        let c = caixa_with_versao("");
11190        assert!(
11191            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
11192            "validate_versao must reject versao == \"\" with VersaoEmpty — \
11193             the accessor and the validate gate must route through the \
11194             same substrate-primitive typed dispatch on the :versao \
11195             empty-arm",
11196        );
11197        let c = caixa_with_versao("0.1.0");
11198        assert!(
11199            c.validate_versao().is_ok(),
11200            "validate_versao must accept versao == \"0.1.0\" (the \
11201             canonical SemVer-2 template baseline)",
11202        );
11203    }
11204
11205    #[test]
11206    fn versao_projects_str_by_borrow() {
11207        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
11208        // — the `&str` borrows the underlying `String` storage of the
11209        // required `versao` slot and the accessor must not allocate a
11210        // fresh `String` on every call. Peer of the [`Caixa::nome`]
11211        // (e6b7d97) by-borrow pin on the sibling outer top-level
11212        // [`Caixa`] required-`&str`-return axis, extended onto the
11213        // second outer top-level [`Caixa`] required-`&str`-return
11214        // universal-axis pinned-version surface — the accessor's
11215        // returned `&str` must borrow from `&self` (the returned
11216        // reference's lifetime is tied to `&self`), and calling the
11217        // accessor twice on the same [`Caixa`] must yield the same
11218        // `&str` verbatim (idempotent, no side effects on `&self`).
11219        //
11220        // Pins against a future silent detour that returned an owned
11221        // `String` (which would type-check but silently allocate on
11222        // every call, breaking the zero-cost projection every peer
11223        // sibling accessor carries), an accidental
11224        // `semver::Version::parse(&self.versao).unwrap().to_string()`
11225        // detour that returned a canonicalized fresh allocation through
11226        // an already-canonical byte-string (breaking a future `const fn`
11227        // regression and silently absorbing the `VersaoInvalid` refusal
11228        // at the accessor boundary), or a one-arm-only accessor that
11229        // returned a canonicalized value on some sentinel input
11230        // (breaking the pass-through invariant the sibling required-
11231        // scalar accessors carry).
11232        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
11233            let c = caixa_with_versao(versao);
11234            let first = c.versao();
11235            let second = c.versao();
11236            assert_eq!(
11237                first, second,
11238                "Caixa::versao must be idempotent — two successive \
11239                 calls on the same &self must return the same &str",
11240            );
11241            assert_eq!(
11242                first, versao,
11243                "Caixa::versao must return :versao verbatim by borrow \
11244                 — got {first}, expected {versao}",
11245            );
11246        }
11247    }
11248
11249    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
11250        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11251        c.kind = kind;
11252        c
11253    }
11254
11255    #[test]
11256    fn kind_returns_kind_variant_verbatim_across_permutations() {
11257        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
11258        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
11259        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
11260        // the raw `.kind` field access across every variant in the
11261        // closed accept-set (`Biblioteca` — the library kind that
11262        // exports lisp forms; `Binario` — the nix-built executable kind
11263        // under `exe/`; `Servico` — the wasm-component daemon kind
11264        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
11265        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
11266        // composition kind).
11267        //
11268        // Pins against a future silent detour that re-derived the kind
11269        // from a peer axis (an accidental fallback to
11270        // `if !servicos.is_empty() { Servico } else if
11271        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
11272        // collapse that read the code-surface / mesh-slot columns into
11273        // the kind discriminator), a variant remap the operator
11274        // authors on one consumer without the other, or a stale-derive
11275        // detour that substituted [`CaixaKind::Biblioteca`] as the
11276        // default when the field held any other variant (which would
11277        // silently collapse the distinction between "author explicitly
11278        // declared `:kind Servico`" and "author declared any other
11279        // kind" every downstream renderer-dispatch site depends on).
11280        //
11281        // First outer top-level [`Caixa`] `Copy`-return required-enum-
11282        // discriminant accessor pin — opens the "outer [`Caixa`]
11283        // `Copy`-return required-discriminant" projection pattern.
11284        // Sibling in shape to the peer per-`:supervisor`
11285        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
11286        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
11287        // (921fe1b), and per-`:children`
11288        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
11289        // `Copy`-return closed-set-enum discriminant accessor pins on
11290        // the sibling nested-spec typed-slot discriminator axes,
11291        // extended here to the outer top-level [`Caixa`] universal-
11292        // axis surface.
11293        for kind in [
11294            CaixaKind::Biblioteca,
11295            CaixaKind::Binario,
11296            CaixaKind::Servico,
11297            CaixaKind::Supervisor,
11298            CaixaKind::Aplicacao,
11299        ] {
11300            let c = caixa_with_kind(kind);
11301            assert_eq!(
11302                c.kind(),
11303                kind,
11304                "Caixa::kind must return :kind verbatim (got {:?}, \
11305                 expected {kind:?})",
11306                c.kind(),
11307            );
11308            assert_eq!(
11309                c.kind(),
11310                c.kind,
11311                "Caixa::kind accessor and .kind field access must \
11312                 byte-equal — the accessor is the substrate-primitive \
11313                 typed dispatch every downstream kind-gate consumer \
11314                 must route through",
11315            );
11316        }
11317    }
11318
11319    #[test]
11320    fn require_kind_reads_through_lifted_kind_accessor() {
11321        // Two-consumer coherence pin: the [`crate::render::require_kind`]
11322        // entry-gate predicate (the canonical two-line
11323        // `require_kind(caixa, Servico)?` prelude every per-Servico /
11324        // per-Aplicacao renderer runs at its entry-point) and the
11325        // sibling [`crate::render::KindMismatch`] error carrier's
11326        // `actual:` field (which names the offending caixa's variant
11327        // in the diagnostic) must both key off the lifted accessor, so
11328        // any future rebrand on the typed slot's reader shape lands at
11329        // exactly one place. Pins the two-site coherence by exercising
11330        // every off-diagonal `(actual, expected)` pair across the
11331        // closed accept-set — the `KindMismatch { actual, expected }`
11332        // surfaced on the mismatch arm must byte-equal the pair the
11333        // accessor returns for each side.
11334        //
11335        // Peer of the sibling per-`:placement`
11336        // `validate_placement_reads_through_lifted_estrategia_accessor`
11337        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
11338        // `Copy`-return discriminant axis — same "the entry-gate
11339        // predicate and the error carrier's `actual:` field must route
11340        // through the substrate-primitive typed dispatch" discipline
11341        // extended onto the outer top-level [`Caixa`] universal-axis
11342        // discriminant surface.
11343        for expected in [
11344            CaixaKind::Biblioteca,
11345            CaixaKind::Binario,
11346            CaixaKind::Servico,
11347            CaixaKind::Supervisor,
11348            CaixaKind::Aplicacao,
11349        ] {
11350            for actual in [
11351                CaixaKind::Biblioteca,
11352                CaixaKind::Binario,
11353                CaixaKind::Servico,
11354                CaixaKind::Supervisor,
11355                CaixaKind::Aplicacao,
11356            ] {
11357                let c = caixa_with_kind(actual);
11358                let result = crate::render::require_kind(&c, expected);
11359                if expected == actual {
11360                    assert!(
11361                        result.is_ok(),
11362                        "require_kind must accept when actual == expected \
11363                         (actual={actual:?}, expected={expected:?})",
11364                    );
11365                } else {
11366                    let err = result.expect_err("require_kind must reject when actual != expected");
11367                    assert_eq!(
11368                        err.actual,
11369                        c.kind(),
11370                        "KindMismatch.actual must byte-equal Caixa::kind() \
11371                         — the error carrier's `actual:` field reads \
11372                         through the lifted accessor",
11373                    );
11374                    assert_eq!(
11375                        err.expected, expected,
11376                        "KindMismatch.expected must byte-equal the \
11377                         expected variant passed to require_kind",
11378                    );
11379                }
11380            }
11381        }
11382    }
11383
11384    #[test]
11385    fn aplicacao_view_kind_gate_routes_through_accessor() {
11386        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
11387        // must key off [`Caixa::kind`], not the raw `.kind` field
11388        // access. Structurally: a `Caixa { kind: X, .. }` for any
11389        // non-`Aplicacao` variant must fold to `None` on the
11390        // `aplicacao_view` composer (the "kind mismatch → no typed
11391        // view" contract every downstream Aplicacao consumer keys off
11392        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
11393        // `Some(_)`. The pair jointly pins the accessor + view-gate
11394        // composition: any future silent detour that had the accessor
11395        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
11396        // input would silently absorb the kind-mismatch case at the
11397        // accessor boundary and every per-Aplicacao renderer would
11398        // silently render a non-Aplicacao caixa's mesh slots — the
11399        // composition pin catches that at caixa-core build time.
11400        //
11401        // Peer of the sibling per-`Caixa`
11402        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
11403        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
11404        // composition pins on the sibling outer top-level [`Caixa`]
11405        // required-`&str` universal-axis surfaces — same "the
11406        // composer / validate gate must route through the substrate-
11407        // primitive typed dispatch" discipline extended onto the
11408        // outer top-level [`Caixa`] `Copy`-return required-
11409        // discriminant composition axis.
11410        for kind in [
11411            CaixaKind::Biblioteca,
11412            CaixaKind::Binario,
11413            CaixaKind::Servico,
11414            CaixaKind::Supervisor,
11415        ] {
11416            let c = caixa_with_kind(kind);
11417            assert!(
11418                c.aplicacao_view().is_none(),
11419                "aplicacao_view must return None on non-Aplicacao \
11420                 kind {kind:?} — the composer's kind-gate must route \
11421                 through Caixa::kind()",
11422            );
11423        }
11424        let c = caixa_with_kind(CaixaKind::Aplicacao);
11425        assert!(
11426            c.aplicacao_view().is_some(),
11427            "aplicacao_view must return Some on kind Aplicacao — \
11428             the composer's kind-gate must accept the matching arm \
11429             through Caixa::kind()",
11430        );
11431    }
11432
11433    #[test]
11434    fn supervisor_view_kind_gate_routes_through_accessor() {
11435        // Composition pin (mirror of the sibling
11436        // `aplicacao_view_kind_gate_routes_through_accessor` on the
11437        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
11438        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
11439        // field access. A `Caixa { kind: X, .. }` for any non-
11440        // `Supervisor` variant must fold to `None` on the
11441        // `supervisor_view` composer, and a `Caixa { kind:
11442        // Supervisor, .. }` must fold to `Some(_)`. Same peer
11443        // composition pin discipline on the second `_view` composer
11444        // axis.
11445        for kind in [
11446            CaixaKind::Biblioteca,
11447            CaixaKind::Binario,
11448            CaixaKind::Servico,
11449            CaixaKind::Aplicacao,
11450        ] {
11451            let c = caixa_with_kind(kind);
11452            assert!(
11453                c.supervisor_view().is_none(),
11454                "supervisor_view must return None on non-Supervisor \
11455                 kind {kind:?} — the composer's kind-gate must route \
11456                 through Caixa::kind()",
11457            );
11458        }
11459        let mut c = caixa_with_kind(CaixaKind::Supervisor);
11460        // A Supervisor caixa needs a strategy + at least one child to
11461        // fold to a Some(_) that also validates; the composer itself
11462        // requires only the kind arm, so bare kind flip is enough to
11463        // pin the `Some(_)` return, but we populate the minimum
11464        // supervisor shape so a future strengthening of the composer
11465        // to reject an empty spec doesn't false-positive this pin.
11466        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
11467        c.children = vec![crate::supervisor::ChildSpec {
11468            caixa: "child".into(),
11469            versao: "^0.1".into(),
11470            restart: crate::supervisor::RestartPolicy::Permanent,
11471        }];
11472        assert!(
11473            c.supervisor_view().is_some(),
11474            "supervisor_view must return Some on kind Supervisor — \
11475             the composer's kind-gate must accept the matching arm \
11476             through Caixa::kind()",
11477        );
11478    }
11479
11480    #[test]
11481    fn kind_projects_by_copy() {
11482        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
11483        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
11484        // `&self` (the returned value is owned, `Copy`-projected from
11485        // the underlying [`CaixaKind`] storage; two calls on the same
11486        // [`Caixa`] must yield byte-equal values). Peer of the peer
11487        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
11488        // `SupervisorSpec::estrategia` / per-`:children`
11489        // `ChildSpec::restart` `Copy`-return discriminant accessor
11490        // pins on the sibling nested-spec typed-slot discriminator
11491        // axes, extended onto the first outer top-level [`Caixa`]
11492        // required-`Copy`-return axis — pins against a future silent
11493        // detour that returned `&CaixaKind` (which would type-check
11494        // but silently constrain every consumer's callsite to a
11495        // borrow-shaped dispatch, breaking the zero-cost `Copy`
11496        // projection every peer sibling accessor carries).
11497        for kind in [
11498            CaixaKind::Biblioteca,
11499            CaixaKind::Binario,
11500            CaixaKind::Servico,
11501            CaixaKind::Supervisor,
11502            CaixaKind::Aplicacao,
11503        ] {
11504            let c = caixa_with_kind(kind);
11505            let first: CaixaKind = c.kind();
11506            let second: CaixaKind = c.kind();
11507            assert_eq!(
11508                first, second,
11509                "Caixa::kind must be idempotent — two successive \
11510                 calls on the same &self must return the same \
11511                 CaixaKind variant",
11512            );
11513            assert_eq!(
11514                first, kind,
11515                "Caixa::kind must return :kind verbatim by Copy — \
11516                 got {first:?}, expected {kind:?}",
11517            );
11518        }
11519    }
11520
11521    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
11522
11523    #[test]
11524    fn autores_returns_autores_slice_verbatim_across_permutations() {
11525        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
11526        // name-list slice pin: [`Caixa::autores`] must return the
11527        // `:autores` typed [`Vec<String>`] list verbatim as a
11528        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
11529        // access across every representative value in the accept-set —
11530        // `[]` (the "no maintainers declared" arm every existing
11531        // fixture without an `:autores` line carries), `[""]` (a past-
11532        // the-guard sentinel that pins the accessor doesn't perform a
11533        // silent `[""] → []` collapse on the empty-entry arm — validate
11534        // rejects `[""]` through `AutorEmpty` but the accessor must
11535        // ship the raw slot verbatim so a validate-time gate regression
11536        // surfaces at the caixa-helm emit boundary rather than being
11537        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
11538        // canonical single-maintainer form every `feira init` template
11539        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
11540        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
11541        // (the canonical RFC-5322 `<name> <email>` form the
11542        // `is_chart_maintainer_name_shape` predicate accepts), and
11543        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
11544        // sentinel — validate rejects through `AutorDuplicate` but the
11545        // accessor must ship the raw slot verbatim).
11546        //
11547        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
11548        // pin on the substrate primitive — opens the "outer [`Caixa`]
11549        // `&[T]` slice" projection pattern the sibling per-`Caixa`
11550        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
11551        // / `:servicos` / `:upgrade-from` / `:children` future lifts
11552        // fold on. Sibling in shape to the peer per-`:supervisor`
11553        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
11554        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
11555        // (a6e18d7), per-`:membros`
11556        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
11557        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
11558        // (0dcc926), and per-`:upgrade-from :instructions`
11559        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
11560        // `&[T]`-return slice accessor pins on the sibling per-M2 /
11561        // per-M3 typed-slot list axes, extended onto the outer top-
11562        // level [`Caixa`] universal-axis surface. Pins against a future
11563        // silent detour that returned an owned `Vec<String>` (which
11564        // would type-check but silently clone on every accessor call,
11565        // breaking the zero-cost projection every peer sibling slice
11566        // accessor carries), a `[""] → []` collapse (which would
11567        // silently absorb the `AutorEmpty` refusal case at the accessor
11568        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
11569        // would silently absorb the `AutorDuplicate` refusal case at
11570        // the accessor boundary and the caixa-helm `maintainers:` fold
11571        // would silently render a dedupped list on a struct-literal
11572        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
11573        for autores in [
11574            vec![],
11575            vec![""],
11576            vec!["pleme-io"],
11577            vec!["alice", "bob"],
11578            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
11579            vec!["pleme-io", "pleme-io"],
11580        ] {
11581            let c = caixa_with_autores(autores.clone());
11582            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
11583            assert_eq!(
11584                c.autores(),
11585                expected.as_slice(),
11586                "Caixa::autores must return :autores verbatim (got {:?}, \
11587                 expected {expected:?})",
11588                c.autores(),
11589            );
11590            assert_eq!(
11591                c.autores(),
11592                c.autores.as_slice(),
11593                "Caixa::autores must byte-equal the raw \
11594                 `self.autores.as_slice()` field access across every \
11595                 value in the Vec<String> accept-set",
11596            );
11597        }
11598    }
11599
11600    #[test]
11601    fn validate_autores_empty_entry_arm_routes_through_accessor() {
11602        // Composition pin: [`Caixa::validate_autores`]'s per-entry
11603        // empty-arm gate must key off [`Caixa::autores`], not the raw
11604        // `&self.autores` field-borrow walk. Structurally: a
11605        // `Caixa { autores: vec!["".into()], .. }` must surface the
11606        // `AutorEmpty` refusal exactly, and a
11607        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
11608        // canonical single-maintainer form) must pass validate. The
11609        // pair jointly pins the accessor + validate-gate composition:
11610        // any future silent detour that had the accessor return an
11611        // empty slice on the `[""]` arm (a
11612        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
11613        // would silently absorb the `AutorEmpty` refusal at the
11614        // accessor boundary and the validate gate would accept a
11615        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
11616        // the composition pin catches that at caixa-core build time.
11617        //
11618        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
11619        // accessor-composition pin
11620        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
11621        // sibling `Option<&str>`-composition axis and the
11622        // per-`:politicas :circuit-breaker`
11623        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
11624        // accessor-composition pin
11625        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
11626        // on the sibling required-`u32`-composition axis — same "the
11627        // validate / shape-gate predicate must route through the
11628        // substrate-primitive typed dispatch" discipline extended onto
11629        // the outer top-level [`Caixa`] universal-axis `&[T]`-
11630        // composition surface.
11631        let c = caixa_with_autores(vec![""]);
11632        assert!(
11633            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
11634            "validate_autores must reject autores == vec![\"\"] with \
11635             AutorEmpty — the accessor and the validate gate must \
11636             route through the same substrate-primitive typed dispatch \
11637             on the :autores per-entry empty arm",
11638        );
11639        let c = caixa_with_autores(vec!["pleme-io"]);
11640        assert!(
11641            c.validate_autores().is_ok(),
11642            "validate_autores must accept autores == vec![\"pleme-io\"] \
11643             (the canonical single-maintainer shape every `feira init` \
11644             template scaffolds)",
11645        );
11646    }
11647
11648    #[test]
11649    fn autores_projects_slice_by_borrow() {
11650        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
11651        // borrow — the returned slice borrows the underlying
11652        // `Vec<String>` storage of the `:autores` slot and the
11653        // accessor must not clone the backing `Vec` on every call.
11654        // Peer of the per-`:membros`
11655        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
11656        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
11657        // (0dcc926) / per-`:placement`
11658        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
11659        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
11660        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
11661        // typed-slot `&[T]`-return axes, extended onto the outer top-
11662        // level [`Caixa`] universal-axis `&[String]` shape — the
11663        // accessor's returned slice must borrow from `&self` (the
11664        // returned reference's lifetime is tied to `&self`), and
11665        // calling the accessor twice on the same [`Caixa`] must yield
11666        // slices that are pointer-equal (the underlying byte-buffer is
11667        // the storage `Vec`'s allocation, not a fresh copy) as well as
11668        // value-equal (idempotent, no side effects on `&self`).
11669        //
11670        // Pins against a future silent detour that returned an owned
11671        // `Vec<String>` (which would type-check but silently clone on
11672        // every call, breaking the zero-cost projection every peer
11673        // sibling slice accessor carries), a `&Vec<String>` return
11674        // (which would leak the backing `Vec`'s grow/push/reserve
11675        // surface no downstream consumer reaches for), or a one-arm-
11676        // only accessor that returned a saturating value on some
11677        // sentinel input (breaking the pass-through invariant the
11678        // sibling slice accessors carry).
11679        for autores in [
11680            vec![],
11681            vec!["pleme-io"],
11682            vec!["alice", "bob"],
11683            vec!["pleme-io", "pleme-io"],
11684        ] {
11685            let c = caixa_with_autores(autores.clone());
11686            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
11687            let first = c.autores();
11688            let second = c.autores();
11689            assert_eq!(
11690                first, second,
11691                "Caixa::autores must be idempotent — two successive \
11692                 calls on the same &self must return the same \
11693                 &[String]",
11694            );
11695            assert_eq!(
11696                first.as_ptr(),
11697                second.as_ptr(),
11698                "Caixa::autores must borrow the underlying Vec<String> \
11699                 storage — two successive calls must return slices \
11700                 with the same backing pointer (a fresh Vec<String> \
11701                 clone would change the pointer on every call)",
11702            );
11703            assert_eq!(
11704                first,
11705                expected.as_slice(),
11706                "Caixa::autores must return :autores verbatim by \
11707                 borrow — got {first:?}, expected {expected:?}",
11708            );
11709        }
11710    }
11711
11712    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
11713
11714    #[test]
11715    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
11716        // The canonical per-`Caixa` `:etiquetas` universal-axis
11717        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
11718        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
11719        // as a `&[String]`, byte-equal to the raw
11720        // `self.etiquetas.as_slice()` access across every representative
11721        // value in the accept-set — `[]` (the "no tags declared" arm
11722        // every existing fixture without an `:etiquetas` line carries),
11723        // `[""]` (a past-the-guard sentinel that pins the accessor
11724        // doesn't perform a silent `[""] → []` collapse on the empty-
11725        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
11726        // but the accessor must ship the raw slot verbatim so a
11727        // validate-time gate regression surfaces at the caixa-helm emit
11728        // boundary rather than being silently absorbed into a keyword-
11729        // drop), `["demo"]` (the canonical single-tag form every
11730        // `feira init` template scaffolds), `["example", "aplicacao",
11731        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
11732        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
11733        // (a past-the-guard duplicate sentinel — validate rejects
11734        // through `EtiquetaDuplicate` but the accessor must ship the
11735        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
11736        // at chart-render time isn't silently promoted into the
11737        // accessor boundary and struct-literal
11738        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
11739        // fixtures continue to expose the duplicate at the accessor).
11740        //
11741        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
11742        // pin on the substrate primitive — folds on the "outer
11743        // [`Caixa`] `&[T]` slice" projection pattern
11744        // `autores_returns_autores_slice_verbatim_across_permutations`
11745        // (b5d813f) opened, sibling in shape and idiom. Pins against a
11746        // future silent detour that returned an owned `Vec<String>`
11747        // (which would type-check but silently clone on every accessor
11748        // call, breaking the zero-cost projection every peer sibling
11749        // slice accessor carries), a `[""] → []` collapse (which would
11750        // silently absorb the `EtiquetaEmpty` refusal case at the
11751        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
11752        // (which would silently absorb the `EtiquetaDuplicate` refusal
11753        // case at the accessor boundary — the caixa-helm chart-render
11754        // `BTreeSet::collect` dedup is downstream of the accessor and
11755        // must not be silently promoted into it).
11756        for etiquetas in [
11757            vec![],
11758            vec![""],
11759            vec!["demo"],
11760            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
11761            vec!["demo", "demo"],
11762        ] {
11763            let c = caixa_with_etiquetas(etiquetas.clone());
11764            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
11765            assert_eq!(
11766                c.etiquetas(),
11767                expected.as_slice(),
11768                "Caixa::etiquetas must return :etiquetas verbatim (got \
11769                 {:?}, expected {expected:?})",
11770                c.etiquetas(),
11771            );
11772            assert_eq!(
11773                c.etiquetas(),
11774                c.etiquetas.as_slice(),
11775                "Caixa::etiquetas must byte-equal the raw \
11776                 `self.etiquetas.as_slice()` field access across every \
11777                 value in the Vec<String> accept-set",
11778            );
11779        }
11780    }
11781
11782    #[test]
11783    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
11784        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
11785        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
11786        // `&self.etiquetas` field-borrow walk. Structurally: a
11787        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
11788        // `EtiquetaEmpty` refusal exactly, and a
11789        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
11790        // single-tag form) must pass validate. The pair jointly pins
11791        // the accessor + validate-gate composition: any future silent
11792        // detour that had the accessor return an empty slice on the
11793        // `[""]` arm (a
11794        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
11795        // silently absorb the `EtiquetaEmpty` refusal at the accessor
11796        // boundary and the validate gate would accept a struct-literal
11797        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
11798        // pin catches that at caixa-core build time.
11799        //
11800        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
11801        // through_accessor` (b5d813f) accessor-composition pin on the
11802        // sibling `&[T]`-composition axis — same "the validate / shape-
11803        // gate predicate must route through the substrate-primitive
11804        // typed dispatch" discipline extended onto the sibling outer
11805        // top-level [`Caixa`] `&[T]`-composition surface.
11806        let c = caixa_with_etiquetas(vec![""]);
11807        assert!(
11808            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
11809            "validate_etiquetas must reject etiquetas == vec![\"\"] \
11810             with EtiquetaEmpty — the accessor and the validate gate \
11811             must route through the same substrate-primitive typed \
11812             dispatch on the :etiquetas per-entry empty arm",
11813        );
11814        let c = caixa_with_etiquetas(vec!["demo"]);
11815        assert!(
11816            c.validate_etiquetas().is_ok(),
11817            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
11818             (the canonical single-tag shape every `feira init` \
11819             template scaffolds)",
11820        );
11821    }
11822
11823    #[test]
11824    fn etiquetas_projects_slice_by_borrow() {
11825        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
11826        // by borrow — the returned slice borrows the underlying
11827        // `Vec<String>` storage of the `:etiquetas` slot and the
11828        // accessor must not clone the backing `Vec` on every call.
11829        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
11830        // (b5d813f) by-borrow pin on the sibling outer top-level
11831        // [`Caixa`] `&[String]`-return axis — the accessor's returned
11832        // slice must borrow from `&self` (the returned reference's
11833        // lifetime is tied to `&self`), and calling the accessor twice
11834        // on the same [`Caixa`] must yield slices that are pointer-
11835        // equal (the underlying byte-buffer is the storage `Vec`'s
11836        // allocation, not a fresh copy) as well as value-equal
11837        // (idempotent, no side effects on `&self`).
11838        //
11839        // Pins against a future silent detour that returned an owned
11840        // `Vec<String>` (which would type-check but silently clone on
11841        // every call, breaking the zero-cost projection every peer
11842        // sibling slice accessor carries), a `&Vec<String>` return
11843        // (which would leak the backing `Vec`'s grow/push/reserve
11844        // surface no downstream consumer reaches for), or a one-arm-
11845        // only accessor that returned a saturating value on some
11846        // sentinel input (breaking the pass-through invariant the
11847        // sibling slice accessors carry).
11848        for etiquetas in [
11849            vec![],
11850            vec!["demo"],
11851            vec!["example", "aplicacao", "mesh"],
11852            vec!["demo", "demo"],
11853        ] {
11854            let c = caixa_with_etiquetas(etiquetas.clone());
11855            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
11856            let first = c.etiquetas();
11857            let second = c.etiquetas();
11858            assert_eq!(
11859                first, second,
11860                "Caixa::etiquetas must be idempotent — two successive \
11861                 calls on the same &self must return the same \
11862                 &[String]",
11863            );
11864            assert_eq!(
11865                first.as_ptr(),
11866                second.as_ptr(),
11867                "Caixa::etiquetas must borrow the underlying \
11868                 Vec<String> storage — two successive calls must \
11869                 return slices with the same backing pointer (a fresh \
11870                 Vec<String> clone would change the pointer on every \
11871                 call)",
11872            );
11873            assert_eq!(
11874                first,
11875                expected.as_slice(),
11876                "Caixa::etiquetas must return :etiquetas verbatim by \
11877                 borrow — got {first:?}, expected {expected:?}",
11878            );
11879        }
11880    }
11881
11882    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
11883
11884    #[test]
11885    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
11886        // The canonical per-`Caixa` `:bibliotecas` universal-axis
11887        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
11888        // must return the `:bibliotecas` typed [`Vec<String>`] list
11889        // verbatim as a `&[String]`, byte-equal to the raw
11890        // `self.bibliotecas.as_slice()` access across every
11891        // representative value in the accept-set — `[]` (the "no
11892        // libraries declared" arm every `:kind` other than `Biblioteca`
11893        // + every `Biblioteca` relying on the canonical
11894        // `lib/<nome>.lisp` implicit-default path carries; the
11895        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
11896        // fires exactly on this empty-slot + `Biblioteca`-kind
11897        // combination), `[""]` (a past-the-guard sentinel that pins
11898        // the accessor doesn't perform a silent `[""] → []` collapse
11899        // on the empty-entry arm — validate rejects `[""]` through
11900        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
11901        // must ship the raw slot verbatim so a validate-time gate
11902        // regression surfaces at the `feira build` phase-1 parse
11903        // boundary rather than being silently absorbed into a
11904        // library-drop), `["lib/demo.lisp"]` (the canonical single-
11905        // entry form `Caixa::template` scaffolds and every `feira init`
11906        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
11907        // (the canonical multi-library form the
11908        // `validate_code_paths_accepts_explicit_relative_paths_on_
11909        // every_slot` fixture emits), and `["lib/foo.lisp",
11910        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
11911        // validate rejects through `CodePathDuplicate { slot:
11912        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
11913        // but the accessor must ship the raw slot verbatim so the
11914        // `feira build` `for entry in caixa.bibliotecas()` parse walk
11915        // sees the duplicate at the accessor boundary and struct-
11916        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
11917        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
11918        // the duplicate at the accessor).
11919        //
11920        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
11921        // pin on the substrate primitive — folds on the "outer
11922        // [`Caixa`] `&[T]` slice" projection pattern
11923        // `autores_returns_autores_slice_verbatim_across_permutations`
11924        // (b5d813f) opened and
11925        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
11926        // (78c7d3c) folded on, sibling in shape and idiom. Pins
11927        // against a future silent detour that returned an owned
11928        // `Vec<String>` (which would type-check but silently clone on
11929        // every accessor call, breaking the zero-cost projection
11930        // every peer sibling slice accessor carries), a `[""] → []`
11931        // collapse (which would silently absorb the `CodePathEmpty`
11932        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
11933        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
11934        // would silently absorb the `CodePathDuplicate` refusal case
11935        // at the accessor boundary — the per-slot set-not-multiset
11936        // gate is downstream of the accessor and must not be silently
11937        // promoted into it).
11938        for bibliotecas in [
11939            vec![],
11940            vec![""],
11941            vec!["lib/demo.lisp"],
11942            vec!["lib/demo.lisp", "lib/helpers.lisp"],
11943            vec!["lib/foo.lisp", "lib/foo.lisp"],
11944        ] {
11945            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
11946            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
11947            assert_eq!(
11948                c.bibliotecas(),
11949                expected.as_slice(),
11950                "Caixa::bibliotecas must return :bibliotecas verbatim \
11951                 (got {:?}, expected {expected:?})",
11952                c.bibliotecas(),
11953            );
11954            assert_eq!(
11955                c.bibliotecas(),
11956                c.bibliotecas.as_slice(),
11957                "Caixa::bibliotecas must byte-equal the raw \
11958                 `self.bibliotecas.as_slice()` field access across \
11959                 every value in the Vec<String> accept-set",
11960            );
11961        }
11962    }
11963
11964    #[test]
11965    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
11966        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
11967        // empty-arm gate on the `:bibliotecas` slot must key off
11968        // [`Caixa::bibliotecas`], not a divergent raw
11969        // `&self.bibliotecas` field-borrow walk. Structurally: a
11970        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
11971        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
11972        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
11973        // into()], .. }` (the canonical single-library form
11974        // `Caixa::template` scaffolds) must pass validate. The pair
11975        // jointly pins the accessor + validate-gate composition: any
11976        // future silent detour that had the accessor return an empty
11977        // slice on the `[""]` arm (a `.iter().filter(|s|
11978        // !s.is_empty()).collect()` collapse) would silently absorb
11979        // the `CodePathEmpty` refusal at the accessor boundary and
11980        // the validate gate would accept a struct-literal
11981        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
11982        // composition pin catches that at caixa-core build time.
11983        //
11984        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
11985        // through_accessor` (b5d813f) and
11986        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
11987        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
11988        // composition axes — same "the validate / shape-gate
11989        // predicate must route through the substrate-primitive typed
11990        // dispatch" discipline extended onto the sibling outer top-
11991        // level [`Caixa`] `&[T]`-composition surface. Nominally the
11992        // in-tree `validate_code_paths` production body still keys
11993        // off the internal `[(":bibliotecas", &self.bibliotecas,
11994        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
11995        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
11996        // (the tuple's homogeneous slice-typed shape blocks a per-
11997        // element accessor swap in isolation — a future companion
11998        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
11999        // `&[T]` slice-accessor axis closes that tuple onto the
12000        // triple of typed dispatches as a unit); the composition pin
12001        // catches any future accessor-side silent filter drop against
12002        // that eventual tuple-closure regardless of whether the
12003        // `:bibliotecas` slot is threaded through the accessor or the
12004        // raw field access at the tuple's construction site.
12005        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
12006        assert!(
12007            matches!(
12008                c.validate_code_paths(),
12009                Err(ManifestError::CodePathEmpty {
12010                    slot: ":bibliotecas"
12011                })
12012            ),
12013            "validate_code_paths must reject bibliotecas == vec![\"\"] \
12014             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
12015             accessor and the validate gate must route through the \
12016             same substrate-primitive typed dispatch on the \
12017             :bibliotecas per-entry empty arm",
12018        );
12019        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
12020        assert!(
12021            c.validate_code_paths().is_ok(),
12022            "validate_code_paths must accept bibliotecas == \
12023             vec![\"lib/demo.lisp\"] (the canonical single-library \
12024             shape every `feira init` template scaffolds)",
12025        );
12026    }
12027
12028    #[test]
12029    fn bibliotecas_projects_slice_by_borrow() {
12030        // The by-borrow pin: [`Caixa::bibliotecas`] returns
12031        // `&[String]` by borrow — the returned slice borrows the
12032        // underlying `Vec<String>` storage of the `:bibliotecas` slot
12033        // and the accessor must not clone the backing `Vec` on every
12034        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12035        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
12036        // by-borrow pins on the sibling outer top-level [`Caixa`]
12037        // `&[String]`-return axes — the accessor's returned slice
12038        // must borrow from `&self` (the returned reference's lifetime
12039        // is tied to `&self`), and calling the accessor twice on the
12040        // same [`Caixa`] must yield slices that are pointer-equal
12041        // (the underlying byte-buffer is the storage `Vec`'s
12042        // allocation, not a fresh copy) as well as value-equal
12043        // (idempotent, no side effects on `&self`).
12044        //
12045        // Pins against a future silent detour that returned an owned
12046        // `Vec<String>` (which would type-check but silently clone on
12047        // every call, breaking the zero-cost projection every peer
12048        // sibling slice accessor carries), a `&Vec<String>` return
12049        // (which would leak the backing `Vec`'s grow/push/reserve
12050        // surface no downstream consumer reaches for), or a one-arm-
12051        // only accessor that returned a saturating value on some
12052        // sentinel input (breaking the pass-through invariant the
12053        // sibling slice accessors carry).
12054        for bibliotecas in [
12055            vec![],
12056            vec!["lib/demo.lisp"],
12057            vec!["lib/demo.lisp", "lib/helpers.lisp"],
12058            vec!["lib/foo.lisp", "lib/foo.lisp"],
12059        ] {
12060            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
12061            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
12062            let first = c.bibliotecas();
12063            let second = c.bibliotecas();
12064            assert_eq!(
12065                first, second,
12066                "Caixa::bibliotecas must be idempotent — two \
12067                 successive calls on the same &self must return the \
12068                 same &[String]",
12069            );
12070            assert_eq!(
12071                first.as_ptr(),
12072                second.as_ptr(),
12073                "Caixa::bibliotecas must borrow the underlying \
12074                 Vec<String> storage — two successive calls must \
12075                 return slices with the same backing pointer (a \
12076                 fresh Vec<String> clone would change the pointer on \
12077                 every call)",
12078            );
12079            assert_eq!(
12080                first,
12081                expected.as_slice(),
12082                "Caixa::bibliotecas must return :bibliotecas verbatim \
12083                 by borrow — got {first:?}, expected {expected:?}",
12084            );
12085        }
12086    }
12087
12088    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
12089
12090    #[test]
12091    fn exe_returns_exe_slice_verbatim_across_permutations() {
12092        // The canonical per-`Caixa` `:exe` universal-axis
12093        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
12094        // must return the `:exe` typed [`Vec<String>`] list verbatim as
12095        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
12096        // access across every representative value in the accept-set —
12097        // `[]` (the "no executable declared" arm every `:kind` other
12098        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
12099        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
12100        // + `Binario`-kind combination), `[""]` (a past-the-guard
12101        // sentinel that pins the accessor doesn't perform a silent
12102        // `[""] → []` collapse on the empty-entry arm — validate rejects
12103        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
12104        // accessor must ship the raw slot verbatim so a validate-time
12105        // gate regression surfaces at the layout / `feira nix` boundary
12106        // rather than being silently absorbed into an executable-drop),
12107        // `["exe/cli"]` (the canonical single-entry Binario form every
12108        // in-tree `caixa_with_code_paths` positive control uses),
12109        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
12110        // form the `validate_code_paths_accepts_explicit_relative_paths_
12111        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
12112        // (a past-the-guard duplicate sentinel — validate rejects
12113        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
12114        // set-not-multiset gate, but the accessor must ship the raw
12115        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
12116        // into(), "exe/cli".into()], .. }` fixtures continue to expose
12117        // the duplicate at the accessor).
12118        //
12119        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
12120        // pin on the substrate primitive — folds on the "outer
12121        // [`Caixa`] `&[T]` slice" projection pattern
12122        // `autores_returns_autores_slice_verbatim_across_permutations`
12123        // (b5d813f) opened,
12124        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12125        // (78c7d3c) folded on, and
12126        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12127        // (8a36c23) closed the universal-axis text-tag family of.
12128        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
12129        // the sibling `:servicos` future lift closes onto. Pins against
12130        // a future silent detour that returned an owned `Vec<String>`
12131        // (which would type-check but silently clone on every accessor
12132        // call, breaking the zero-cost projection every peer sibling
12133        // slice accessor carries), a `[""] → []` collapse (which would
12134        // silently absorb the `CodePathEmpty` refusal case at the
12135        // accessor boundary), or an `["exe/cli", "exe/cli"] →
12136        // ["exe/cli"]` dedup collapse (which would silently absorb the
12137        // `CodePathDuplicate` refusal case at the accessor boundary —
12138        // the per-slot set-not-multiset gate is downstream of the
12139        // accessor and must not be silently promoted into it).
12140        for exe in [
12141            vec![],
12142            vec![""],
12143            vec!["exe/cli"],
12144            vec!["exe/cli", "exe/serve"],
12145            vec!["exe/cli", "exe/cli"],
12146        ] {
12147            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
12148            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
12149            assert_eq!(
12150                c.exe(),
12151                expected.as_slice(),
12152                "Caixa::exe must return :exe verbatim (got {:?}, \
12153                 expected {expected:?})",
12154                c.exe(),
12155            );
12156            assert_eq!(
12157                c.exe(),
12158                c.exe.as_slice(),
12159                "Caixa::exe must byte-equal the raw \
12160                 `self.exe.as_slice()` field access across every value \
12161                 in the Vec<String> accept-set",
12162            );
12163        }
12164    }
12165
12166    #[test]
12167    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
12168        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12169        // empty-arm gate on the `:exe` slot must key off
12170        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
12171        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
12172        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
12173        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
12174        // (the canonical single-executable form every in-tree
12175        // `caixa_with_code_paths` positive control uses) must pass
12176        // validate. The pair jointly pins the accessor + validate-gate
12177        // composition: any future silent detour that had the accessor
12178        // return an empty slice on the `[""]` arm (a
12179        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
12180        // silently absorb the `CodePathEmpty` refusal at the accessor
12181        // boundary and the validate gate would accept a struct-literal
12182        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
12183        // catches that at caixa-core build time.
12184        //
12185        // Peer of the per-`Caixa`
12186        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12187        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
12188        // (b5d813f), and
12189        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12190        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12191        // composition axes — same "the validate / shape-gate predicate
12192        // must route through the substrate-primitive typed dispatch"
12193        // discipline extended onto the sibling outer top-level [`Caixa`]
12194        // `&[T]`-composition surface. Nominally the in-tree
12195        // `validate_code_paths` production body still keys off the
12196        // internal `[(":bibliotecas", &self.bibliotecas,
12197        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12198        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12199        // (the tuple's homogeneous slice-typed shape blocks a per-
12200        // element accessor swap in isolation — a future companion lift
12201        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
12202        // accessor axis closes that tuple onto the triple of typed
12203        // dispatches as a unit); the composition pin catches any future
12204        // accessor-side silent filter drop against that eventual tuple-
12205        // closure regardless of whether the `:exe` slot is threaded
12206        // through the accessor or the raw field access at the tuple's
12207        // construction site.
12208        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
12209        assert!(
12210            matches!(
12211                c.validate_code_paths(),
12212                Err(ManifestError::CodePathEmpty { slot: ":exe" })
12213            ),
12214            "validate_code_paths must reject exe == vec![\"\"] \
12215             with CodePathEmpty {{ slot: \":exe\" }} — the \
12216             accessor and the validate gate must route through the \
12217             same substrate-primitive typed dispatch on the \
12218             :exe per-entry empty arm",
12219        );
12220        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
12221        assert!(
12222            c.validate_code_paths().is_ok(),
12223            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
12224             (the canonical single-executable shape every in-tree \
12225             `caixa_with_code_paths` positive control uses)",
12226        );
12227    }
12228
12229    #[test]
12230    fn exe_projects_slice_by_borrow() {
12231        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
12232        // borrow — the returned slice borrows the underlying
12233        // `Vec<String>` storage of the `:exe` slot and the accessor
12234        // must not clone the backing `Vec` on every call. Peer of the
12235        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
12236        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
12237        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
12238        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
12239        // return axes — the accessor's returned slice must borrow from
12240        // `&self` (the returned reference's lifetime is tied to
12241        // `&self`), and calling the accessor twice on the same
12242        // [`Caixa`] must yield slices that are pointer-equal (the
12243        // underlying byte-buffer is the storage `Vec`'s allocation,
12244        // not a fresh copy) as well as value-equal (idempotent, no
12245        // side effects on `&self`).
12246        //
12247        // Pins against a future silent detour that returned an owned
12248        // `Vec<String>` (which would type-check but silently clone on
12249        // every call, breaking the zero-cost projection every peer
12250        // sibling slice accessor carries), a `&Vec<String>` return
12251        // (which would leak the backing `Vec`'s grow/push/reserve
12252        // surface no downstream consumer reaches for), or a one-arm-
12253        // only accessor that returned a saturating value on some
12254        // sentinel input (breaking the pass-through invariant the
12255        // sibling slice accessors carry).
12256        for exe in [
12257            vec![],
12258            vec!["exe/cli"],
12259            vec!["exe/cli", "exe/serve"],
12260            vec!["exe/cli", "exe/cli"],
12261        ] {
12262            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
12263            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
12264            let first = c.exe();
12265            let second = c.exe();
12266            assert_eq!(
12267                first, second,
12268                "Caixa::exe must be idempotent — two successive calls \
12269                 on the same &self must return the same &[String]",
12270            );
12271            assert_eq!(
12272                first.as_ptr(),
12273                second.as_ptr(),
12274                "Caixa::exe must borrow the underlying Vec<String> \
12275                 storage — two successive calls must return slices \
12276                 with the same backing pointer (a fresh Vec<String> \
12277                 clone would change the pointer on every call)",
12278            );
12279            assert_eq!(
12280                first,
12281                expected.as_slice(),
12282                "Caixa::exe must return :exe verbatim by borrow — \
12283                 got {first:?}, expected {expected:?}",
12284            );
12285        }
12286    }
12287
12288    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
12289
12290    #[test]
12291    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
12292        // The canonical per-`Caixa` `:servicos` universal-axis
12293        // ComputeUnit-CR-YAML-entry-path-list slice pin:
12294        // [`Caixa::servicos`] must return the `:servicos` typed
12295        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
12296        // the raw `self.servicos.as_slice()` access across every
12297        // representative value in the accept-set — `[]` (the "no
12298        // ComputeUnit-CR declared" arm every `:kind` other than
12299        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
12300        // `ServicoWithoutServicos` arm-gate fires exactly on this
12301        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
12302        // guard sentinel that pins the accessor doesn't perform a
12303        // silent `[""] → []` collapse on the empty-entry arm — validate
12304        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
12305        // but the accessor must ship the raw slot verbatim so a
12306        // validate-time gate regression surfaces at the layout /
12307        // per-Servico renderer boundary rather than being silently
12308        // absorbed into a component-drop),
12309        // `["servicos/demo.computeunit.yaml"]` (the canonical
12310        // singleton V0-shape every in-tree `caixa_with_code_paths`
12311        // positive control uses; the same shape
12312        // [`crate::require_single_servico`] admits),
12313        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
12314        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
12315        // singularity gate rejects through `ServicoCountMismatch
12316        // { count: 2 }` but the accessor must ship the raw slot
12317        // verbatim so struct-literal `Caixa { servicos: vec![...,
12318        // ...], .. }` fixtures continue to expose the count at the
12319        // accessor), and `["servicos/a.computeunit.yaml",
12320        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
12321        // sentinel — validate rejects through
12322        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
12323        // set-not-multiset gate, but the accessor must ship the raw
12324        // slot verbatim so struct-literal fixtures continue to expose
12325        // the duplicate at the accessor).
12326        //
12327        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
12328        // slice accessor pin on the substrate primitive — folds on the
12329        // "outer [`Caixa`] `&[T]` slice" projection pattern
12330        // `autores_returns_autores_slice_verbatim_across_permutations`
12331        // (b5d813f) opened,
12332        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12333        // (78c7d3c) folded on,
12334        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12335        // (8a36c23) closed the universal-axis text-tag family of, and
12336        // `exe_returns_exe_slice_verbatim_across_permutations`
12337        // (65d9527) opened the foreign-code-slot sub-family of. Closes
12338        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
12339        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
12340        // `:servicos`) now each carries a substrate-canonical slice
12341        // accessor. Pins against a future silent detour that returned
12342        // an owned `Vec<String>` (which would type-check but silently
12343        // clone on every accessor call, breaking the zero-cost
12344        // projection every peer sibling slice accessor carries), a
12345        // `[""] → []` collapse (which would silently absorb the
12346        // `CodePathEmpty` refusal case at the accessor boundary), an
12347        // `[a, a] → [a]` dedup collapse (which would silently absorb
12348        // the `CodePathDuplicate` refusal case at the accessor
12349        // boundary — the per-slot set-not-multiset gate is downstream
12350        // of the accessor and must not be silently promoted into it),
12351        // or a `[a, b] → [a]` singleton collapse (which would silently
12352        // absorb the V0 `ServicoCountMismatch` refusal case at the
12353        // accessor boundary — the V0 singularity gate is downstream of
12354        // the accessor and must not be silently promoted into it).
12355        for servicos in [
12356            vec![],
12357            vec![""],
12358            vec!["servicos/demo.computeunit.yaml"],
12359            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
12360            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
12361        ] {
12362            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
12363            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
12364            assert_eq!(
12365                c.servicos(),
12366                expected.as_slice(),
12367                "Caixa::servicos must return :servicos verbatim (got \
12368                 {:?}, expected {expected:?})",
12369                c.servicos(),
12370            );
12371            assert_eq!(
12372                c.servicos(),
12373                c.servicos.as_slice(),
12374                "Caixa::servicos must byte-equal the raw \
12375                 `self.servicos.as_slice()` field access across every \
12376                 value in the Vec<String> accept-set",
12377            );
12378        }
12379    }
12380
12381    #[test]
12382    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
12383        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12384        // empty-arm gate on the `:servicos` slot must key off
12385        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
12386        // field-borrow walk. Structurally: a `Caixa { servicos:
12387        // vec!["".into()], .. }` must surface the `CodePathEmpty
12388        // { slot: ":servicos" }` refusal exactly, and a `Caixa
12389        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
12390        // .. }` (the canonical singleton V0-shape every in-tree
12391        // `caixa_with_code_paths` positive control uses) must pass
12392        // validate. The pair jointly pins the accessor + validate-gate
12393        // composition: any future silent detour that had the accessor
12394        // return an empty slice on the `[""]` arm (a `.iter().filter
12395        // (|s| !s.is_empty()).collect()` collapse) would silently
12396        // absorb the `CodePathEmpty` refusal at the accessor boundary
12397        // and the validate gate would accept a struct-literal
12398        // `Caixa { servicos: vec!["".into()], .. }` — the composition
12399        // pin catches that at caixa-core build time.
12400        //
12401        // Peer of the per-`Caixa`
12402        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12403        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
12404        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
12405        // (b5d813f), and
12406        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12407        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12408        // composition axes — same "the validate / shape-gate predicate
12409        // must route through the substrate-primitive typed dispatch"
12410        // discipline extended onto the sibling outer top-level
12411        // [`Caixa`] `&[T]`-composition surface, closing the trio of
12412        // code-surface accessor-composition pins on the same axis.
12413        // Nominally the in-tree `validate_code_paths` production body
12414        // still keys off the internal
12415        // `[(":bibliotecas", &self.bibliotecas,
12416        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12417        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12418        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
12419        // per-element accessor swap in isolation — a future companion
12420        // lift promotes the tuple's element type to `&[String]` and
12421        // threads the triple of typed dispatches through as a unit);
12422        // the composition pin catches any future accessor-side silent
12423        // filter drop against that eventual tuple-closure regardless
12424        // of whether the `:servicos` slot is threaded through the
12425        // accessor or the raw field access at the tuple's construction
12426        // site.
12427        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
12428        assert!(
12429            matches!(
12430                c.validate_code_paths(),
12431                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
12432            ),
12433            "validate_code_paths must reject servicos == vec![\"\"] \
12434             with CodePathEmpty {{ slot: \":servicos\" }} — the \
12435             accessor and the validate gate must route through the \
12436             same substrate-primitive typed dispatch on the \
12437             :servicos per-entry empty arm",
12438        );
12439        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
12440        assert!(
12441            c.validate_code_paths().is_ok(),
12442            "validate_code_paths must accept servicos == \
12443             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
12444             singleton V0-shape every in-tree `caixa_with_code_paths` \
12445             positive control uses)",
12446        );
12447    }
12448
12449    #[test]
12450    fn servicos_projects_slice_by_borrow() {
12451        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
12452        // borrow — the returned slice borrows the underlying
12453        // `Vec<String>` storage of the `:servicos` slot and the
12454        // accessor must not clone the backing `Vec` on every call.
12455        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12456        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
12457        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
12458        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
12459        // the sibling outer top-level [`Caixa`] `&[String]`-return
12460        // axes — the accessor's returned slice must borrow from
12461        // `&self` (the returned reference's lifetime is tied to
12462        // `&self`), and calling the accessor twice on the same
12463        // [`Caixa`] must yield slices that are pointer-equal (the
12464        // underlying byte-buffer is the storage `Vec`'s allocation,
12465        // not a fresh copy) as well as value-equal (idempotent, no
12466        // side effects on `&self`).
12467        //
12468        // Pins against a future silent detour that returned an owned
12469        // `Vec<String>` (which would type-check but silently clone on
12470        // every call, breaking the zero-cost projection every peer
12471        // sibling slice accessor carries), a `&Vec<String>` return
12472        // (which would leak the backing `Vec`'s grow/push/reserve
12473        // surface no downstream consumer reaches for), or a one-arm-
12474        // only accessor that returned a saturating value on some
12475        // sentinel input (breaking the pass-through invariant the
12476        // sibling slice accessors carry).
12477        for servicos in [
12478            vec![],
12479            vec!["servicos/demo.computeunit.yaml"],
12480            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
12481            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
12482        ] {
12483            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
12484            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
12485            let first = c.servicos();
12486            let second = c.servicos();
12487            assert_eq!(
12488                first, second,
12489                "Caixa::servicos must be idempotent — two successive \
12490                 calls on the same &self must return the same &[String]",
12491            );
12492            assert_eq!(
12493                first.as_ptr(),
12494                second.as_ptr(),
12495                "Caixa::servicos must borrow the underlying \
12496                 Vec<String> storage — two successive calls must \
12497                 return slices with the same backing pointer (a fresh \
12498                 Vec<String> clone would change the pointer on every \
12499                 call)",
12500            );
12501            assert_eq!(
12502                first,
12503                expected.as_slice(),
12504                "Caixa::servicos must return :servicos verbatim by \
12505                 borrow — got {first:?}, expected {expected:?}",
12506            );
12507        }
12508    }
12509
12510    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
12511
12512    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
12513        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12514        c.deps = deps;
12515        c
12516    }
12517
12518    #[test]
12519    fn deps_returns_deps_slice_verbatim_across_permutations() {
12520        // The canonical per-`Caixa` `:deps` universal-axis runtime-
12521        // dependency-declaration-list slice pin: [`Caixa::deps`] must
12522        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
12523        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
12524        // access across every representative value in the accept-set —
12525        // `[]` (the "no runtime deps declared" arm every existing
12526        // fixture without a `:deps` line carries; the
12527        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
12528        // single-entry list (the shape most consumer caixas carry), a
12529        // canonical two-entry list (the multi-dep runtime closure), and
12530        // two past-the-guard sentinels — a `[""]`-`:nome` entry
12531        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
12532        // `NomeInvalid` but the accessor must ship the raw slot
12533        // verbatim) and a `[a, a]` duplicate (validate rejects through
12534        // `DuplicateNome { list: ":deps" }` but the accessor must ship
12535        // the raw slot verbatim so struct-literal fixtures continue to
12536        // expose the duplicate at the accessor).
12537        //
12538        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
12539        // pin on the substrate primitive — opens the outer-`Caixa`
12540        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
12541        // future lift closes on. Peer of the closed outer-`Caixa`
12542        // foreign-code-slot `&[String]` sub-family
12543        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12544        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
12545        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
12546        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
12547        // (`autores_returns_autores_slice_verbatim_across_permutations`
12548        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12549        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
12550        // projection pattern onto a novel element-type axis (`Dep`
12551        // composite vs the prior sibling family's `String` scalar).
12552        // Pins against a future silent detour that returned an owned
12553        // `Vec<Dep>` (which would type-check but silently clone on every
12554        // accessor call, breaking the zero-cost projection every peer
12555        // sibling slice accessor carries), a `[""] → []` collapse (which
12556        // would silently absorb the `NomeEmpty` refusal case at the
12557        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
12558        // would silently absorb the `DuplicateNome` refusal case at the
12559        // accessor boundary).
12560        for deps in [
12561            vec![],
12562            vec![Dep::simple("", "^0.1")],
12563            vec![Dep::simple("caixa-teia", "^0.1")],
12564            vec![
12565                Dep::simple("caixa-teia", "^0.1"),
12566                Dep::simple("caixa-core", "^0.1"),
12567            ],
12568            vec![
12569                Dep::simple("caixa-teia", "^0.1"),
12570                Dep::simple("caixa-teia", "^0.2"),
12571            ],
12572        ] {
12573            let c = caixa_with_deps(deps.clone());
12574            assert_eq!(
12575                c.deps(),
12576                deps.as_slice(),
12577                "Caixa::deps must return :deps verbatim (got {:?}, \
12578                 expected {deps:?})",
12579                c.deps(),
12580            );
12581            assert_eq!(
12582                c.deps(),
12583                c.deps.as_slice(),
12584                "Caixa::deps must element-equal the raw \
12585                 `self.deps.as_slice()` field access across every \
12586                 value in the Vec<Dep> accept-set",
12587            );
12588        }
12589    }
12590
12591    #[test]
12592    fn validate_deps_duplicate_arm_routes_through_accessor() {
12593        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
12594        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
12595        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
12596        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
12597        // "^0.2")], .. }` must surface the `DuplicateNome { list:
12598        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
12599        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
12600        // form) must pass validate. The pair jointly pins the accessor +
12601        // validate-gate composition: any future silent detour that had
12602        // the accessor return a dedupped slice on the `[a, a]` arm (a
12603        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
12604        // would silently absorb the `DuplicateNome` refusal at the
12605        // accessor boundary and the validate gate would accept a
12606        // struct-literal `Caixa` carrying the drift — the composition
12607        // pin catches that at caixa-core build time.
12608        //
12609        // Peer of the per-`Caixa`
12610        // `validate_autores_empty_entry_arm_routes_through_accessor`
12611        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12612        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12613        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
12614        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
12615        // (611f78b) accessor-composition pins on the sibling `&[T]`-
12616        // composition axes — same "the validate gate must route through
12617        // the substrate-primitive typed dispatch" discipline extended
12618        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
12619        // composition surface, opening the outer-`Caixa` dependency-slot
12620        // arm of the composition-pin family.
12621        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
12622        let err = c.validate_deps().unwrap_err();
12623        assert!(
12624            matches!(
12625                err,
12626                DepError::DuplicateNome { ref nome, list } if nome == "d"
12627                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
12628            ),
12629            "validate_deps must reject deps == \
12630             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
12631             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
12632             accessor and the validate gate must route through the \
12633             same substrate-primitive typed dispatch on the :deps \
12634             within-list duplicate arm (got {err:?})",
12635        );
12636        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
12637        assert!(
12638            c.validate_deps().is_ok(),
12639            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
12640             (the canonical single-entry form)",
12641        );
12642    }
12643
12644    #[test]
12645    fn deps_projects_slice_by_borrow() {
12646        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
12647        // — the returned slice borrows the underlying `Vec<Dep>` storage
12648        // of the `:deps` slot and the accessor must not clone the
12649        // backing `Vec` on every call. Peer of the per-`Caixa`
12650        // `autores_projects_slice_by_borrow` (b5d813f),
12651        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
12652        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
12653        // `exe_projects_slice_by_borrow` (65d9527), and
12654        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
12655        // on the sibling outer top-level [`Caixa`] `&[String]`-return
12656        // axes — the accessor's returned slice must borrow from `&self`
12657        // (the returned reference's lifetime is tied to `&self`), and
12658        // calling the accessor twice on the same [`Caixa`] must yield
12659        // slices that are pointer-equal (the underlying byte-buffer is
12660        // the storage `Vec`'s allocation, not a fresh copy) as well as
12661        // value-equal (idempotent, no side effects on `&self`).
12662        //
12663        // Pins against a future silent detour that returned an owned
12664        // `Vec<Dep>` (which would type-check but silently clone on
12665        // every call), a `&Vec<Dep>` return (which would leak the
12666        // backing `Vec`'s grow/push/reserve surface no downstream
12667        // consumer reaches for), or a one-arm-only accessor that
12668        // returned a saturating value on some sentinel input.
12669        for deps in [
12670            vec![],
12671            vec![Dep::simple("caixa-teia", "^0.1")],
12672            vec![
12673                Dep::simple("caixa-teia", "^0.1"),
12674                Dep::simple("caixa-core", "^0.1"),
12675            ],
12676        ] {
12677            let c = caixa_with_deps(deps.clone());
12678            let first = c.deps();
12679            let second = c.deps();
12680            assert_eq!(
12681                first, second,
12682                "Caixa::deps must be idempotent — two successive calls \
12683                 on the same &self must return the same &[Dep]",
12684            );
12685            assert_eq!(
12686                first.as_ptr(),
12687                second.as_ptr(),
12688                "Caixa::deps must borrow the underlying Vec<Dep> \
12689                 storage — two successive calls must return slices \
12690                 with the same backing pointer (a fresh Vec<Dep> clone \
12691                 would change the pointer on every call)",
12692            );
12693            assert_eq!(
12694                first,
12695                deps.as_slice(),
12696                "Caixa::deps must return :deps verbatim by borrow — \
12697                 got {first:?}, expected {deps:?}",
12698            );
12699        }
12700    }
12701
12702    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
12703
12704    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
12705        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12706        c.deps_dev = deps_dev;
12707        c
12708    }
12709
12710    #[test]
12711    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
12712        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
12713        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
12714        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
12715        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
12716        // access across every representative value in the accept-set —
12717        // `[]` (the "no dev deps declared" arm every existing fixture
12718        // without a `:deps-dev` line carries; the [`Caixa::template`]
12719        // scaffold emits `:deps-dev ()`), a canonical single-entry list
12720        // (the shape most consumer caixas carry — a `tatara-check` dev
12721        // pin), a canonical two-entry list (the multi-dev-dep closure),
12722        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
12723        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
12724        // `NomeInvalid` but the accessor must ship the raw slot
12725        // verbatim) and a `[a, a]` duplicate (validate rejects through
12726        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
12727        // ship the raw slot verbatim so struct-literal fixtures continue
12728        // to expose the duplicate at the accessor).
12729        //
12730        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
12731        // pin on the substrate primitive — closes the outer-`Caixa`
12732        // dependency-slot `&[Dep]` sub-family the sibling
12733        // `deps_returns_deps_slice_verbatim_across_permutations`
12734        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
12735        // slice" projection pattern onto the sibling dev-dep axis —
12736        // pins against a future silent detour that returned an owned
12737        // `Vec<Dep>` (which would type-check but silently clone on every
12738        // accessor call, breaking the zero-cost projection every peer
12739        // sibling slice accessor carries), a `[""] → []` collapse (which
12740        // would silently absorb the `NomeEmpty` refusal case at the
12741        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
12742        // would silently absorb the `DuplicateNome` refusal case at the
12743        // accessor boundary).
12744        for deps_dev in [
12745            vec![],
12746            vec![Dep::simple("", "^0.1")],
12747            vec![Dep::simple("tatara-check", "^0.1")],
12748            vec![
12749                Dep::simple("tatara-check", "^0.1"),
12750                Dep::simple("caixa-lint", "^0.1"),
12751            ],
12752            vec![
12753                Dep::simple("tatara-check", "^0.1"),
12754                Dep::simple("tatara-check", "^0.2"),
12755            ],
12756        ] {
12757            let c = caixa_with_deps_dev(deps_dev.clone());
12758            assert_eq!(
12759                c.deps_dev(),
12760                deps_dev.as_slice(),
12761                "Caixa::deps_dev must return :deps-dev verbatim (got \
12762                 {:?}, expected {deps_dev:?})",
12763                c.deps_dev(),
12764            );
12765            assert_eq!(
12766                c.deps_dev(),
12767                c.deps_dev.as_slice(),
12768                "Caixa::deps_dev must element-equal the raw \
12769                 `self.deps_dev.as_slice()` field access across every \
12770                 value in the Vec<Dep> accept-set",
12771            );
12772        }
12773    }
12774
12775    #[test]
12776    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
12777        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
12778        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
12779        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
12780        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
12781        // Dep::simple("d", "^0.2")], .. }` must surface the
12782        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
12783        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
12784        // canonical single-entry form) must pass validate. The pair
12785        // jointly pins the accessor + validate-gate composition: any
12786        // future silent detour that had the accessor return a dedupped
12787        // slice on the `[a, a]` arm (a
12788        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
12789        // would silently absorb the `DuplicateNome` refusal at the
12790        // accessor boundary and the validate gate would accept a
12791        // struct-literal `Caixa` carrying the drift — the composition
12792        // pin catches that at caixa-core build time.
12793        //
12794        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
12795        // (ad34b4e) on the sibling `:deps` axis — same "the validate
12796        // gate must route through the substrate-primitive typed
12797        // dispatch" discipline folded onto the sibling `:deps-dev`
12798        // axis, closing the two-list dep-graph composition-pin family.
12799        // The `:deps-dev` diagnostic must carry the
12800        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
12801        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
12802        // offending list unambiguously.
12803        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
12804        let err = c.validate_deps().unwrap_err();
12805        assert!(
12806            matches!(
12807                err,
12808                DepError::DuplicateNome { ref nome, list } if nome == "d"
12809                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
12810            ),
12811            "validate_deps must reject deps_dev == \
12812             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
12813             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
12814             accessor and the validate gate must route through the \
12815             same substrate-primitive typed dispatch on the :deps-dev \
12816             within-list duplicate arm (got {err:?})",
12817        );
12818        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
12819        assert!(
12820            c.validate_deps().is_ok(),
12821            "validate_deps must accept deps_dev == \
12822             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
12823        );
12824    }
12825
12826    #[test]
12827    fn deps_dev_projects_slice_by_borrow() {
12828        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
12829        // borrow — the returned slice borrows the underlying `Vec<Dep>`
12830        // storage of the `:deps-dev` slot and the accessor must not
12831        // clone the backing `Vec` on every call. Peer of
12832        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
12833        // `:deps` axis, and of the per-`Caixa`
12834        // `autores_projects_slice_by_borrow` (b5d813f),
12835        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
12836        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
12837        // `exe_projects_slice_by_borrow` (65d9527), and
12838        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
12839        // on the sibling outer top-level [`Caixa`] `&[String]`-return
12840        // axes — the accessor's returned slice must borrow from `&self`
12841        // (the returned reference's lifetime is tied to `&self`), and
12842        // calling the accessor twice on the same [`Caixa`] must yield
12843        // slices that are pointer-equal (the underlying byte-buffer is
12844        // the storage `Vec`'s allocation, not a fresh copy) as well as
12845        // value-equal (idempotent, no side effects on `&self`).
12846        //
12847        // Pins against a future silent detour that returned an owned
12848        // `Vec<Dep>` (which would type-check but silently clone on
12849        // every call), a `&Vec<Dep>` return (which would leak the
12850        // backing `Vec`'s grow/push/reserve surface no downstream
12851        // consumer reaches for), or a one-arm-only accessor that
12852        // returned a saturating value on some sentinel input.
12853        for deps_dev in [
12854            vec![],
12855            vec![Dep::simple("tatara-check", "^0.1")],
12856            vec![
12857                Dep::simple("tatara-check", "^0.1"),
12858                Dep::simple("caixa-lint", "^0.1"),
12859            ],
12860        ] {
12861            let c = caixa_with_deps_dev(deps_dev.clone());
12862            let first = c.deps_dev();
12863            let second = c.deps_dev();
12864            assert_eq!(
12865                first, second,
12866                "Caixa::deps_dev must be idempotent — two successive \
12867                 calls on the same &self must return the same &[Dep]",
12868            );
12869            assert_eq!(
12870                first.as_ptr(),
12871                second.as_ptr(),
12872                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
12873                 storage — two successive calls must return slices \
12874                 with the same backing pointer (a fresh Vec<Dep> clone \
12875                 would change the pointer on every call)",
12876            );
12877            assert_eq!(
12878                first,
12879                deps_dev.as_slice(),
12880                "Caixa::deps_dev must return :deps-dev verbatim by \
12881                 borrow — got {first:?}, expected {deps_dev:?}",
12882            );
12883        }
12884    }
12885
12886    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
12887
12888    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
12889        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12890        c.limits = limits;
12891        c
12892    }
12893
12894    #[test]
12895    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
12896        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
12897        // composite optional-composite-reference-shape pin:
12898        // [`Caixa::limits`] must return the `:limits` typed
12899        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
12900        // reference over the same backing storage the raw
12901        // `self.limits.as_ref()` field access borrows from, byte-equal
12902        // across every representative fixture in the accept-set — the
12903        // author-omitted `None` shape (the "engine-default applies"
12904        // partition every downstream Servico M2 overlay emitter treats
12905        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
12906        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
12907        // per-axis cap is `None`, so the peer M2 overlay emitter's
12908        // `.is_empty()`-gated projection still emits nothing but the
12909        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
12910        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
12911        // fixture (only `:memory` set — the canonical shape most
12912        // memory-heavy Servicos carry), and a fully-populated composite
12913        // (every per-axis cap set — the canonical shape a
12914        // sandboxed-by-default Servico carries).
12915        //
12916        // Pins against a future silent detour that returned a fresh-
12917        // cloned [`LimitsSpec`] copy (which would type-check via the
12918        // `Clone` impl but silently break every downstream caller that
12919        // relied on the reference sharing the composite's backing
12920        // identity), a reference to an operator-resolved overlay (the
12921        // future per-cluster `:limits-overrides` slot — its resolution
12922        // must land at exactly this accessor body, not silently divert
12923        // the raw slot away from a second consumer), a
12924        // `None` → `Some(LimitsSpec::default)` cluster-default
12925        // projection (which would collapse the load-bearing
12926        // "author-omitted `:limits` ⇒ engine-default applies" partition
12927        // the peer [`crate::render::servico_m2_overlay`] emitter and
12928        // the peer [`Caixa::declared_servico_slots`] enumerator both
12929        // read), or an axis-shuffled projection (a future detour that
12930        // swapped `memory` and `fuel` through the accessor would
12931        // silently split the paired [`crate::StandardLayout::verify`]
12932        // per-`:limits` shape gate's traversal input from the peer
12933        // `servico_m2_overlay` emitter's projection input).
12934        //
12935        // First outer top-level [`Caixa`] `Option<&Composite>`-return
12936        // composite-reference accessor pin on the substrate primitive
12937        // — opens the outer-`Caixa` `Option<&Composite>` composite-
12938        // reference projection pattern the sibling `:behavior`
12939        // [`crate::BehaviorSpec`] / `:politicas`
12940        // [`crate::aplicacao::MeshPolicy`] / `:placement`
12941        // [`crate::aplicacao::Placement`] / `:entrada`
12942        // [`crate::aplicacao::Entrada`] future outer-composite lifts
12943        // fold on. Peer of the closed M3 outer-composite family the
12944        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
12945        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
12946        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
12947        // reference accessor pins already carry on the outer
12948        // [`crate::AplicacaoSpec`] altitude — extends the outer-
12949        // accessor byte-equal-projection discipline onto the outer
12950        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
12951        use crate::LimitsSpec;
12952        use std::time::Duration;
12953        let fixtures: Vec<Option<LimitsSpec>> = vec![
12954            None,
12955            Some(LimitsSpec::default()),
12956            Some(LimitsSpec {
12957                memory: Some(64 * 1024 * 1024),
12958                ..Default::default()
12959            }),
12960            Some(LimitsSpec {
12961                memory: Some(64 * 1024 * 1024),
12962                fuel: Some(1_000_000),
12963                wall_clock: Some(Duration::from_secs(30)),
12964                cpu: Some(500),
12965            }),
12966        ];
12967        for limits in fixtures {
12968            let c = caixa_with_limits(limits.clone());
12969            assert_eq!(
12970                c.limits(),
12971                limits.as_ref(),
12972                "Caixa::limits must return :limits verbatim (got {:?}, \
12973                 expected {:?})",
12974                c.limits(),
12975                limits.as_ref(),
12976            );
12977            match (c.limits(), c.limits.as_ref()) {
12978                (Some(a), Some(b)) => assert!(
12979                    std::ptr::eq(a, b),
12980                    "Caixa::limits accessor and self.limits.as_ref() \
12981                     field access must borrow the same backing storage \
12982                     — the accessor is the substrate-primitive typed \
12983                     dispatch every downstream Servico-M2-overlay \
12984                     composite consumer must route through, and a \
12985                     reference-identity split would silently break \
12986                     every consumer that relied on the borrow sharing \
12987                     the composite's storage",
12988                ),
12989                (None, None) => {}
12990                _ => panic!(
12991                    "Caixa::limits presence bit must byte-equal \
12992                     self.limits.is_some() — a presence-bit drift would \
12993                     silently split the paired StandardLayout::verify \
12994                     per-`:limits` shape gate's traversal head from \
12995                     the peer render::servico_m2_overlay M2 overlay \
12996                     emitter's traversal head from the peer \
12997                     Caixa::declared_servico_slots M2 declared-slot \
12998                     enumerator's presence probe",
12999                ),
13000            }
13001            assert_eq!(
13002                c.limits().is_some(),
13003                c.limits.is_some(),
13004                "Caixa::limits().is_some() must byte-equal \
13005                 self.limits.is_some() — a presence-bit drift would \
13006                 silently split every downstream Option<&LimitsSpec> \
13007                 consumer's partition on the engine-default arm",
13008            );
13009        }
13010    }
13011
13012    #[test]
13013    fn declared_servico_slots_limits_arm_routes_through_accessor() {
13014        // Composition pin: [`Caixa::declared_servico_slots`]'s
13015        // `:limits` presence-probe arm must key off [`Caixa::limits`],
13016        // not the raw `self.limits.is_some()` field-probe. Structurally:
13017        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
13018        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
13019        // (the presence bit is `Some`, so the M2 kind-coherence gate
13020        // must surface the slot as "declared" even when every per-axis
13021        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
13022        // push the label (the "author omitted the slot entirely"
13023        // partition). The pair jointly pins the accessor + declared-
13024        // slot enumerator composition: any future silent detour that
13025        // had the accessor collapse `Some(LimitsSpec::default())` to
13026        // `None` (a `.filter(|l| !l.is_empty())` projection) would
13027        // silently absorb the "declared but empty" arm at the
13028        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
13029        // kind-coherence gate would silently accept a
13030        // struct-literal `Caixa` carrying the drift.
13031        //
13032        // Peer of the sibling per-`Caixa`
13033        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
13034        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
13035        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
13036        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
13037        // enumerator gate must route through the substrate-primitive
13038        // typed dispatch" discipline extended onto the outer top-level
13039        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
13040        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
13041        // composition-pin family.
13042        use crate::LimitsSpec;
13043        let c = caixa_with_limits(Some(LimitsSpec::default()));
13044        let slots = c.declared_servico_slots();
13045        assert!(
13046            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
13047            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
13048             when `:limits` is Some (even for LimitsSpec::default()) \
13049             — the accessor and the enumerator gate must route through \
13050             the same substrate-primitive typed dispatch on the outer \
13051             :limits presence bit (got slots={slots:?})",
13052        );
13053        let c = caixa_with_limits(None);
13054        let slots = c.declared_servico_slots();
13055        assert!(
13056            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
13057            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
13058             when `:limits` is None — the author-omitted arm must \
13059             route through the accessor's None-return unchanged (got \
13060             slots={slots:?})",
13061        );
13062    }
13063
13064    #[test]
13065    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
13066        // Composition pin: [`crate::render::servico_m2_overlay`]'s
13067        // per-`:limits` M2 overlay emit arm must key off
13068        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
13069        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
13070        // Some(64 MiB), .. default }), .. }` must surface the
13071        // `M2_KEY_LIMITS` key with the per-axis
13072        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
13073        // limits: Some(LimitsSpec::default()), .. }` must omit the
13074        // key entirely (the `.is_empty()`-gated inner arm elides an
13075        // empty composite even when the outer presence bit is `Some`),
13076        // and a `Caixa { limits: None, .. }` must also omit the key
13077        // (the "author omitted the slot entirely" partition). The
13078        // three-fixture family jointly pins the accessor + M2 overlay
13079        // emitter composition: any future silent detour that had the
13080        // accessor return a fresh-cloned copy on the `Some` arm (a
13081        // `LimitsSpec::clone()` projection) would silently break the
13082        // reference-identity pin the peer per-axis
13083        // `serde_yaml::to_value(limits)` projection reads from.
13084        use crate::LimitsSpec;
13085        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
13086        let c = caixa_with_limits(Some(LimitsSpec {
13087            memory: Some(64 * 1024 * 1024),
13088            ..Default::default()
13089        }));
13090        let overlay = servico_m2_overlay(&c).unwrap();
13091        assert!(
13092            overlay.contains_key(M2_KEY_LIMITS),
13093            "servico_m2_overlay must surface M2_KEY_LIMITS when \
13094             `:limits` carries a non-empty composite — the accessor \
13095             and the M2 overlay emitter must route through the same \
13096             substrate-primitive typed dispatch on the outer :limits \
13097             composite (got overlay={overlay:?})",
13098        );
13099        let c = caixa_with_limits(Some(LimitsSpec::default()));
13100        let overlay = servico_m2_overlay(&c).unwrap();
13101        assert!(
13102            !overlay.contains_key(M2_KEY_LIMITS),
13103            "servico_m2_overlay must omit M2_KEY_LIMITS when \
13104             `:limits` is Some(LimitsSpec::default()) — the empty \
13105             composite's `.is_empty()`-gated inner arm must elide \
13106             the key regardless of the outer presence bit (got \
13107             overlay={overlay:?})",
13108        );
13109        let c = caixa_with_limits(None);
13110        let overlay = servico_m2_overlay(&c).unwrap();
13111        assert!(
13112            !overlay.contains_key(M2_KEY_LIMITS),
13113            "servico_m2_overlay must omit M2_KEY_LIMITS when \
13114             `:limits` is None — the author-omitted arm must route \
13115             through the accessor's None-return unchanged (got \
13116             overlay={overlay:?})",
13117        );
13118    }
13119
13120    #[test]
13121    fn limits_projects_option_ref_by_borrow() {
13122        // The by-borrow pin: [`Caixa::limits`] returns
13123        // `Option<&LimitsSpec>` by borrow — the returned reference
13124        // borrows the underlying `Option<LimitsSpec>` storage of the
13125        // `:limits` slot and the accessor must not clone the backing
13126        // composite on every call. Peer of the sibling
13127        // `deps_projects_slice_by_borrow` (ad34b4e) /
13128        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
13129        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
13130        // extended here to the outer [`Caixa`] `Option<&Composite>`-
13131        // return axis: the accessor's returned reference must borrow
13132        // from `&self` (the returned reference's lifetime is tied to
13133        // `&self`), and calling the accessor twice on the same
13134        // [`Caixa`] must yield references that are pointer-equal (the
13135        // underlying byte-buffer is the storage `LimitsSpec`'s
13136        // allocation, not a fresh copy) as well as value-equal
13137        // (idempotent, no side effects on `&self`).
13138        //
13139        // Pins against a future silent detour that returned an owned
13140        // `LimitsSpec` (which would type-check via the `Clone` impl
13141        // but silently clone on every call), a `&LimitsSpec` panic-
13142        // return on the `None` arm (which would collapse the load-
13143        // bearing `Option` presence-bit into a runtime panic), or a
13144        // one-arm-only accessor that returned a saturating composite
13145        // on some sentinel input.
13146        use crate::LimitsSpec;
13147        use std::time::Duration;
13148        for limits in [
13149            Some(LimitsSpec::default()),
13150            Some(LimitsSpec {
13151                memory: Some(64 * 1024 * 1024),
13152                fuel: Some(1_000_000),
13153                wall_clock: Some(Duration::from_secs(30)),
13154                cpu: Some(500),
13155            }),
13156        ] {
13157            let c = caixa_with_limits(limits.clone());
13158            let first = c.limits().unwrap();
13159            let second = c.limits().unwrap();
13160            assert_eq!(
13161                first, second,
13162                "Caixa::limits must be idempotent — two successive \
13163                 calls on the same &self must return the same \
13164                 &LimitsSpec",
13165            );
13166            assert!(
13167                std::ptr::eq(first, second),
13168                "Caixa::limits must borrow the underlying \
13169                 Option<LimitsSpec> storage — two successive calls \
13170                 must return references with the same backing pointer \
13171                 (a fresh LimitsSpec clone would change the pointer \
13172                 on every call)",
13173            );
13174            assert_eq!(
13175                Some(first),
13176                limits.as_ref(),
13177                "Caixa::limits must return :limits verbatim by borrow \
13178                 — got {first:?}, expected {:?}",
13179                limits.as_ref(),
13180            );
13181        }
13182        let c = caixa_with_limits(None);
13183        assert!(
13184            c.limits().is_none(),
13185            "Caixa::limits must return None when :limits is absent — \
13186             the author-omitted arm must project through the \
13187             accessor's Option::None unchanged",
13188        );
13189    }
13190
13191    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
13192
13193    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
13194        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13195        c.behavior = behavior;
13196        c
13197    }
13198
13199    #[test]
13200    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
13201        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
13202        // composite optional-composite-reference-shape pin:
13203        // [`Caixa::behavior`] must return the `:behavior` typed
13204        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
13205        // reference over the same backing storage the raw
13206        // `self.behavior.as_ref()` field access borrows from, byte-equal
13207        // across every representative fixture in the accept-set — the
13208        // author-omitted `None` shape (the "runtime-default applies"
13209        // partition every downstream Servico M2 overlay emitter treats
13210        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
13211        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
13212        // every per-callback path is `None`, so the peer M2 overlay
13213        // emitter's `.is_empty()`-gated projection still emits nothing
13214        // but the outer presence-bit is `Some`, so
13215        // [`Caixa::declared_servico_slots`] still pushes the
13216        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
13217        // (only `:on-state-change` set — the canonical shape a caixa
13218        // that only wires the hot-upgrade migration path carries), and
13219        // a fully-populated composite (every per-callback path set —
13220        // the canonical shape a fully-instrumented gen_server-shaped
13221        // Servico carries).
13222        //
13223        // Peer of the sibling
13224        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13225        // (b2bd9d7) opening fixture-family + reference-identity +
13226        // presence-bit tetrad pin on the outer top-level [`Caixa`]
13227        // `Option<&Composite>`-return sub-family — extended here to the
13228        // second axis of that sub-family so both of the currently-lifted
13229        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
13230        // `:behavior`) carry the same "byte-equal, borrow-shared,
13231        // presence-bit-preserved" outer-accessor discipline.
13232        //
13233        // Pins against a future silent detour that returned a fresh-
13234        // cloned [`crate::BehaviorSpec`] copy (which would type-check
13235        // via the `Clone` impl but silently break every downstream
13236        // caller that relied on the reference sharing the composite's
13237        // backing identity), a reference to an operator-resolved
13238        // overlay (a future per-cluster `:behavior-overrides` slot —
13239        // its resolution must land at exactly this accessor body, not
13240        // silently divert the raw slot away from a second consumer), a
13241        // `None` → `Some(BehaviorSpec::default)` cluster-default
13242        // projection (which would collapse the load-bearing
13243        // "author-omitted `:behavior` ⇒ runtime-default applies"
13244        // partition the peer [`crate::render::servico_m2_overlay`]
13245        // emitter, the peer [`Caixa::declared_servico_slots`]
13246        // enumerator, and the cross-slot
13247        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
13248        // gate all read), or a callback-shuffled projection (a future
13249        // detour that swapped `on_init` and `on_terminate` through the
13250        // accessor would silently split the paired
13251        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
13252        // traversal input from the peer `servico_m2_overlay` emitter's
13253        // projection input from the cross-slot `:state-change`
13254        // composition gate's traversal input).
13255        use crate::BehaviorSpec;
13256        use std::path::PathBuf;
13257        let fixtures: Vec<Option<BehaviorSpec>> = vec![
13258            None,
13259            Some(BehaviorSpec::default()),
13260            Some(BehaviorSpec {
13261                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13262                ..Default::default()
13263            }),
13264            Some(BehaviorSpec {
13265                on_init: Some(PathBuf::from("lib/init.lisp")),
13266                on_call: Some(PathBuf::from("lib/handlers.lisp")),
13267                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
13268                on_info: Some(PathBuf::from("lib/handlers.lisp")),
13269                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13270                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
13271            }),
13272        ];
13273        for behavior in fixtures {
13274            let c = caixa_with_behavior(behavior.clone());
13275            assert_eq!(
13276                c.behavior(),
13277                behavior.as_ref(),
13278                "Caixa::behavior must return :behavior verbatim (got \
13279                 {:?}, expected {:?})",
13280                c.behavior(),
13281                behavior.as_ref(),
13282            );
13283            match (c.behavior(), c.behavior.as_ref()) {
13284                (Some(a), Some(b)) => assert!(
13285                    std::ptr::eq(a, b),
13286                    "Caixa::behavior accessor and self.behavior.as_ref() \
13287                     field access must borrow the same backing storage \
13288                     — the accessor is the substrate-primitive typed \
13289                     dispatch every downstream Servico-M2-overlay \
13290                     composite consumer must route through, and a \
13291                     reference-identity split would silently break \
13292                     every consumer that relied on the borrow sharing \
13293                     the composite's storage",
13294                ),
13295                (None, None) => {}
13296                _ => panic!(
13297                    "Caixa::behavior presence bit must byte-equal \
13298                     self.behavior.is_some() — a presence-bit drift \
13299                     would silently split the paired \
13300                     StandardLayout::verify per-`:behavior` shape \
13301                     gate's traversal head from the peer \
13302                     render::servico_m2_overlay M2 overlay emitter's \
13303                     traversal head from the cross-slot \
13304                     validate_upgrade_from_against_behavior \
13305                     composition gate's traversal head from the peer \
13306                     Caixa::declared_servico_slots M2 declared-slot \
13307                     enumerator's presence probe",
13308                ),
13309            }
13310            assert_eq!(
13311                c.behavior().is_some(),
13312                c.behavior.is_some(),
13313                "Caixa::behavior().is_some() must byte-equal \
13314                 self.behavior.is_some() — a presence-bit drift would \
13315                 silently split every downstream Option<&BehaviorSpec> \
13316                 consumer's partition on the runtime-default arm",
13317            );
13318        }
13319    }
13320
13321    #[test]
13322    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
13323        // Composition pin: [`Caixa::declared_servico_slots`]'s
13324        // `:behavior` presence-probe arm must key off
13325        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
13326        // field-probe. Structurally: a `Caixa { behavior:
13327        // Some(BehaviorSpec::default()), .. }` must still push
13328        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
13329        // presence bit is `Some`, so the M2 kind-coherence gate must
13330        // surface the slot as "declared" even when every per-callback
13331        // path is unset), and a `Caixa { behavior: None, .. }` must
13332        // NOT push the label (the "author omitted the slot entirely"
13333        // partition). The pair jointly pins the accessor + declared-
13334        // slot enumerator composition: any future silent detour that
13335        // had the accessor collapse `Some(BehaviorSpec::default())`
13336        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
13337        // silently absorb the "declared but empty" arm at the
13338        // accessor boundary and the
13339        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
13340        // kind-coherence gate would silently accept a struct-literal
13341        // `Caixa` carrying the drift.
13342        //
13343        // Peer of the sibling
13344        // `declared_servico_slots_limits_arm_routes_through_accessor`
13345        // (b2bd9d7) composition pin on the sibling `:limits` outer-
13346        // `Option<&LimitsSpec>` arm of the same
13347        // [`Caixa::declared_servico_slots`] M2 declared-slot
13348        // enumerator's traversal — same "the enumerator gate must
13349        // route through the substrate-primitive typed dispatch"
13350        // discipline extended onto the outer top-level [`Caixa`]
13351        // `Option<&BehaviorSpec>`-composition surface.
13352        use crate::BehaviorSpec;
13353        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
13354        let slots = c.declared_servico_slots();
13355        assert!(
13356            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
13357            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
13358             when `:behavior` is Some (even for BehaviorSpec::default()) \
13359             — the accessor and the enumerator gate must route through \
13360             the same substrate-primitive typed dispatch on the outer \
13361             :behavior presence bit (got slots={slots:?})",
13362        );
13363        let c = caixa_with_behavior(None);
13364        let slots = c.declared_servico_slots();
13365        assert!(
13366            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
13367            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
13368             when `:behavior` is None — the author-omitted arm must \
13369             route through the accessor's None-return unchanged (got \
13370             slots={slots:?})",
13371        );
13372    }
13373
13374    #[test]
13375    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
13376        // Composition pin: [`crate::render::servico_m2_overlay`]'s
13377        // per-`:behavior` M2 overlay emit arm must key off
13378        // [`Caixa::behavior`], not the raw `&caixa.behavior`
13379        // field-borrow. Structurally: a `Caixa { behavior:
13380        // Some(BehaviorSpec { on_state_change: Some(...), .. default
13381        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
13382        // per-callback `onStateChange` sub-mapping in the overlay, a
13383        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
13384        // must omit the key entirely (the `.is_empty()`-gated inner
13385        // arm elides an empty composite even when the outer presence
13386        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
13387        // also omit the key (the "author omitted the slot entirely"
13388        // partition). The three-fixture family jointly pins the
13389        // accessor + M2 overlay emitter composition: any future
13390        // silent detour that had the accessor return a fresh-cloned
13391        // copy on the `Some` arm (a `BehaviorSpec::clone()`
13392        // projection) would silently break the reference-identity
13393        // pin the peer per-callback `serde_yaml::to_value(behavior)`
13394        // projection reads from.
13395        //
13396        // Peer of the sibling
13397        // `servico_m2_overlay_limits_arm_routes_through_accessor`
13398        // (b2bd9d7) composition pin on the sibling `:limits` outer-
13399        // `Option<&LimitsSpec>` arm of the same
13400        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
13401        // traversal — same "the emitter must route through the
13402        // substrate-primitive typed dispatch on the outer composite"
13403        // discipline extended onto the outer top-level [`Caixa`]
13404        // `Option<&BehaviorSpec>`-composition surface.
13405        use crate::BehaviorSpec;
13406        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
13407        use std::path::PathBuf;
13408        let c = caixa_with_behavior(Some(BehaviorSpec {
13409            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13410            ..Default::default()
13411        }));
13412        let overlay = servico_m2_overlay(&c).unwrap();
13413        assert!(
13414            overlay.contains_key(M2_KEY_BEHAVIOR),
13415            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
13416             `:behavior` carries a non-empty composite — the accessor \
13417             and the M2 overlay emitter must route through the same \
13418             substrate-primitive typed dispatch on the outer :behavior \
13419             composite (got overlay={overlay:?})",
13420        );
13421        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
13422        let overlay = servico_m2_overlay(&c).unwrap();
13423        assert!(
13424            !overlay.contains_key(M2_KEY_BEHAVIOR),
13425            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
13426             `:behavior` is Some(BehaviorSpec::default()) — the empty \
13427             composite's `.is_empty()`-gated inner arm must elide the \
13428             key regardless of the outer presence bit (got \
13429             overlay={overlay:?})",
13430        );
13431        let c = caixa_with_behavior(None);
13432        let overlay = servico_m2_overlay(&c).unwrap();
13433        assert!(
13434            !overlay.contains_key(M2_KEY_BEHAVIOR),
13435            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
13436             `:behavior` is None — the author-omitted arm must route \
13437             through the accessor's None-return unchanged (got \
13438             overlay={overlay:?})",
13439        );
13440    }
13441
13442    #[test]
13443    fn behavior_projects_option_ref_by_borrow() {
13444        // The by-borrow pin: [`Caixa::behavior`] returns
13445        // `Option<&BehaviorSpec>` by borrow — the returned reference
13446        // borrows the underlying `Option<BehaviorSpec>` storage of the
13447        // `:behavior` slot and the accessor must not clone the backing
13448        // composite on every call. Peer of the sibling
13449        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
13450        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
13451        // return sub-family — extended here to the second axis of the
13452        // same sub-family: the accessor's returned reference must
13453        // borrow from `&self` (the returned reference's lifetime is
13454        // tied to `&self`), and calling the accessor twice on the same
13455        // [`Caixa`] must yield references that are pointer-equal (the
13456        // underlying byte-buffer is the storage `BehaviorSpec`'s
13457        // allocation, not a fresh copy) as well as value-equal
13458        // (idempotent, no side effects on `&self`).
13459        //
13460        // Pins against a future silent detour that returned an owned
13461        // `BehaviorSpec` (which would type-check via the `Clone` impl
13462        // but silently clone on every call), a `&BehaviorSpec` panic-
13463        // return on the `None` arm (which would collapse the load-
13464        // bearing `Option` presence-bit into a runtime panic), or a
13465        // one-arm-only accessor that returned a saturating composite
13466        // on some sentinel input.
13467        use crate::BehaviorSpec;
13468        use std::path::PathBuf;
13469        for behavior in [
13470            Some(BehaviorSpec::default()),
13471            Some(BehaviorSpec {
13472                on_init: Some(PathBuf::from("lib/init.lisp")),
13473                on_call: Some(PathBuf::from("lib/handlers.lisp")),
13474                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
13475                on_info: Some(PathBuf::from("lib/handlers.lisp")),
13476                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13477                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
13478            }),
13479        ] {
13480            let c = caixa_with_behavior(behavior.clone());
13481            let first = c.behavior().unwrap();
13482            let second = c.behavior().unwrap();
13483            assert_eq!(
13484                first, second,
13485                "Caixa::behavior must be idempotent — two successive \
13486                 calls on the same &self must return the same \
13487                 &BehaviorSpec",
13488            );
13489            assert!(
13490                std::ptr::eq(first, second),
13491                "Caixa::behavior must borrow the underlying \
13492                 Option<BehaviorSpec> storage — two successive calls \
13493                 must return references with the same backing pointer \
13494                 (a fresh BehaviorSpec clone would change the pointer \
13495                 on every call)",
13496            );
13497            assert_eq!(
13498                Some(first),
13499                behavior.as_ref(),
13500                "Caixa::behavior must return :behavior verbatim by \
13501                 borrow — got {first:?}, expected {:?}",
13502                behavior.as_ref(),
13503            );
13504        }
13505        let c = caixa_with_behavior(None);
13506        assert!(
13507            c.behavior().is_none(),
13508            "Caixa::behavior must return None when :behavior is absent \
13509             — the author-omitted arm must project through the \
13510             accessor's Option::None unchanged",
13511        );
13512    }
13513
13514    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
13515
13516    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
13517        use crate::aplicacao::{Membro, WitContract};
13518        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13519        c.kind = CaixaKind::Aplicacao;
13520        c.membros = vec![Membro {
13521            caixa: "a".into(),
13522            versao: "^0.1".into(),
13523        }];
13524        c.contratos = vec![WitContract {
13525            de: "a".into(),
13526            para: "a".into(),
13527            wit: "wasi:http/proxy".into(),
13528            endpoint: Some("/x".into()),
13529            subject: None,
13530            slot: None,
13531        }];
13532        c.politicas = politicas;
13533        c
13534    }
13535
13536    #[test]
13537    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
13538        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
13539        // composite optional-composite-reference-shape pin:
13540        // [`Caixa::politicas`] must return the `:politicas` typed
13541        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
13542        // reference over the same backing storage the raw
13543        // `self.politicas.as_ref()` field access borrows from,
13544        // byte-equal across every representative fixture in the
13545        // accept-set — the author-omitted `None` shape (the "cluster-
13546        // default applies" partition every downstream mesh-artifact
13547        // emitter treats as "emit no `:politicas` overlay"), the
13548        // empty-composite `Some(MeshPolicy { .. default })` shape
13549        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
13550        // per-axis mesh-policy scalar is `None`, so the peer inner
13551        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
13552        // caixa-mesh overlay elides every per-axis emit but the outer
13553        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
13554        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
13555        // single-axis fixture (only `:timeout` set — the canonical
13556        // shape a latency-sensitive Aplicacao carries), and a
13557        // fully-populated composite (every per-axis mesh-policy
13558        // scalar set — the canonical shape a fully-governed
13559        // Aplicacao carries).
13560        //
13561        // Pins against a future silent detour that returned a fresh-
13562        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
13563        // type-check via the `Clone` impl but silently break every
13564        // downstream caller that relied on the reference sharing the
13565        // composite's backing identity), a reference to an operator-
13566        // resolved overlay (the future per-cluster
13567        // `:politicas-overrides` slot — its resolution must land at
13568        // exactly this accessor body, not silently divert the raw
13569        // slot away from the peer [`Caixa::declared_mesh_slots`]
13570        // enumerator's presence probe), a
13571        // `None` → `Some(MeshPolicy::default)` cluster-default
13572        // projection (which would collapse the load-bearing
13573        // "author-omitted `:politicas` ⇒ cluster-default applies"
13574        // partition the peer [`Caixa::declared_mesh_slots`]
13575        // enumerator and the peer [`Caixa::aplicacao_view`]
13576        // Aplicacao-composition seed both read), or an axis-shuffled
13577        // projection (a future detour that swapped `timeout` and
13578        // `retries` through the accessor would silently split the
13579        // paired [`Caixa::aplicacao_view`] seed's fold input from the
13580        // sibling M3 mesh-artifact emitter's projection input).
13581        //
13582        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
13583        // composite-reference accessor pin on the substrate primitive
13584        // — peer of the sibling
13585        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13586        // (b2bd9d7) and
13587        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
13588        // (35d8b52) opening tetrad pins on the outer top-level
13589        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
13590        // here to the first of the three M3 mesh-slot axes so the
13591        // opening third of the outer `Option<&Composite>` sub-family
13592        // carries the same "byte-equal, borrow-shared, presence-bit-
13593        // preserved" outer-accessor discipline.
13594        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
13595        use std::time::Duration;
13596        let fixtures: Vec<Option<MeshPolicy>> = vec![
13597            None,
13598            Some(MeshPolicy::default()),
13599            Some(MeshPolicy {
13600                timeout: Some(Duration::from_secs(30)),
13601                ..Default::default()
13602            }),
13603            Some(MeshPolicy {
13604                timeout: Some(Duration::from_secs(30)),
13605                retries: Some(3),
13606                circuit_breaker: Some(CircuitBreaker {
13607                    max_failures: 5,
13608                    window: Duration::from_secs(60),
13609                }),
13610                mtls_required: Some(true),
13611                rate_limit: Some(RateLimit {
13612                    rate: 100,
13613                    window: Duration::from_secs(1),
13614                }),
13615            }),
13616        ];
13617        for politicas in fixtures {
13618            let c = caixa_aplicacao_with_politicas(politicas.clone());
13619            assert_eq!(
13620                c.politicas(),
13621                politicas.as_ref(),
13622                "Caixa::politicas must return :politicas verbatim (got \
13623                 {:?}, expected {:?})",
13624                c.politicas(),
13625                politicas.as_ref(),
13626            );
13627            match (c.politicas(), c.politicas.as_ref()) {
13628                (Some(a), Some(b)) => assert!(
13629                    std::ptr::eq(a, b),
13630                    "Caixa::politicas accessor and self.politicas.as_ref() \
13631                     field access must borrow the same backing storage \
13632                     — the accessor is the substrate-primitive typed \
13633                     dispatch every downstream Aplicacao-mesh-overlay \
13634                     composite consumer must route through, and a \
13635                     reference-identity split would silently break \
13636                     every consumer that relied on the borrow sharing \
13637                     the composite's storage",
13638                ),
13639                (None, None) => {}
13640                _ => panic!(
13641                    "Caixa::politicas presence bit must byte-equal \
13642                     self.politicas.is_some() — a presence-bit drift \
13643                     would silently split the paired \
13644                     Caixa::aplicacao_view Aplicacao-composition seed's \
13645                     traversal head from the peer \
13646                     Caixa::declared_mesh_slots M3 declared-slot \
13647                     enumerator's presence probe",
13648                ),
13649            }
13650            assert_eq!(
13651                c.politicas().is_some(),
13652                c.politicas.is_some(),
13653                "Caixa::politicas().is_some() must byte-equal \
13654                 self.politicas.is_some() — a presence-bit drift would \
13655                 silently split every downstream Option<&MeshPolicy> \
13656                 consumer's partition on the cluster-default arm",
13657            );
13658        }
13659    }
13660
13661    #[test]
13662    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
13663        // Composition pin: [`Caixa::declared_mesh_slots`]'s
13664        // `:politicas` presence-probe arm must key off
13665        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
13666        // field-probe. Structurally: a `Caixa { politicas:
13667        // Some(MeshPolicy::default()), .. }` must still push
13668        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
13669        // presence bit is `Some`, so the M3 kind-coherence gate must
13670        // surface the slot as "declared" even when every per-axis
13671        // scalar is unset), and a `Caixa { politicas: None, .. }` must
13672        // NOT push the label (the "author omitted the slot entirely"
13673        // partition). The pair jointly pins the accessor + declared-
13674        // slot enumerator composition: any future silent detour that
13675        // had the accessor collapse `Some(MeshPolicy::default())` to
13676        // `None` (a `.filter(|p| !p.is_empty())` projection) would
13677        // silently absorb the "declared but empty" arm at the
13678        // accessor boundary and the
13679        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
13680        // coherence gate would silently accept a struct-literal
13681        // `Caixa` carrying the drift.
13682        //
13683        // Peer of the sibling
13684        // `declared_servico_slots_limits_arm_routes_through_accessor`
13685        // (b2bd9d7) and
13686        // `declared_servico_slots_behavior_arm_routes_through_accessor`
13687        // (35d8b52) composition pins on the sibling `:limits` /
13688        // `:behavior` outer-`Option<&Composite>` arms of the peer
13689        // [`Caixa::declared_servico_slots`] M2 declared-slot
13690        // enumerator's traversal — same "the enumerator gate must
13691        // route through the substrate-primitive typed dispatch"
13692        // discipline extended onto the outer top-level [`Caixa`] M3
13693        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
13694        // enumerator carries the same routing invariant as its M2
13695        // sibling.
13696        use crate::aplicacao::MeshPolicy;
13697        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
13698        let slots = c.declared_mesh_slots();
13699        assert!(
13700            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
13701            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
13702             when `:politicas` is Some (even for MeshPolicy::default()) \
13703             — the accessor and the enumerator gate must route through \
13704             the same substrate-primitive typed dispatch on the outer \
13705             :politicas presence bit (got slots={slots:?})",
13706        );
13707        let c = caixa_aplicacao_with_politicas(None);
13708        let slots = c.declared_mesh_slots();
13709        assert!(
13710            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
13711            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
13712             when `:politicas` is None — the author-omitted arm must \
13713             route through the accessor's None-return unchanged (got \
13714             slots={slots:?})",
13715        );
13716    }
13717
13718    #[test]
13719    fn aplicacao_view_politicas_arm_folds_through_accessor() {
13720        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
13721        // Aplicacao-composition seed must fold through
13722        // [`Caixa::politicas`], not the raw
13723        // `self.politicas.clone().unwrap_or_default()` field-borrow.
13724        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
13725        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
13726        // must surface a projected [`crate::AplicacaoSpec`] whose
13727        // `politicas().timeout()` field byte-equals the outer
13728        // composite's `timeout` scalar (the fold must project the
13729        // authored composite verbatim), a `Caixa { politicas:
13730        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
13731        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
13732        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
13733        // fold's empty-composite arm collapses to the same default the
13734        // author-omitted arm does), and a `Caixa { politicas: None,
13735        // kind: Aplicacao, .. }` must surface an
13736        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
13737        // [`crate::aplicacao::MeshPolicy::default`] (the "author
13738        // omitted the slot entirely" arm folds through the
13739        // `unwrap_or_default` onto the cluster-default). The triad
13740        // jointly pins the accessor + Aplicacao-composition seed
13741        // composition: any future silent detour that had the accessor
13742        // divert the raw slot away from the seed's fold (an operator-
13743        // resolved overlay's default-fold arm silently differing from
13744        // the raw slot's default-fold arm) would silently split the
13745        // build-time mesh-artifact emission gate from the caixa-mesh
13746        // renderer's Aplicacao-view input at the composition boundary.
13747        use crate::aplicacao::MeshPolicy;
13748        use std::time::Duration;
13749        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
13750            timeout: Some(Duration::from_secs(30)),
13751            ..Default::default()
13752        }));
13753        let view = c.aplicacao_view().unwrap();
13754        assert_eq!(
13755            view.politicas().timeout(),
13756            Some(Duration::from_secs(30)),
13757            "Caixa::aplicacao_view must fold the authored :politicas \
13758             :timeout scalar through the accessor verbatim onto the \
13759             projected AplicacaoSpec — a future silent detour at the \
13760             seed's fold arm would surface here as a projected-scalar \
13761             drift (got {:?})",
13762            view.politicas().timeout(),
13763        );
13764        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
13765        let view = c.aplicacao_view().unwrap();
13766        assert_eq!(
13767            view.politicas(),
13768            &MeshPolicy::default(),
13769            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
13770             through the accessor onto MeshPolicy::default — the empty- \
13771             composite arm collapses to the same default the author- \
13772             omitted arm does (got {:?})",
13773            view.politicas(),
13774        );
13775        let c = caixa_aplicacao_with_politicas(None);
13776        let view = c.aplicacao_view().unwrap();
13777        assert_eq!(
13778            view.politicas(),
13779            &MeshPolicy::default(),
13780            "Caixa::aplicacao_view must fold None through the accessor's \
13781             unwrap_or_default onto MeshPolicy::default — the author- \
13782             omitted arm must route through the accessor's None-return \
13783             unchanged (got {:?})",
13784            view.politicas(),
13785        );
13786    }
13787
13788    #[test]
13789    fn politicas_projects_option_ref_by_borrow() {
13790        // The by-borrow pin: [`Caixa::politicas`] returns
13791        // `Option<&MeshPolicy>` by borrow — the returned reference
13792        // borrows the underlying `Option<MeshPolicy>` storage of the
13793        // `:politicas` slot and the accessor must not clone the
13794        // backing composite on every call. Peer of the sibling
13795        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
13796        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
13797        // pins on the outer top-level [`Caixa`]
13798        // `Option<&Composite>`-return sub-family — extended here to
13799        // the third axis of the same sub-family: the accessor's
13800        // returned reference must borrow from `&self` (the returned
13801        // reference's lifetime is tied to `&self`), and calling the
13802        // accessor twice on the same [`Caixa`] must yield references
13803        // that are pointer-equal (the underlying byte-buffer is the
13804        // storage `MeshPolicy`'s allocation, not a fresh copy) as
13805        // well as value-equal (idempotent, no side effects on
13806        // `&self`).
13807        //
13808        // Pins against a future silent detour that returned an owned
13809        // `MeshPolicy` (which would type-check via the `Clone` impl
13810        // but silently clone on every call), a `&MeshPolicy` panic-
13811        // return on the `None` arm (which would collapse the load-
13812        // bearing `Option` presence-bit into a runtime panic), or a
13813        // one-arm-only accessor that returned a saturating composite
13814        // on some sentinel input.
13815        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
13816        use std::time::Duration;
13817        for politicas in [
13818            Some(MeshPolicy::default()),
13819            Some(MeshPolicy {
13820                timeout: Some(Duration::from_secs(30)),
13821                retries: Some(3),
13822                circuit_breaker: Some(CircuitBreaker {
13823                    max_failures: 5,
13824                    window: Duration::from_secs(60),
13825                }),
13826                mtls_required: Some(true),
13827                rate_limit: Some(RateLimit {
13828                    rate: 100,
13829                    window: Duration::from_secs(1),
13830                }),
13831            }),
13832        ] {
13833            let c = caixa_aplicacao_with_politicas(politicas.clone());
13834            let first = c.politicas().unwrap();
13835            let second = c.politicas().unwrap();
13836            assert_eq!(
13837                first, second,
13838                "Caixa::politicas must be idempotent — two successive \
13839                 calls on the same &self must return the same \
13840                 &MeshPolicy",
13841            );
13842            assert!(
13843                std::ptr::eq(first, second),
13844                "Caixa::politicas must borrow the underlying \
13845                 Option<MeshPolicy> storage — two successive calls \
13846                 must return references with the same backing pointer \
13847                 (a fresh MeshPolicy clone would change the pointer on \
13848                 every call)",
13849            );
13850            assert_eq!(
13851                Some(first),
13852                politicas.as_ref(),
13853                "Caixa::politicas must return :politicas verbatim by \
13854                 borrow — got {first:?}, expected {:?}",
13855                politicas.as_ref(),
13856            );
13857        }
13858        let c = caixa_aplicacao_with_politicas(None);
13859        assert!(
13860            c.politicas().is_none(),
13861            "Caixa::politicas must return None when :politicas is \
13862             absent — the author-omitted arm must project through the \
13863             accessor's Option::None unchanged",
13864        );
13865    }
13866
13867    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
13868
13869    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
13870        use crate::aplicacao::{Membro, WitContract};
13871        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13872        c.kind = CaixaKind::Aplicacao;
13873        c.membros = vec![Membro {
13874            caixa: "a".into(),
13875            versao: "^0.1".into(),
13876        }];
13877        c.contratos = vec![WitContract {
13878            de: "a".into(),
13879            para: "a".into(),
13880            wit: "wasi:http/proxy".into(),
13881            endpoint: Some("/x".into()),
13882            subject: None,
13883            slot: None,
13884        }];
13885        c.placement = placement;
13886        c
13887    }
13888
13889    #[test]
13890    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
13891        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
13892        // composite optional-composite-reference-shape pin:
13893        // [`Caixa::placement`] must return the `:placement` typed
13894        // `Option<Placement>` verbatim as an `Option<&Placement>`
13895        // reference over the same backing storage the raw
13896        // `self.placement.as_ref()` field access borrows from,
13897        // byte-equal across every representative fixture in the
13898        // accept-set — the author-omitted `None` shape (the
13899        // "cluster-default applies" partition every downstream mesh-
13900        // artifact emitter treats as "emit no `:placement` overlay"),
13901        // the empty-composite `Some(Placement { .. default })` shape
13902        // (`estrategia: SingleNode`, empty clusters, no shard-key /
13903        // affinity — the outer presence-bit is `Some` so
13904        // [`Caixa::declared_mesh_slots`] still pushes the
13905        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
13906        // `Replicated`-on-two-clusters fixture (the canonical shape a
13907        // stateless HTTP Aplicacao carries), and a fully-populated
13908        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
13909        // shape a stateful Akka-style cluster-sharding Aplicacao
13910        // carries).
13911        //
13912        // Pins against a future silent detour that returned a fresh-
13913        // cloned [`crate::aplicacao::Placement`] copy (which would
13914        // type-check via the `Clone` impl but silently break every
13915        // downstream caller that relied on the reference sharing the
13916        // composite's backing identity), a reference to an operator-
13917        // resolved overlay (the future per-cluster
13918        // `:placement-overrides` slot — its resolution must land at
13919        // exactly this accessor body, not silently divert the raw
13920        // slot away from the peer [`Caixa::declared_mesh_slots`]
13921        // enumerator's presence probe), a `None` →
13922        // `Some(Placement::default)` cluster-default projection (which
13923        // would collapse the load-bearing "author-omitted `:placement`
13924        // ⇒ cluster-default applies" partition the peer
13925        // [`Caixa::declared_mesh_slots`] enumerator and the peer
13926        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
13927        // read), or an axis-shuffled projection (a future detour that
13928        // swapped `clusters` and `affinity` through the accessor would
13929        // silently split the paired [`Caixa::aplicacao_view`] seed's
13930        // fold input from the sibling M3 mesh-artifact emitter's
13931        // projection input).
13932        //
13933        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
13934        // composite-reference accessor pin on the substrate primitive
13935        // — peer of the sibling
13936        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13937        // (b2bd9d7),
13938        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
13939        // (35d8b52), and
13940        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
13941        // (5d23d29) opening triad pins on the outer top-level
13942        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
13943        // here to the second of the three M3 mesh-slot axes so the
13944        // opening four-fifths of the outer `Option<&Composite>` sub-
13945        // family carries the same "byte-equal, borrow-shared,
13946        // presence-bit-preserved" outer-accessor discipline.
13947        use crate::aplicacao::{Placement, PlacementStrategy};
13948        let fixtures: Vec<Option<Placement>> = vec![
13949            None,
13950            Some(Placement::default()),
13951            Some(Placement {
13952                estrategia: PlacementStrategy::Replicated,
13953                clusters: vec!["rio".into(), "sao-paulo".into()],
13954                affinity: None,
13955                shard_key: None,
13956            }),
13957            Some(Placement {
13958                estrategia: PlacementStrategy::Sharded,
13959                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
13960                affinity: Some("data-locality".into()),
13961                shard_key: Some("$tenantId".into()),
13962            }),
13963        ];
13964        for placement in fixtures {
13965            let c = caixa_aplicacao_with_placement(placement.clone());
13966            assert_eq!(
13967                c.placement(),
13968                placement.as_ref(),
13969                "Caixa::placement must return :placement verbatim (got \
13970                 {:?}, expected {:?})",
13971                c.placement(),
13972                placement.as_ref(),
13973            );
13974            match (c.placement(), c.placement.as_ref()) {
13975                (Some(a), Some(b)) => assert!(
13976                    std::ptr::eq(a, b),
13977                    "Caixa::placement accessor and self.placement.as_ref() \
13978                     field access must borrow the same backing storage \
13979                     — the accessor is the substrate-primitive typed \
13980                     dispatch every downstream Aplicacao-distribution- \
13981                     overlay composite consumer must route through, and \
13982                     a reference-identity split would silently break \
13983                     every consumer that relied on the borrow sharing \
13984                     the composite's storage",
13985                ),
13986                (None, None) => {}
13987                _ => panic!(
13988                    "Caixa::placement presence bit must byte-equal \
13989                     self.placement.is_some() — a presence-bit drift \
13990                     would silently split the paired \
13991                     Caixa::aplicacao_view Aplicacao-composition seed's \
13992                     traversal head from the peer \
13993                     Caixa::declared_mesh_slots M3 declared-slot \
13994                     enumerator's presence probe",
13995                ),
13996            }
13997            assert_eq!(
13998                c.placement().is_some(),
13999                c.placement.is_some(),
14000                "Caixa::placement().is_some() must byte-equal \
14001                 self.placement.is_some() — a presence-bit drift would \
14002                 silently split every downstream Option<&Placement> \
14003                 consumer's partition on the cluster-default arm",
14004            );
14005        }
14006    }
14007
14008    #[test]
14009    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
14010        // Composition pin: [`Caixa::declared_mesh_slots`]'s
14011        // `:placement` presence-probe arm must key off
14012        // [`Caixa::placement`], not the raw `self.placement.is_some()`
14013        // field-probe. Structurally: a `Caixa { placement:
14014        // Some(Placement::default()), .. }` must still push
14015        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
14016        // presence bit is `Some`, so the M3 kind-coherence gate must
14017        // surface the slot as "declared" even when every per-axis
14018        // scalar defers to the cluster-default arm), and a `Caixa {
14019        // placement: None, .. }` must NOT push the label (the "author
14020        // omitted the slot entirely" partition). The pair jointly pins
14021        // the accessor + declared-slot enumerator composition: any
14022        // future silent detour that had the accessor collapse
14023        // `Some(Placement::default())` to `None` (a `.filter(|p|
14024        // p.clusters().is_empty().not())` projection) would silently
14025        // absorb the "declared but empty" arm at the accessor boundary
14026        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
14027        // kind-coherence gate would silently accept a struct-literal
14028        // `Caixa` carrying the drift.
14029        //
14030        // Peer of the sibling
14031        // `declared_servico_slots_limits_arm_routes_through_accessor`
14032        // (b2bd9d7),
14033        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14034        // (35d8b52), and
14035        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
14036        // (5d23d29) composition pins on the sibling `:limits` /
14037        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
14038        // — same "the enumerator gate must route through the
14039        // substrate-primitive typed dispatch" discipline extended onto
14040        // the second of the three M3 mesh-slot axes so the
14041        // [`Caixa::declared_mesh_slots`] enumerator carries the same
14042        // routing invariant on the `:placement` arm as the peer
14043        // `:politicas` arm.
14044        use crate::aplicacao::Placement;
14045        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
14046        let slots = c.declared_mesh_slots();
14047        assert!(
14048            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
14049            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
14050             when `:placement` is Some (even for Placement::default()) \
14051             — the accessor and the enumerator gate must route through \
14052             the same substrate-primitive typed dispatch on the outer \
14053             :placement presence bit (got slots={slots:?})",
14054        );
14055        let c = caixa_aplicacao_with_placement(None);
14056        let slots = c.declared_mesh_slots();
14057        assert!(
14058            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
14059            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
14060             when `:placement` is None — the author-omitted arm must \
14061             route through the accessor's None-return unchanged (got \
14062             slots={slots:?})",
14063        );
14064    }
14065
14066    #[test]
14067    fn aplicacao_view_placement_arm_folds_through_accessor() {
14068        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
14069        // Aplicacao-composition seed must fold through
14070        // [`Caixa::placement`], not the raw
14071        // `self.placement.clone().unwrap_or_default()` field-borrow.
14072        // Structurally: a `Caixa { placement: Some(Placement {
14073        // estrategia: Replicated, clusters: ["rio"], .. default }),
14074        // kind: Aplicacao, .. }` must surface a projected
14075        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
14076        // `placement().clusters()` byte-equal the outer composite's
14077        // authored values (the fold must project the authored
14078        // composite verbatim), a `Caixa { placement:
14079        // Some(Placement::default()), kind: Aplicacao, .. }` must
14080        // surface an [`crate::AplicacaoSpec`] whose `placement()`
14081        // byte-equals [`crate::aplicacao::Placement::default`] (the
14082        // fold's empty-composite arm collapses to the same default
14083        // the author-omitted arm does), and a `Caixa { placement:
14084        // None, kind: Aplicacao, .. }` must surface an
14085        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
14086        // [`crate::aplicacao::Placement::default`] (the "author
14087        // omitted the slot entirely" arm folds through the
14088        // `unwrap_or_default` onto the cluster-default). The triad
14089        // jointly pins the accessor + Aplicacao-composition seed
14090        // composition: any future silent detour that had the accessor
14091        // divert the raw slot away from the seed's fold (an operator-
14092        // resolved overlay's default-fold arm silently differing from
14093        // the raw slot's default-fold arm) would silently split the
14094        // build-time distribution-artifact emission gate from the
14095        // caixa-mesh renderer's Aplicacao-view input at the
14096        // composition boundary.
14097        use crate::aplicacao::{Placement, PlacementStrategy};
14098        let c = caixa_aplicacao_with_placement(Some(Placement {
14099            estrategia: PlacementStrategy::Replicated,
14100            clusters: vec!["rio".into()],
14101            affinity: None,
14102            shard_key: None,
14103        }));
14104        let view = c.aplicacao_view().unwrap();
14105        assert_eq!(
14106            view.placement().estrategia(),
14107            PlacementStrategy::Replicated,
14108            "Caixa::aplicacao_view must fold the authored :placement \
14109             :estrategia scalar through the accessor verbatim onto the \
14110             projected AplicacaoSpec — a future silent detour at the \
14111             seed's fold arm would surface here as a projected-scalar \
14112             drift (got {:?})",
14113            view.placement().estrategia(),
14114        );
14115        assert_eq!(
14116            view.placement().clusters(),
14117            &["rio"],
14118            "Caixa::aplicacao_view must fold the authored :placement \
14119             :clusters list through the accessor verbatim onto the \
14120             projected AplicacaoSpec — a future silent detour at the \
14121             seed's fold arm would surface here as a projected-list \
14122             drift (got {:?})",
14123            view.placement().clusters(),
14124        );
14125        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
14126        let view = c.aplicacao_view().unwrap();
14127        assert_eq!(
14128            view.placement(),
14129            &Placement::default(),
14130            "Caixa::aplicacao_view must fold Some(Placement::default()) \
14131             through the accessor onto Placement::default — the empty- \
14132             composite arm collapses to the same default the author- \
14133             omitted arm does (got {:?})",
14134            view.placement(),
14135        );
14136        let c = caixa_aplicacao_with_placement(None);
14137        let view = c.aplicacao_view().unwrap();
14138        assert_eq!(
14139            view.placement(),
14140            &Placement::default(),
14141            "Caixa::aplicacao_view must fold None through the accessor's \
14142             unwrap_or_default onto Placement::default — the author- \
14143             omitted arm must route through the accessor's None-return \
14144             unchanged (got {:?})",
14145            view.placement(),
14146        );
14147    }
14148
14149    #[test]
14150    fn placement_projects_option_ref_by_borrow() {
14151        // The by-borrow pin: [`Caixa::placement`] returns
14152        // `Option<&Placement>` by borrow — the returned reference
14153        // borrows the underlying `Option<Placement>` storage of the
14154        // `:placement` slot and the accessor must not clone the
14155        // backing composite on every call. Peer of the sibling
14156        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
14157        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
14158        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
14159        // pins on the outer top-level [`Caixa`]
14160        // `Option<&Composite>`-return sub-family — extended here to
14161        // the fourth axis of the same sub-family: the accessor's
14162        // returned reference must borrow from `&self` (the returned
14163        // reference's lifetime is tied to `&self`), and calling the
14164        // accessor twice on the same [`Caixa`] must yield references
14165        // that are pointer-equal (the underlying byte-buffer is the
14166        // storage `Placement`'s allocation, not a fresh copy) as well
14167        // as value-equal (idempotent, no side effects on `&self`).
14168        //
14169        // Pins against a future silent detour that returned an owned
14170        // `Placement` (which would type-check via the `Clone` impl
14171        // but silently clone on every call), a `&Placement` panic-
14172        // return on the `None` arm (which would collapse the load-
14173        // bearing `Option` presence-bit into a runtime panic), or a
14174        // one-arm-only accessor that returned a saturating composite
14175        // on some sentinel input.
14176        use crate::aplicacao::{Placement, PlacementStrategy};
14177        for placement in [
14178            Some(Placement::default()),
14179            Some(Placement {
14180                estrategia: PlacementStrategy::Sharded,
14181                clusters: vec!["rio".into(), "sao-paulo".into()],
14182                affinity: Some("data-locality".into()),
14183                shard_key: Some("$tenantId".into()),
14184            }),
14185        ] {
14186            let c = caixa_aplicacao_with_placement(placement.clone());
14187            let first = c.placement().unwrap();
14188            let second = c.placement().unwrap();
14189            assert_eq!(
14190                first, second,
14191                "Caixa::placement must be idempotent — two successive \
14192                 calls on the same &self must return the same \
14193                 &Placement",
14194            );
14195            assert!(
14196                std::ptr::eq(first, second),
14197                "Caixa::placement must borrow the underlying \
14198                 Option<Placement> storage — two successive calls \
14199                 must return references with the same backing pointer \
14200                 (a fresh Placement clone would change the pointer on \
14201                 every call)",
14202            );
14203            assert_eq!(
14204                Some(first),
14205                placement.as_ref(),
14206                "Caixa::placement must return :placement verbatim by \
14207                 borrow — got {first:?}, expected {:?}",
14208                placement.as_ref(),
14209            );
14210        }
14211        let c = caixa_aplicacao_with_placement(None);
14212        assert!(
14213            c.placement().is_none(),
14214            "Caixa::placement must return None when :placement is \
14215             absent — the author-omitted arm must project through the \
14216             accessor's Option::None unchanged",
14217        );
14218    }
14219
14220    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
14221
14222    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
14223        use crate::aplicacao::{Membro, WitContract};
14224        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14225        c.kind = CaixaKind::Aplicacao;
14226        c.membros = vec![Membro {
14227            caixa: "a".into(),
14228            versao: "^0.1".into(),
14229        }];
14230        c.contratos = vec![WitContract {
14231            de: "a".into(),
14232            para: "a".into(),
14233            wit: "wasi:http/proxy".into(),
14234            endpoint: Some("/x".into()),
14235            subject: None,
14236            slot: None,
14237        }];
14238        c.entrada = entrada;
14239        c
14240    }
14241
14242    #[test]
14243    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
14244        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
14245        // composite optional-composite-reference-shape pin:
14246        // [`Caixa::entrada`] must return the `:entrada` typed
14247        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
14248        // reference over the same backing storage the raw
14249        // `self.entrada.as_ref()` field access borrows from,
14250        // byte-equal across every representative fixture in the
14251        // accept-set — the author-omitted `None` shape (the
14252        // "cluster-internal Aplicacao" partition every downstream
14253        // Gateway-API emitter treats as "emit no listener + no
14254        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
14255        // (empty `paths` — the resolved-paths fallback the peer
14256        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
14257        // onto the substrate catch-all), and a fully-populated
14258        // multi-path-with-non-default-port fixture (the canonical
14259        // shape a public HTTP Aplicacao carries).
14260        //
14261        // Pins against a future silent detour that returned a fresh-
14262        // cloned [`crate::aplicacao::Entrada`] copy (which would
14263        // type-check via the `Clone` impl but silently break every
14264        // downstream caller that relied on the reference sharing the
14265        // composite's backing identity), a reference to an operator-
14266        // resolved overlay (the future per-cluster
14267        // `:entrada-overrides` slot — its resolution must land at
14268        // exactly this accessor body, not silently divert the raw
14269        // slot away from the peer [`Caixa::declared_mesh_slots`]
14270        // enumerator's presence probe), or an axis-shuffled projection
14271        // (a future detour that swapped `host` and `para` through the
14272        // accessor would silently split the paired
14273        // [`Caixa::aplicacao_view`] seed's forward input from the
14274        // sibling M3 gateway-artifact emitter's projection input).
14275        //
14276        // Fifth and final outer top-level [`Caixa`]
14277        // `Option<&Composite>`-return composite-reference accessor pin
14278        // on the substrate primitive — peer of the sibling
14279        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14280        // (b2bd9d7),
14281        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14282        // (35d8b52),
14283        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
14284        // (5d23d29), and
14285        // `placement_returns_placement_option_ref_verbatim_across_permutations`
14286        // (4fb8074) opening tetrad pins on the outer top-level
14287        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14288        // here to the third and final M3 mesh-slot axis so the closed
14289        // outer `Option<&Composite>` sub-family carries the same
14290        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
14291        // accessor discipline across all five arms.
14292        use crate::aplicacao::Entrada;
14293        let fixtures: Vec<Option<Entrada>> = vec![
14294            None,
14295            Some(Entrada {
14296                host: "checkout.quero.cloud".into(),
14297                para: "gateway".into(),
14298                paths: Vec::new(),
14299                port: crate::DEFAULT_SERVICO_PORT,
14300            }),
14301            Some(Entrada {
14302                host: "api.pleme.io".into(),
14303                para: "public-api".into(),
14304                paths: vec!["/v1".into(), "/v2".into()],
14305                port: 8080,
14306            }),
14307        ];
14308        for entrada in fixtures {
14309            let c = caixa_aplicacao_with_entrada(entrada.clone());
14310            assert_eq!(
14311                c.entrada(),
14312                entrada.as_ref(),
14313                "Caixa::entrada must return :entrada verbatim (got \
14314                 {:?}, expected {:?})",
14315                c.entrada(),
14316                entrada.as_ref(),
14317            );
14318            match (c.entrada(), c.entrada.as_ref()) {
14319                (Some(a), Some(b)) => assert!(
14320                    std::ptr::eq(a, b),
14321                    "Caixa::entrada accessor and self.entrada.as_ref() \
14322                     field access must borrow the same backing storage \
14323                     — the accessor is the substrate-primitive typed \
14324                     dispatch every downstream Aplicacao-external- \
14325                     gateway composite consumer must route through, and \
14326                     a reference-identity split would silently break \
14327                     every consumer that relied on the borrow sharing \
14328                     the composite's storage",
14329                ),
14330                (None, None) => {}
14331                _ => panic!(
14332                    "Caixa::entrada presence bit must byte-equal \
14333                     self.entrada.is_some() — a presence-bit drift \
14334                     would silently split the paired \
14335                     Caixa::aplicacao_view Aplicacao-composition seed's \
14336                     traversal head from the peer \
14337                     Caixa::declared_mesh_slots M3 declared-slot \
14338                     enumerator's presence probe",
14339                ),
14340            }
14341            assert_eq!(
14342                c.entrada().is_some(),
14343                c.entrada.is_some(),
14344                "Caixa::entrada().is_some() must byte-equal \
14345                 self.entrada.is_some() — a presence-bit drift would \
14346                 silently split every downstream Option<&Entrada> \
14347                 consumer's partition on the cluster-internal arm",
14348            );
14349        }
14350    }
14351
14352    #[test]
14353    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
14354        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
14355        // presence-probe arm must key off [`Caixa::entrada`], not the
14356        // raw `self.entrada.is_some()` field-probe. Structurally: a
14357        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
14358        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
14359        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
14360        // presence bit is `Some`, so the M3 kind-coherence gate must
14361        // surface the slot as "declared" even when every per-axis
14362        // scalar defers to the substrate catch-all / default port),
14363        // and a `Caixa { entrada: None, .. }` must NOT push the label
14364        // (the "author omitted the slot entirely" partition). The pair
14365        // jointly pins the accessor + declared-slot enumerator
14366        // composition: any future silent detour that had the accessor
14367        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
14368        // `.filter(|e| !e.paths.is_empty())` projection) would silently
14369        // absorb the "declared but empty-paths" arm at the accessor
14370        // boundary and the
14371        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
14372        // coherence gate would silently accept a struct-literal
14373        // `Caixa` carrying the drift.
14374        //
14375        // Peer of the sibling
14376        // `declared_servico_slots_limits_arm_routes_through_accessor`
14377        // (b2bd9d7),
14378        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14379        // (35d8b52),
14380        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
14381        // (5d23d29), and
14382        // `declared_mesh_slots_placement_arm_routes_through_accessor`
14383        // (4fb8074) composition pins on the sibling `:limits` /
14384        // `:behavior` / `:politicas` / `:placement` outer-
14385        // `Option<&Composite>` arms — same "the enumerator gate must
14386        // route through the substrate-primitive typed dispatch"
14387        // discipline extended onto the third and final M3 mesh-slot
14388        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
14389        // carries the routing invariant on every M3 mesh-slot arm.
14390        use crate::aplicacao::Entrada;
14391        let c = caixa_aplicacao_with_entrada(Some(Entrada {
14392            host: "checkout.quero.cloud".into(),
14393            para: "gateway".into(),
14394            paths: Vec::new(),
14395            port: crate::DEFAULT_SERVICO_PORT,
14396        }));
14397        let slots = c.declared_mesh_slots();
14398        assert!(
14399            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
14400            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
14401             `:entrada` is Some (even for empty-paths / default-port) \
14402             — the accessor and the enumerator gate must route through \
14403             the same substrate-primitive typed dispatch on the outer \
14404             :entrada presence bit (got slots={slots:?})",
14405        );
14406        let c = caixa_aplicacao_with_entrada(None);
14407        let slots = c.declared_mesh_slots();
14408        assert!(
14409            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
14410            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
14411             when `:entrada` is None — the author-omitted arm must \
14412             route through the accessor's None-return unchanged (got \
14413             slots={slots:?})",
14414        );
14415    }
14416
14417    #[test]
14418    fn aplicacao_view_entrada_arm_folds_through_accessor() {
14419        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
14420        // Aplicacao-composition seed must fold through
14421        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
14422        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
14423        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
14424        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
14425        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
14426        // equals the outer composite's authored value (the fold must
14427        // project the authored composite verbatim), and a `Caixa {
14428        // entrada: None, kind: Aplicacao, .. }` must surface an
14429        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
14430        // "author omitted the slot entirely" arm folds through the
14431        // accessor's `Option::cloned` onto the same `None` presence
14432        // bit — unlike the peer `:politicas` / `:placement` arms
14433        // `:entrada` has no cluster-default fold, the omitted arm
14434        // stays omitted). The pair jointly pins the accessor +
14435        // Aplicacao-composition seed composition: any future silent
14436        // detour that had the accessor divert the raw slot away from
14437        // the seed's fold (an operator-resolved overlay's forward arm
14438        // silently differing from the raw slot's forward arm) would
14439        // silently split the build-time gateway-artifact emission gate
14440        // from the caixa-mesh renderer's Aplicacao-view input at the
14441        // composition boundary.
14442        use crate::aplicacao::Entrada;
14443        let authored = Entrada {
14444            host: "api.pleme.io".into(),
14445            para: "public-api".into(),
14446            paths: vec!["/v1".into()],
14447            port: 8080,
14448        };
14449        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
14450        let view = c.aplicacao_view().unwrap();
14451        assert_eq!(
14452            view.entrada(),
14453            Some(&authored),
14454            "Caixa::aplicacao_view must fold the authored :entrada \
14455             composite through the accessor verbatim onto the \
14456             projected AplicacaoSpec — a future silent detour at the \
14457             seed's fold arm would surface here as a projected- \
14458             composite drift (got {:?})",
14459            view.entrada(),
14460        );
14461        let c = caixa_aplicacao_with_entrada(None);
14462        let view = c.aplicacao_view().unwrap();
14463        assert!(
14464            view.entrada().is_none(),
14465            "Caixa::aplicacao_view must fold None through the \
14466             accessor's Option::cloned onto None — the author- \
14467             omitted arm must route through the accessor's None-return \
14468             unchanged (got {:?})",
14469            view.entrada(),
14470        );
14471    }
14472
14473    #[test]
14474    fn entrada_projects_option_ref_by_borrow() {
14475        // The by-borrow pin: [`Caixa::entrada`] returns
14476        // `Option<&Entrada>` by borrow — the returned reference
14477        // borrows the underlying `Option<Entrada>` storage of the
14478        // `:entrada` slot and the accessor must not clone the backing
14479        // composite on every call. Peer of the sibling
14480        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
14481        // `behavior_projects_option_ref_by_borrow` (35d8b52),
14482        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
14483        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
14484        // borrow pins on the outer top-level [`Caixa`]
14485        // `Option<&Composite>`-return sub-family — extended here to
14486        // the fifth and final axis of the same sub-family, closing
14487        // the discipline: the accessor's returned reference must
14488        // borrow from `&self` (the returned reference's lifetime is
14489        // tied to `&self`), and calling the accessor twice on the
14490        // same [`Caixa`] must yield references that are pointer-equal
14491        // (the underlying byte-buffer is the storage `Entrada`'s
14492        // allocation, not a fresh copy) as well as value-equal
14493        // (idempotent, no side effects on `&self`).
14494        //
14495        // Pins against a future silent detour that returned an owned
14496        // `Entrada` (which would type-check via the `Clone` impl but
14497        // silently clone on every call), a `&Entrada` panic-return on
14498        // the `None` arm (which would collapse the load-bearing
14499        // `Option` presence-bit into a runtime panic), or a one-arm-
14500        // only accessor that returned a saturating composite on some
14501        // sentinel input.
14502        use crate::aplicacao::Entrada;
14503        for entrada in [
14504            Some(Entrada {
14505                host: "checkout.quero.cloud".into(),
14506                para: "gateway".into(),
14507                paths: Vec::new(),
14508                port: crate::DEFAULT_SERVICO_PORT,
14509            }),
14510            Some(Entrada {
14511                host: "api.pleme.io".into(),
14512                para: "public-api".into(),
14513                paths: vec!["/v1".into(), "/v2".into()],
14514                port: 8080,
14515            }),
14516        ] {
14517            let c = caixa_aplicacao_with_entrada(entrada.clone());
14518            let first = c.entrada().unwrap();
14519            let second = c.entrada().unwrap();
14520            assert_eq!(
14521                first, second,
14522                "Caixa::entrada must be idempotent — two successive \
14523                 calls on the same &self must return the same &Entrada",
14524            );
14525            assert!(
14526                std::ptr::eq(first, second),
14527                "Caixa::entrada must borrow the underlying \
14528                 Option<Entrada> storage — two successive calls must \
14529                 return references with the same backing pointer (a \
14530                 fresh Entrada clone would change the pointer on every \
14531                 call)",
14532            );
14533            assert_eq!(
14534                Some(first),
14535                entrada.as_ref(),
14536                "Caixa::entrada must return :entrada verbatim by \
14537                 borrow — got {first:?}, expected {:?}",
14538                entrada.as_ref(),
14539            );
14540        }
14541        let c = caixa_aplicacao_with_entrada(None);
14542        assert!(
14543            c.entrada().is_none(),
14544            "Caixa::entrada must return None when :entrada is absent \
14545             — the author-omitted arm must project through the \
14546             accessor's Option::None unchanged",
14547        );
14548    }
14549
14550    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
14551
14552    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
14553        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14554        c.estrategia = estrategia;
14555        c
14556    }
14557
14558    #[test]
14559    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
14560        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
14561        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
14562        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
14563        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
14564        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
14565        // over the same discriminant the raw `self.estrategia` field
14566        // access carries, byte-equal across every representative fixture
14567        // in the accept-set — the author-omitted `None` shape (the
14568        // "defer to [`RestartStrategy::default`] through the
14569        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
14570        // every non-`Supervisor`-kind `defcaixa` carries by
14571        // `#[serde(default)]`), and each of the four closed-set variants
14572        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
14573        // / [`RestartStrategy::RestForOne`] /
14574        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
14575        // partitions on.
14576        //
14577        // Pins against a future silent detour that re-derived the
14578        // strategy from a peer axis (an accidental fallback to
14579        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
14580        // collapse that read the outer `:children` list-length axis into
14581        // the strategy discriminator at the accessor boundary), a
14582        // stale-derive detour that substituted [`RestartStrategy::default`]
14583        // when the outer `Option` held `None` (which would silently
14584        // collapse the load-bearing "author explicitly declared
14585        // `:estrategia OneForOne`" vs "author omitted the slot and
14586        // inherited the default" partition the [`Self::declared_supervisor_slots`]
14587        // presence-probe reads — the enumerator gate would still push
14588        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
14589        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
14590        // kind-coherence gate's traversal head from the
14591        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
14592        // composition head), a reference to an operator-resolved overlay
14593        // (the future per-cluster `:estrategia-overrides` slot — its
14594        // resolution must land at exactly this accessor body, not
14595        // silently divert the raw slot away from a second consumer), or
14596        // an axis-remap projection (a future detour that mapped
14597        // `OneForAll` through the accessor onto `OneForOne` would
14598        // silently split every downstream sibling-restart-strategy
14599        // consumer's per-arm fan-out).
14600        //
14601        // First outer top-level [`Caixa`] `Option<Copy>`-return
14602        // supervisor-tree-slot flat-spread accessor pin on the substrate
14603        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
14604        // projection pattern the sibling per-`Caixa` `:max-restarts` /
14605        // `:restart-window` future outer-scalar pins fold on. Peer of
14606        // the inner-altitude
14607        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
14608        // (eafb619) pin on the post-composition [`SupervisorSpec`]
14609        // altitude — same "the substrate-primitive accessor must byte-
14610        // equal the raw field access verbatim across every author-
14611        // declared value" discipline extended onto the pre-composition
14612        // outer author-surface [`Caixa`] altitude. Peer of the closed
14613        // outer-`Caixa` `Option<&Composite>` composite-reference family
14614        // the sibling `limits` / `behavior` / `politicas` / `placement` /
14615        // `entrada`
14616        // `..._returns_..._option_ref_verbatim_across_permutations` pins
14617        // already carry on the outer `Option<&Composite>` altitude.
14618        use crate::supervisor::RestartStrategy;
14619        let fixtures: Vec<Option<RestartStrategy>> = vec![
14620            None,
14621            Some(RestartStrategy::OneForOne),
14622            Some(RestartStrategy::OneForAll),
14623            Some(RestartStrategy::RestForOne),
14624            Some(RestartStrategy::SimpleOneForOne),
14625        ];
14626        for estrategia in fixtures {
14627            let c = caixa_with_estrategia(estrategia);
14628            assert_eq!(
14629                c.estrategia(),
14630                estrategia,
14631                "Caixa::estrategia must return :estrategia verbatim (got \
14632                 {:?}, expected {:?})",
14633                c.estrategia(),
14634                estrategia,
14635            );
14636            assert_eq!(
14637                c.estrategia(),
14638                c.estrategia,
14639                "Caixa::estrategia accessor and self.estrategia field \
14640                 access must byte-equal — the accessor is the substrate-\
14641                 primitive typed dispatch every downstream supervisor-\
14642                 tree flat-spread consumer must route through, and a \
14643                 discriminant split would silently break every consumer \
14644                 that relied on the accessor sharing the field's own \
14645                 Option<Copy> shape",
14646            );
14647            assert_eq!(
14648                c.estrategia().is_some(),
14649                c.estrategia.is_some(),
14650                "Caixa::estrategia().is_some() must byte-equal \
14651                 self.estrategia.is_some() — a presence-bit drift would \
14652                 silently split the paired Caixa::declared_supervisor_slots \
14653                 presence-probe arm from the Caixa::supervisor_view \
14654                 unwrap_or_default() fold's composition input",
14655            );
14656        }
14657    }
14658
14659    #[test]
14660    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
14661        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
14662        // `:estrategia` presence-probe arm must key off
14663        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
14664        // field-probe. Structurally: every `Caixa { estrategia:
14665        // Some(RestartStrategy::_), .. }` variant must push
14666        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
14667        // (the presence bit is `Some` for every closed-set variant, so
14668        // the M2 supervisor-tree kind-coherence gate must surface the
14669        // slot as "declared" regardless of which variant the author
14670        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
14671        // the label (the "author omitted the slot entirely, deferring
14672        // to [`RestartStrategy::default`] through the supervisor_view
14673        // fold" partition). The pair jointly pins the accessor +
14674        // declared-slot enumerator composition: any future silent detour
14675        // that had the accessor collapse `Some(RestartStrategy::default())`
14676        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
14677        // projection) would silently absorb the "declared but default-
14678        // valued" arm at the accessor boundary and the
14679        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
14680        // coherence gate would silently accept a struct-literal `Caixa`
14681        // carrying the drift.
14682        //
14683        // Peer of the sibling per-`Caixa`
14684        // `declared_servico_slots_limits_arm_routes_through_accessor`
14685        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
14686        // `Option<&LimitsSpec>` composition axis — same "the enumerator
14687        // gate must route through the substrate-primitive typed
14688        // dispatch" discipline extended onto the flat-spread M2
14689        // supervisor-tree `Option<RestartStrategy>`-composition surface,
14690        // opening the outer-`Caixa` supervisor-tree-slot arm of the
14691        // composition-pin family.
14692        use crate::supervisor::RestartStrategy;
14693        for estrategia in [
14694            RestartStrategy::OneForOne,
14695            RestartStrategy::OneForAll,
14696            RestartStrategy::RestForOne,
14697            RestartStrategy::SimpleOneForOne,
14698        ] {
14699            let c = caixa_with_estrategia(Some(estrategia));
14700            let slots = c.declared_supervisor_slots();
14701            assert!(
14702                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
14703                "declared_supervisor_slots must push \
14704                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
14705                 Some({estrategia:?}) — the accessor and the enumerator \
14706                 gate must route through the same substrate-primitive \
14707                 typed dispatch on the outer :estrategia presence bit \
14708                 (got slots={slots:?})",
14709            );
14710        }
14711        let c = caixa_with_estrategia(None);
14712        let slots = c.declared_supervisor_slots();
14713        assert!(
14714            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
14715            "declared_supervisor_slots must NOT push \
14716             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
14717             — the author-omitted arm must route through the accessor's \
14718             None-return unchanged (got slots={slots:?})",
14719        );
14720    }
14721
14722    #[test]
14723    fn supervisor_view_estrategia_arm_routes_through_accessor() {
14724        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
14725        // [`SupervisorSpec`] construction arm must key off
14726        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
14727        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
14728        // for every `:kind Supervisor` `Caixa` carrying an author-
14729        // declared `Some(RestartStrategy::_)` variant, the composed
14730        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
14731        // outer accessor's declared variant unchanged; and for a
14732        // `:kind Supervisor` `Caixa` carrying `None`, the composed
14733        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
14734        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
14735        // arm the flat-spread `unwrap_or_default()` fold projects to on
14736        // the author-omitted arm — this is the *composition* between the
14737        // outer `Option<RestartStrategy>` accessor's presence-bit
14738        // surface and the inner post-composition non-`Option`
14739        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
14740        // pins the accessor + supervisor_view composition: any future
14741        // silent detour that had the accessor promote `None` to
14742        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
14743        // projection) would silently collapse the two arms into one at
14744        // the accessor boundary and the [`Self::declared_supervisor_slots`]
14745        // presence probe would silently drift from the composition site.
14746        //
14747        // Peer of the sibling M2 supervisor-slot post-composition
14748        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
14749        // pin on the [`SupervisorSpec::validate`] altitude — this pin
14750        // extends that inner-altitude accessor-routing discipline onto
14751        // the pre-composition outer author-surface [`Caixa`] altitude,
14752        // pinning the composition edge between the flat-spread outer
14753        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
14754        // `RestartStrategy` axes.
14755        use crate::CaixaKind;
14756        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
14757        for estrategia in [
14758            RestartStrategy::OneForOne,
14759            RestartStrategy::OneForAll,
14760            RestartStrategy::RestForOne,
14761            RestartStrategy::SimpleOneForOne,
14762        ] {
14763            let mut c = caixa_with_estrategia(Some(estrategia));
14764            c.kind = CaixaKind::Supervisor;
14765            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
14766            // shape partition through the [`gen_platform::IsVariant`]
14767            // derive-generated
14768            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
14769            // than the raw `matches!(estrategia, RestartStrategy::
14770            // SimpleOneForOne)` open-coded pattern-match — same closed-
14771            // set-typed-enum arm-discriminator dispatch discipline the
14772            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
14773            // convergence (915a934) extended onto its two paired positive
14774            // / negated `matches!` sites and the peer
14775            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
14776            // predicate convergence (766ec63) extended onto the M3 mesh-
14777            // slot per-`:placement` distribution-strategy discriminator
14778            // axis. See the sibling `supervisor::tests::
14779            // round_trip_all_strategies` and
14780            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
14781            // fixtures — the three sites (all test-only,
14782            // acknowledged in 915a934's Prior-commits footnote as the
14783            // outstanding follow-up) now consult one typed dispatch on
14784            // the substrate primitive.
14785            c.children = if estrategia.is_simple_one_for_one() {
14786                Vec::new()
14787            } else {
14788                vec![ChildSpec {
14789                    caixa: "worker".into(),
14790                    versao: "^0.1".into(),
14791                    restart: RestartPolicy::Permanent,
14792                }]
14793            };
14794            let view = c.supervisor_view().expect(
14795                "supervisor_view must materialize a SupervisorSpec for a \
14796                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
14797            );
14798            assert_eq!(
14799                view.estrategia(),
14800                c.estrategia().unwrap(),
14801                "supervisor_view must carry the outer Caixa::estrategia() \
14802                 declared variant onto the composed SupervisorSpec.estrategia \
14803                 field verbatim on the Some arm (got {:?}, expected {:?})",
14804                view.estrategia(),
14805                c.estrategia().unwrap(),
14806            );
14807        }
14808        // The author-omitted arm: outer `None` → composed
14809        // `RestartStrategy::default()` through the flat-spread
14810        // `unwrap_or_default()` fold.
14811        let mut c = caixa_with_estrategia(None);
14812        c.kind = CaixaKind::Supervisor;
14813        // Populate children so the sibling supervisor slots are coherent
14814        // for the [`Self::supervisor_view`] projection; the `:estrategia`
14815        // arm still defers to [`RestartStrategy::default`] on the
14816        // author-omitted arm even when the sibling slots carry values.
14817        c.children = vec![ChildSpec {
14818            caixa: "worker".into(),
14819            versao: "^0.1".into(),
14820            restart: RestartPolicy::Permanent,
14821        }];
14822        let view = c.supervisor_view().expect(
14823            "supervisor_view must materialize a SupervisorSpec for a \
14824             :kind Supervisor Caixa carrying a None `:estrategia` slot",
14825        );
14826        assert_eq!(
14827            view.estrategia(),
14828            RestartStrategy::default(),
14829            "supervisor_view must project the outer Caixa::estrategia() \
14830             None arm onto RestartStrategy::default() through the flat-\
14831             spread unwrap_or_default() fold (got {:?}, expected {:?})",
14832            view.estrategia(),
14833            RestartStrategy::default(),
14834        );
14835        assert!(
14836            c.estrategia().is_none(),
14837            "Caixa::estrategia() must remain None on the author-omitted \
14838             arm — the supervisor_view fold must not mutate the outer \
14839             flat-spread presence bit",
14840        );
14841    }
14842
14843    #[test]
14844    fn estrategia_projects_option_by_copy() {
14845        // The by-`Copy` pin: [`Caixa::estrategia`] returns
14846        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
14847        // the accessor does not borrow `&self` past the call (no
14848        // lifetime on the return type), and calling the accessor twice
14849        // on the same [`Caixa`] must yield discriminant-equal values
14850        // (idempotent, no side effects on `&self`). Peer of the sibling
14851        // outer-`Caixa` `Option<&Composite>` by-borrow
14852        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
14853        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
14854        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
14855        // `placement_projects_option_ref_by_borrow` (4fb8074) /
14856        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
14857        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
14858        // extended here to the outer-`Caixa` `Option<Copy>`-return
14859        // flat-spread axis. The `Copy` discipline replaces the pointer-
14860        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
14861        // `Copy` discriminant is definitionally the same discriminant, so
14862        // the axis reduces to discriminant equality).
14863        //
14864        // Pins against a future silent detour that returned a fresh
14865        // `Option<&RestartStrategy>` (which would type-check but silently
14866        // introduce a borrow of `&self` past the call, collapsing the
14867        // load-bearing "no lifetime on the return type" `Copy` projection
14868        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
14869        // read side effect that flipped the outer discriminant on
14870        // successive calls, or an axis-remap projection that returned a
14871        // different variant than the field storage.
14872        use crate::supervisor::RestartStrategy;
14873        for estrategia in [
14874            Some(RestartStrategy::OneForOne),
14875            Some(RestartStrategy::OneForAll),
14876            Some(RestartStrategy::RestForOne),
14877            Some(RestartStrategy::SimpleOneForOne),
14878        ] {
14879            let c = caixa_with_estrategia(estrategia);
14880            let first = c.estrategia();
14881            let second = c.estrategia();
14882            assert_eq!(
14883                first, second,
14884                "Caixa::estrategia must be idempotent — two successive \
14885                 calls on the same &self must return the same \
14886                 Option<RestartStrategy>",
14887            );
14888            assert_eq!(
14889                first, estrategia,
14890                "Caixa::estrategia must return :estrategia verbatim by \
14891                 Copy — got {first:?}, expected {estrategia:?}",
14892            );
14893        }
14894        let c = caixa_with_estrategia(None);
14895        assert!(
14896            c.estrategia().is_none(),
14897            "Caixa::estrategia must return None when :estrategia is \
14898             absent — the author-omitted arm must project through the \
14899             accessor's Option::None unchanged",
14900        );
14901    }
14902
14903    // ── Caixa::max_restarts / Caixa::restart_window —
14904    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
14905    //    (Option<u32> / Option<&str>) folding on the ed04d3c
14906    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
14907
14908    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
14909        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14910        c.max_restarts = max_restarts;
14911        c
14912    }
14913
14914    fn caixa_supervisor_with_max_restarts_and_window(
14915        max_restarts: Option<u32>,
14916        restart_window: Option<&str>,
14917    ) -> Caixa {
14918        use crate::CaixaKind;
14919        use crate::supervisor::{ChildSpec, RestartPolicy};
14920        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
14921        c.kind = CaixaKind::Supervisor;
14922        c.max_restarts = max_restarts;
14923        c.restart_window = restart_window.map(str::to_string);
14924        c.children = vec![ChildSpec {
14925            caixa: "worker".into(),
14926            versao: "^0.1".into(),
14927            restart: RestartPolicy::Permanent,
14928        }];
14929        c
14930    }
14931
14932    #[test]
14933    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
14934        // Value-shape pin: [`Caixa::max_restarts`] returns the
14935        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
14936        // from the typed slot's own storage, byte-equal across the
14937        // author-omitted `None` arm (the "defer to the
14938        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
14939        // `{intensity, 5, 60}` default" partition every
14940        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
14941        // and each of the representative fixtures in the accept-set —
14942        // `0` (the zero-floor arm the peer
14943        // [`crate::supervisor::SupervisorSpec::validate`]
14944        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
14945        // the post-composition altitude — the accessor must ship the
14946        // raw slot verbatim so struct-literal fixtures continue to
14947        // expose the zero at the accessor boundary), the OTP-canonical
14948        // `5` default (`{intensity, 5, 60}` worker-supervisor from
14949        // Learn You Some Erlang), `1000` (the
14950        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
14951        // upper-bound gate accepts on the boundary), `u32::MAX` (a
14952        // past-the-cap sentinel that the substrate-primitive accessor
14953        // must still ship verbatim). Second outer top-level
14954        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
14955        // pin — folds on the sibling
14956        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
14957        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
14958        // onto the sibling `Option<u32>` restart-budget-count arm.
14959        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
14960        for max_restarts in fixtures {
14961            let c = caixa_with_max_restarts(max_restarts);
14962            assert_eq!(
14963                c.max_restarts(),
14964                max_restarts,
14965                "Caixa::max_restarts must return :max-restarts verbatim \
14966                 (got {:?}, expected {max_restarts:?})",
14967                c.max_restarts(),
14968            );
14969            assert_eq!(
14970                c.max_restarts(),
14971                c.max_restarts,
14972                "Caixa::max_restarts accessor and self.max_restarts \
14973                 field access must byte-equal — a presence-bit or count \
14974                 drift would silently split the paired \
14975                 Caixa::declared_supervisor_slots presence-probe arm \
14976                 from the Caixa::supervisor_view unwrap_or(5) fold's \
14977                 composition input",
14978            );
14979        }
14980    }
14981
14982    #[test]
14983    fn max_restarts_projects_option_by_copy() {
14984        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
14985        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
14986        // borrow `&self` past the call (no lifetime on the return type),
14987        // and calling the accessor twice on the same [`Caixa`] must
14988        // yield equal values (idempotent, no side effects). Peer of the
14989        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
14990        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
14991        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
14992            let c = caixa_with_max_restarts(max_restarts);
14993            let first = c.max_restarts();
14994            let second = c.max_restarts();
14995            assert_eq!(
14996                first, second,
14997                "Caixa::max_restarts must be idempotent — two successive \
14998                 calls on the same &self must return the same Option<u32>",
14999            );
15000            assert_eq!(
15001                first, max_restarts,
15002                "Caixa::max_restarts must return :max-restarts verbatim \
15003                 by Copy — got {first:?}, expected {max_restarts:?}",
15004            );
15005        }
15006    }
15007
15008    #[test]
15009    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
15010        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15011        // `:max-restarts` presence-probe arm must key off
15012        // [`Caixa::max_restarts`], not the raw
15013        // `self.max_restarts.is_some()` field-probe. Structurally: every
15014        // `Caixa { max_restarts: Some(_), .. }` variant must push
15015        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
15016        // list (the presence bit is `Some` for every representative
15017        // count, so the M2 kind-coherence gate must surface the slot as
15018        // "declared"), and a `Caixa { max_restarts: None, .. }` must
15019        // NOT push the label. Peer of the sibling
15020        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
15021        // (ed04d3c) composition pin — same routing-through-accessor
15022        // discipline extended onto the sibling flat-spread `Option<u32>`
15023        // arm.
15024        for max_restarts in [0u32, 5, 1000, u32::MAX] {
15025            let c = caixa_with_max_restarts(Some(max_restarts));
15026            let slots = c.declared_supervisor_slots();
15027            assert!(
15028                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
15029                "declared_supervisor_slots must push \
15030                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
15031                 is Some({max_restarts}) — the accessor and the \
15032                 enumerator gate must route through the same \
15033                 substrate-primitive typed dispatch on the outer \
15034                 :max-restarts presence bit (got slots={slots:?})",
15035            );
15036        }
15037        let c = caixa_with_max_restarts(None);
15038        let slots = c.declared_supervisor_slots();
15039        assert!(
15040            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
15041            "declared_supervisor_slots must NOT push \
15042             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
15043             None — the author-omitted arm must route through the \
15044             accessor's None-return unchanged (got slots={slots:?})",
15045        );
15046    }
15047
15048    #[test]
15049    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
15050        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
15051        // [`SupervisorSpec`] construction arm must key off
15052        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
15053        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
15054        // every `:kind Supervisor` `Caixa` carrying an author-declared
15055        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
15056        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
15057        // carrying `None`, the composed [`SupervisorSpec`]'s
15058        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
15059        // of the sibling
15060        // `supervisor_view_estrategia_arm_routes_through_accessor`
15061        // (ed04d3c) composition pin.
15062        for max_restarts in [1u32, 5, 1000] {
15063            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
15064            let view = c.supervisor_view().expect(
15065                "supervisor_view must materialize a SupervisorSpec for a \
15066                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
15067            );
15068            assert_eq!(
15069                view.max_restarts(),
15070                max_restarts,
15071                "supervisor_view must carry the outer \
15072                 Caixa::max_restarts() Some arm onto the composed \
15073                 SupervisorSpec.max_restarts field verbatim (got {}, \
15074                 expected {max_restarts})",
15075                view.max_restarts(),
15076            );
15077        }
15078        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
15079        let view = c.supervisor_view().expect(
15080            "supervisor_view must materialize a SupervisorSpec for a \
15081             :kind Supervisor Caixa carrying a None :max-restarts",
15082        );
15083        assert_eq!(
15084            view.max_restarts(),
15085            5,
15086            "supervisor_view must project the outer \
15087             Caixa::max_restarts() None arm onto the OTP-canonical \
15088             {{intensity, 5, 60}} default (5) through the flat-spread \
15089             unwrap_or(5) fold (got {})",
15090            view.max_restarts(),
15091        );
15092        assert!(
15093            c.max_restarts().is_none(),
15094            "Caixa::max_restarts() must remain None on the author-\
15095             omitted arm — the supervisor_view fold must not mutate \
15096             the outer flat-spread presence bit",
15097        );
15098    }
15099
15100    #[test]
15101    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
15102        // Value-shape pin: [`Caixa::restart_window`] returns the
15103        // `:restart-window` typed `Option<String>` verbatim as an
15104        // `Option<&str>`, borrowed from the typed slot's own storage,
15105        // byte-equal across the author-omitted `None` arm and each of
15106        // the representative fixtures in the accept-set — the canonical
15107        // `"60s"` from `{intensity, 5, 60}`, the sibling
15108        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
15109        // / `"0s"`) the shared codec's positive-set sweep pin covers,
15110        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
15111        // seconds drift the sibling [`Self::validate_restart_window`]
15112        // gate refuses; the accessor must ship the raw slot verbatim
15113        // so struct-literal fixtures continue to expose the drift at
15114        // the accessor boundary). Third outer top-level [`Caixa`]
15115        // supervisor-tree flat-spread pin — extends the sub-family onto
15116        // the sibling `Option<&str>` raw-duration-string arm.
15117        for window in [
15118            None,
15119            Some("60s"),
15120            Some("5m"),
15121            Some("1h"),
15122            Some("500ms"),
15123            Some("1.5s"),
15124            Some(""),
15125        ] {
15126            let c = caixa_with_restart_window(window);
15127            assert_eq!(
15128                c.restart_window(),
15129                window,
15130                "Caixa::restart_window must return :restart-window \
15131                 verbatim as Option<&str> (got {:?}, expected {window:?})",
15132                c.restart_window(),
15133            );
15134            assert_eq!(
15135                c.restart_window(),
15136                c.restart_window.as_deref(),
15137                "Caixa::restart_window accessor and \
15138                 self.restart_window.as_deref() field access must \
15139                 byte-equal — a byte-level drift would silently split \
15140                 the paired Caixa::declared_supervisor_slots \
15141                 presence-probe arm from the \
15142                 Caixa::validate_restart_window shared-codec gate and \
15143                 the Caixa::supervisor_view soft-swallowing fold",
15144            );
15145        }
15146    }
15147
15148    #[test]
15149    fn restart_window_projects_slice_by_borrow() {
15150        // The by-borrow pin: [`Caixa::restart_window`] returns
15151        // `Option<&str>` by borrow — the returned string slice borrows
15152        // the underlying `Option<String>` storage of the `:restart-window`
15153        // slot and the accessor must not clone on every call. Peer of
15154        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
15155        // by-borrow pins on the universal-axis scalar family
15156        // (`licenca_projects_option_ref_by_borrow` /
15157        // `descricao_projects_option_ref_by_borrow` and siblings) —
15158        // extended onto the M2 supervisor-tree flat-spread
15159        // `Option<&str>` raw-duration-string axis.
15160        for window in [None, Some("60s"), Some("5m"), Some("")] {
15161            let c = caixa_with_restart_window(window);
15162            let first = c.restart_window();
15163            let second = c.restart_window();
15164            assert_eq!(
15165                first, second,
15166                "Caixa::restart_window must be idempotent — two \
15167                 successive calls on the same &self must return the \
15168                 same Option<&str>",
15169            );
15170            if let (Some(a), Some(b)) = (first, second) {
15171                assert_eq!(
15172                    a.as_ptr(),
15173                    b.as_ptr(),
15174                    "Caixa::restart_window must borrow the underlying \
15175                     String storage — two successive Some-arm calls must \
15176                     return slices with the same backing pointer (a fresh \
15177                     String clone would change the pointer on every call)",
15178                );
15179            }
15180            assert_eq!(
15181                first, window,
15182                "Caixa::restart_window must return :restart-window \
15183                 verbatim by borrow — got {first:?}, expected {window:?}",
15184            );
15185        }
15186    }
15187
15188    #[test]
15189    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
15190        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15191        // `:restart-window` presence-probe arm must key off
15192        // [`Caixa::restart_window`], not the raw
15193        // `self.restart_window.is_some()` field-probe. Structurally:
15194        // every `Caixa { restart_window: Some(_), .. }` must push
15195        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
15196        // list, and a `Caixa { restart_window: None, .. }` must NOT
15197        // push the label. Peer of the sibling
15198        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
15199        // routing pin.
15200        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
15201            let c = caixa_with_restart_window(Some(window));
15202            let slots = c.declared_supervisor_slots();
15203            assert!(
15204                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
15205                "declared_supervisor_slots must push \
15206                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
15207                 `:restart-window` is Some({window:?}) — the accessor \
15208                 and the enumerator gate must route through the same \
15209                 substrate-primitive typed dispatch on the outer \
15210                 :restart-window presence bit (got slots={slots:?})",
15211            );
15212        }
15213        let c = caixa_with_restart_window(None);
15214        let slots = c.declared_supervisor_slots();
15215        assert!(
15216            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
15217            "declared_supervisor_slots must NOT push \
15218             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
15219             is None — the author-omitted arm must route through the \
15220             accessor's None-return unchanged (got slots={slots:?})",
15221        );
15222    }
15223
15224    #[test]
15225    fn validate_restart_window_arm_routes_through_accessor() {
15226        // Composition pin: [`Caixa::validate_restart_window`]'s
15227        // shared-codec fold arm must key off [`Caixa::restart_window`],
15228        // not the raw `self.restart_window.as_deref()` field-projection.
15229        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
15230        // express no reset" canonical shape); (2) a canonical `Some`
15231        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
15232        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
15233        // .. })` carrying the offending raw string verbatim. The three
15234        // arms jointly pin that the validator's raw-string binding is
15235        // the accessor's return, not a peer projection — any future
15236        // silent detour that had the accessor collapse `Some("")` to
15237        // `None` would silently absorb the empty-after-trim refusal
15238        // case at the accessor boundary.
15239        caixa_with_restart_window(None)
15240            .validate_restart_window()
15241            .expect("None :restart-window must validate through the accessor");
15242        caixa_with_restart_window(Some("60s"))
15243            .validate_restart_window()
15244            .expect("canonical :restart-window \"60s\" must validate through the accessor");
15245        let err = caixa_with_restart_window(Some("1.5s"))
15246            .validate_restart_window()
15247            .expect_err("fractional-seconds :restart-window must fail through the accessor");
15248        assert!(
15249            matches!(
15250                err,
15251                ManifestError::RestartWindowMalformed { ref restart_window, .. }
15252                    if restart_window == "1.5s"
15253            ),
15254            "validator must carry the offending raw string verbatim \
15255             from the accessor's borrowed &str (got {err:?})",
15256        );
15257    }
15258
15259    #[test]
15260    fn supervisor_view_restart_window_arm_routes_through_accessor() {
15261        // Composition pin: [`Caixa::supervisor_view`]'s
15262        // per-`:restart-window` [`SupervisorSpec`] construction arm
15263        // must key off [`Caixa::restart_window`]'s soft-swallowing
15264        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
15265        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
15266        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
15267        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
15268        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
15269        // (the shared codec's canonical parse); (3) codec-rejected
15270        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
15271        // (the soft-swallow preserving the view's best-effort shape).
15272        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
15273        let view = c.supervisor_view().expect("Supervisor kind has a view");
15274        assert_eq!(
15275            view.restart_window(),
15276            None,
15277            "supervisor_view must project outer None :restart-window \
15278             onto None on the composed SupervisorSpec (never-reset \
15279             sentinel) through the accessor's None-return unchanged",
15280        );
15281
15282        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
15283        let view = c.supervisor_view().expect("Supervisor kind has a view");
15284        assert_eq!(
15285            view.restart_window(),
15286            Some(std::time::Duration::from_secs(60)),
15287            "supervisor_view must fold outer Some(\"60s\") through the \
15288             shared duration_codec into Duration::from_secs(60) on the \
15289             composed SupervisorSpec (accessor's Some(&str) → codec \
15290             parse → Some(Duration))",
15291        );
15292
15293        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
15294        let view = c.supervisor_view().expect("Supervisor kind has a view");
15295        assert_eq!(
15296            view.restart_window(),
15297            None,
15298            "supervisor_view must soft-swallow the shared-codec parse \
15299             failure to None (the view's best-effort shape the sibling \
15300             manifest-level validate_restart_window surfaces as \
15301             RestartWindowMalformed); the accessor's raw-string return \
15302             is the single input every downstream consumer keys off",
15303        );
15304    }
15305
15306    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
15307
15308    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
15309        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15310        c.upgrade_from = upgrade_from;
15311        c
15312    }
15313
15314    #[test]
15315    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
15316        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
15317        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
15318        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
15319        // typed `Vec<UpgradeFromEntry>` verbatim as a
15320        // `&[UpgradeFromEntry]` slice-view over the same backing
15321        // buffer the raw `self.upgrade_from.as_slice()` field access
15322        // borrows from, element-equal across every representative
15323        // fixture in the accept-set — `[]` (the "no hot-upgrade path
15324        // declared" arm every `defcaixa` without an `:upgrade-from`
15325        // block carries; `#[serde(default)]` folds an omitted slot
15326        // onto `Vec::new()`), a canonical single-entry `Restart`
15327        // fixture (the shape most Servicos carry — a single prior
15328        // version with the fallback strategy), a canonical multi-
15329        // entry list carrying every typed instruction variant
15330        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
15331        // `Restart`), and a past-the-guard sentinel — a duplicate-
15332        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
15333        // ([`crate::upgrade::validate_upgrade_from`] rejects through
15334        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
15335        // ship the raw slot verbatim so struct-literal fixtures
15336        // continue to expose the duplicate at the accessor boundary).
15337        //
15338        // Pins against a future silent detour that returned an owned
15339        // `Vec<UpgradeFromEntry>` (which would type-check but silently
15340        // clone on every accessor call, breaking the zero-cost
15341        // projection every peer sibling slice accessor carries), a
15342        // `[dup, dup] → [dup]` dedup collapse (which would silently
15343        // absorb the `DuplicateFrom` refusal case at the accessor
15344        // boundary and the [`crate::StandardLayout::verify`] cross-
15345        // entry gate would silently accept a struct-literal `Caixa`
15346        // carrying the drift), a reference to an operator-resolved
15347        // overlay (the future per-cluster `:upgrade-overrides` slot
15348        // — its resolution must land at exactly this accessor body,
15349        // not silently divert the raw slot away from a second
15350        // consumer), or an axis-shuffled projection (a future detour
15351        // that reordered entries through the accessor would silently
15352        // split the paired [`crate::StandardLayout::verify`] per-
15353        // `:upgrade-from` shape gate's traversal input from the peer
15354        // [`crate::render::servico_m2_overlay`] emitter's projection
15355        // input, since the operator's hot-upgrade dispatch matches
15356        // per-`:from` and axis reordering would silently split the
15357        // per-entry script-path existence probe's iteration order
15358        // from the M2 overlay emitter's serialized-entry order).
15359        //
15360        // First outer top-level [`Caixa`] `&[Composite]`-return
15361        // slice accessor pin on the substrate primitive for M2 / M3
15362        // typed-slot vec-carry axes — opens the outer-`Caixa`
15363        // `&[Composite]` composite-slice projection pattern the
15364        // sibling `:children` [`crate::supervisor::ChildSpec`] /
15365        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
15366        // [`crate::aplicacao::WitContract`] future outer-composite-
15367        // slice pins fold on. Peer of the closed outer-`Caixa`
15368        // scalar `Option<&Composite>` composite-reference family the
15369        // sibling `limits` / `behavior` / `politicas` / `placement`
15370        // / `entrada` `..._returns_..._option_ref_verbatim_across_
15371        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
15372        // the "byte-equal, borrow-shared" outer-accessor discipline
15373        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
15374        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15375        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
15376            vec![],
15377            vec![UpgradeFromEntry {
15378                from: "0.0.1".into(),
15379                instructions: vec![UpgradeInstruction::Restart],
15380            }],
15381            vec![
15382                UpgradeFromEntry {
15383                    from: "0.0.1".into(),
15384                    instructions: vec![
15385                        UpgradeInstruction::LoadModule {
15386                            module: "demo".into(),
15387                        },
15388                        UpgradeInstruction::SoftPurge {
15389                            module: "demo".into(),
15390                        },
15391                    ],
15392                },
15393                UpgradeFromEntry {
15394                    from: "0.0.2".into(),
15395                    instructions: vec![
15396                        UpgradeInstruction::StateChange {
15397                            script: "servicos/upgrade.lisp".into(),
15398                        },
15399                        UpgradeInstruction::Purge {
15400                            module: "demo".into(),
15401                        },
15402                        UpgradeInstruction::Restart,
15403                    ],
15404                },
15405            ],
15406            vec![
15407                UpgradeFromEntry {
15408                    from: "0.1.0".into(),
15409                    instructions: vec![UpgradeInstruction::Restart],
15410                },
15411                UpgradeFromEntry {
15412                    from: "0.1.0".into(),
15413                    instructions: vec![UpgradeInstruction::Restart],
15414                },
15415            ],
15416        ];
15417        for upgrade_from in fixtures {
15418            let c = caixa_with_upgrade_from(upgrade_from.clone());
15419            assert_eq!(
15420                c.upgrade_from(),
15421                upgrade_from.as_slice(),
15422                "Caixa::upgrade_from must return :upgrade-from \
15423                 verbatim (got {:?}, expected {upgrade_from:?})",
15424                c.upgrade_from(),
15425            );
15426            assert_eq!(
15427                c.upgrade_from(),
15428                c.upgrade_from.as_slice(),
15429                "Caixa::upgrade_from must element-equal the raw \
15430                 `self.upgrade_from.as_slice()` field access across \
15431                 every value in the Vec<UpgradeFromEntry> accept-set",
15432            );
15433            assert_eq!(
15434                c.upgrade_from().is_empty(),
15435                c.upgrade_from.is_empty(),
15436                "Caixa::upgrade_from().is_empty() must byte-equal \
15437                 self.upgrade_from.is_empty() — a presence-bit drift \
15438                 would silently split the paired \
15439                 Caixa::declared_servico_slots M2 declared-slot \
15440                 enumerator's presence probe from the peer \
15441                 crate::render::servico_m2_overlay M2 overlay \
15442                 emitter's presence gate",
15443            );
15444        }
15445    }
15446
15447    #[test]
15448    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
15449        // Composition pin: [`Caixa::declared_servico_slots`]'s
15450        // `:upgrade-from` presence-probe arm must key off
15451        // [`Caixa::upgrade_from`], not the raw
15452        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
15453        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
15454        // instructions: vec![Restart] }], .. }` must push
15455        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
15456        // (the presence bit is non-empty, so the M2 kind-coherence
15457        // gate must surface the slot as "declared"), and a `Caixa {
15458        // upgrade_from: vec![], .. }` must NOT push the label (the
15459        // "author omitted the slot entirely" arm — the empty-slice
15460        // partition the serde-default folds onto). The pair jointly
15461        // pins the accessor + declared-slot enumerator composition:
15462        // any future silent detour that had the accessor collapse
15463        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
15464        // is_empty())` projection) would silently absorb the
15465        // "declared but degenerate" arm at the accessor boundary and
15466        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
15467        // coherence gate would silently accept a struct-literal
15468        // `Caixa` carrying the drift.
15469        //
15470        // Peer of the sibling
15471        // `declared_servico_slots_limits_arm_routes_through_accessor`
15472        // (b2bd9d7) and
15473        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15474        // (35d8b52) composition pins on the sibling `:limits` /
15475        // `:behavior` outer-`Option<&Composite>` arms — same "the
15476        // enumerator gate must route through the substrate-primitive
15477        // typed dispatch" discipline extended onto the third M2
15478        // Servico-runtime slot axis, closing the enumerator's routing
15479        // invariant on every M2 arm.
15480        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15481        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
15482            from: "0.0.1".into(),
15483            instructions: vec![UpgradeInstruction::Restart],
15484        }]);
15485        let slots = c.declared_servico_slots();
15486        assert!(
15487            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
15488            "declared_servico_slots must push \
15489             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
15490             non-empty — the accessor and the enumerator gate must \
15491             route through the same substrate-primitive typed \
15492             dispatch on the outer :upgrade-from presence bit (got \
15493             slots={slots:?})",
15494        );
15495        let c = caixa_with_upgrade_from(vec![]);
15496        let slots = c.declared_servico_slots();
15497        assert!(
15498            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
15499            "declared_servico_slots must NOT push \
15500             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
15501             empty — the author-omitted arm must route through the \
15502             accessor's empty-slice return unchanged (got \
15503             slots={slots:?})",
15504        );
15505    }
15506
15507    #[test]
15508    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
15509        // Composition pin: [`crate::render::servico_m2_overlay`]'s
15510        // per-`:upgrade-from` M2 overlay emit arm must key off
15511        // [`Caixa::upgrade_from`], not the raw
15512        // `!caixa.upgrade_from.is_empty()` presence gate + the
15513        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
15514        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
15515        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
15516        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
15517        // sequence in the overlay (the emitter fans onto the serde
15518        // slice-serialization), and a `Caixa { upgrade_from: vec![],
15519        // .. }` must omit the key entirely (the empty-slice
15520        // partition — the `!.is_empty()` outer gate elides the key
15521        // when the author omitted the slot). The pair jointly pins
15522        // the accessor + M2 overlay emitter composition: any future
15523        // silent detour that had the accessor return a fresh-cloned
15524        // `Vec<UpgradeFromEntry>` copy would silently break the
15525        // reference-identity pin the peer per-entry
15526        // `serde_yaml::to_value(caixa.upgrade_from())` projection
15527        // reads from — the projection would clone once per accessor
15528        // call instead of borrowing the storage buffer verbatim.
15529        //
15530        // Peer of the sibling
15531        // `servico_m2_overlay_limits_arm_routes_through_accessor`
15532        // (b2bd9d7) and
15533        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
15534        // (35d8b52) composition pins on the sibling `:limits` /
15535        // `:behavior` outer-`Option<&Composite>` arms — same "the
15536        // M2 overlay emitter must route through the substrate-
15537        // primitive typed dispatch" discipline extended onto the
15538        // third M2 Servico-runtime slot axis, closing the overlay
15539        // emitter's routing invariant on every M2 arm.
15540        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
15541        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15542        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
15543            from: "0.0.1".into(),
15544            instructions: vec![UpgradeInstruction::Restart],
15545        }]);
15546        let overlay = servico_m2_overlay(&c).unwrap();
15547        assert!(
15548            overlay.contains_key(M2_KEY_UPGRADE_FROM),
15549            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
15550             `:upgrade-from` is non-empty — the accessor and the M2 \
15551             overlay emitter must route through the same substrate- \
15552             primitive typed dispatch on the outer :upgrade-from \
15553             slice (got overlay={overlay:?})",
15554        );
15555        let c = caixa_with_upgrade_from(vec![]);
15556        let overlay = servico_m2_overlay(&c).unwrap();
15557        assert!(
15558            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
15559            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
15560             `:upgrade-from` is empty — the empty-slice partition \
15561             must route through the accessor's empty-slice return \
15562             unchanged (got overlay={overlay:?})",
15563        );
15564    }
15565
15566    #[test]
15567    fn upgrade_from_projects_slice_by_borrow() {
15568        // The by-borrow pin: [`Caixa::upgrade_from`] returns
15569        // `&[UpgradeFromEntry]` by borrow — the returned slice
15570        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
15571        // the `:upgrade-from` slot and the accessor must not clone
15572        // the backing `Vec` on every call. Peer of the sibling
15573        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
15574        // (`autores_projects_slice_by_borrow` b5d813f,
15575        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
15576        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15577        // `exe_projects_slice_by_borrow` 65d9527,
15578        // `servicos_projects_slice_by_borrow` 611f78b,
15579        // `deps_projects_slice_by_borrow` ad34b4e,
15580        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
15581        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
15582        // axes — extended here to the first outer-`Caixa`
15583        // composite-element `&[Composite]` axis: the accessor's
15584        // returned slice must borrow from `&self` (the returned
15585        // reference's lifetime is tied to `&self`), and calling the
15586        // accessor twice on the same [`Caixa`] must yield slices
15587        // that are pointer-equal (the underlying byte-buffer is the
15588        // storage `Vec`'s allocation, not a fresh copy) as well as
15589        // value-equal (idempotent, no side effects on `&self`).
15590        //
15591        // Pins against a future silent detour that returned an owned
15592        // `Vec<UpgradeFromEntry>` (which would type-check but
15593        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
15594        // return (which would leak the backing `Vec`'s
15595        // grow/push/reserve surface no downstream consumer reaches
15596        // for), or a one-arm-only accessor that returned a
15597        // saturating value on some sentinel input.
15598        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15599        for upgrade_from in [
15600            vec![],
15601            vec![UpgradeFromEntry {
15602                from: "0.0.1".into(),
15603                instructions: vec![UpgradeInstruction::Restart],
15604            }],
15605            vec![
15606                UpgradeFromEntry {
15607                    from: "0.0.1".into(),
15608                    instructions: vec![UpgradeInstruction::Restart],
15609                },
15610                UpgradeFromEntry {
15611                    from: "0.0.2".into(),
15612                    instructions: vec![UpgradeInstruction::SoftPurge {
15613                        module: "demo".into(),
15614                    }],
15615                },
15616            ],
15617        ] {
15618            let c = caixa_with_upgrade_from(upgrade_from.clone());
15619            let first = c.upgrade_from();
15620            let second = c.upgrade_from();
15621            assert_eq!(
15622                first, second,
15623                "Caixa::upgrade_from must be idempotent — two \
15624                 successive calls on the same &self must return the \
15625                 same &[UpgradeFromEntry]",
15626            );
15627            assert_eq!(
15628                first.as_ptr(),
15629                second.as_ptr(),
15630                "Caixa::upgrade_from must borrow the underlying \
15631                 Vec<UpgradeFromEntry> storage — two successive calls \
15632                 must return slices with the same backing pointer (a \
15633                 fresh Vec<UpgradeFromEntry> clone would change the \
15634                 pointer on every call)",
15635            );
15636            assert_eq!(
15637                first,
15638                upgrade_from.as_slice(),
15639                "Caixa::upgrade_from must return :upgrade-from \
15640                 verbatim by borrow — got {first:?}, expected \
15641                 {upgrade_from:?}",
15642            );
15643        }
15644    }
15645
15646    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
15647
15648    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
15649        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15650        c.children = children;
15651        c
15652    }
15653
15654    #[test]
15655    fn children_returns_children_slice_verbatim_across_permutations() {
15656        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
15657        // outer-composite `&[ChildSpec]`-return slice-shape pin:
15658        // [`Caixa::children`] must return the `:children` typed
15659        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
15660        // the same backing buffer the raw `self.children.as_slice()`
15661        // field access borrows from, element-equal across every
15662        // representative fixture in the accept-set — `[]` (the "no
15663        // static children declared" arm every non-`Supervisor`-kind
15664        // `defcaixa` carries by `#[serde(default)]` and every
15665        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
15666        // a canonical single-child `Permanent` fixture (the shape
15667        // most `OneForOne` supervisors carry — a single long-running
15668        // worker child), a canonical multi-child list carrying every
15669        // typed restart-policy variant (`Permanent` / `Transient` /
15670        // `Temporary`), and a past-the-guard sentinel — a duplicate
15671        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
15672        // ([`crate::SupervisorSpec::validate`] rejects through
15673        // `DuplicateChildNome { nome: "w" }` but the accessor must
15674        // ship the raw slot verbatim so struct-literal fixtures
15675        // continue to expose the duplicate at the accessor boundary).
15676        //
15677        // Pins against a future silent detour that returned an owned
15678        // `Vec<ChildSpec>` (which would type-check but silently clone
15679        // on every accessor call, breaking the zero-cost projection
15680        // every peer sibling slice accessor carries), a `[dup, dup] →
15681        // [dup]` dedup collapse (which would silently absorb the
15682        // `DuplicateChildNome` refusal case at the accessor boundary
15683        // and the [`crate::StandardLayout::verify`] cross-child gate
15684        // would silently accept a struct-literal `Caixa` carrying the
15685        // drift), a reference to an operator-resolved overlay (the
15686        // future per-cluster `:children-overrides` slot — its
15687        // resolution must land at exactly this accessor body, not
15688        // silently divert the raw slot away from a second consumer),
15689        // or an axis-shuffled projection (a future detour that
15690        // reordered children through the accessor would silently
15691        // split the paired [`crate::StandardLayout::verify`] per-
15692        // supervisor gate's traversal input from the peer
15693        // [`Self::supervisor_view`] fold-in path's clone-order input,
15694        // since the OTP `RestForOne` restart strategy dispatches on
15695        // declared child order and axis reordering would silently
15696        // split the operator's per-cluster restart-fan-out order
15697        // from the caixa.lisp source-order).
15698        //
15699        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
15700        // accessor pin on the substrate primitive for M2 / M3 typed-
15701        // slot vec-carry axes — folds on the outer-`Caixa`
15702        // `&[Composite]` composite-slice sub-family the sibling
15703        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
15704        // (2a1f907) pin opened, peer at the outer altitude of the
15705        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
15706        // (bc92bce) accessor on the same OTP-supervisor static-child-
15707        // list axis.
15708        use crate::supervisor::{ChildSpec, RestartPolicy};
15709        let fixtures: Vec<Vec<ChildSpec>> = vec![
15710            vec![],
15711            vec![ChildSpec {
15712                caixa: "worker".into(),
15713                versao: "^0.1".into(),
15714                restart: RestartPolicy::Permanent,
15715            }],
15716            vec![
15717                ChildSpec {
15718                    caixa: "worker-a".into(),
15719                    versao: "^0.1".into(),
15720                    restart: RestartPolicy::Permanent,
15721                },
15722                ChildSpec {
15723                    caixa: "worker-b".into(),
15724                    versao: "^0.1".into(),
15725                    restart: RestartPolicy::Transient,
15726                },
15727                ChildSpec {
15728                    caixa: "worker-c".into(),
15729                    versao: "^0.1".into(),
15730                    restart: RestartPolicy::Temporary,
15731                },
15732            ],
15733            vec![
15734                ChildSpec {
15735                    caixa: "w".into(),
15736                    versao: "^0.1".into(),
15737                    restart: RestartPolicy::Permanent,
15738                },
15739                ChildSpec {
15740                    caixa: "w".into(),
15741                    versao: "^0.1".into(),
15742                    restart: RestartPolicy::Permanent,
15743                },
15744            ],
15745        ];
15746        for children in fixtures {
15747            let c = caixa_with_children(children.clone());
15748            assert_eq!(
15749                c.children(),
15750                children.as_slice(),
15751                "Caixa::children must return :children verbatim \
15752                 (got {:?}, expected {children:?})",
15753                c.children(),
15754            );
15755            assert_eq!(
15756                c.children(),
15757                c.children.as_slice(),
15758                "Caixa::children must element-equal the raw \
15759                 `self.children.as_slice()` field access across \
15760                 every value in the Vec<ChildSpec> accept-set",
15761            );
15762            assert_eq!(
15763                c.children().is_empty(),
15764                c.children.is_empty(),
15765                "Caixa::children().is_empty() must byte-equal \
15766                 self.children.is_empty() — a presence-bit drift \
15767                 would silently split the paired \
15768                 Caixa::declared_supervisor_slots supervisor-tree \
15769                 declared-slot enumerator's presence probe from the \
15770                 peer Caixa::supervisor_view typed-view composer's \
15771                 fold-in path",
15772            );
15773        }
15774    }
15775
15776    #[test]
15777    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
15778        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15779        // `:children` presence-probe arm must key off
15780        // [`Caixa::children`], not the raw
15781        // `!self.children.is_empty()` field-probe. Structurally: a
15782        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
15783        // "^0.1", restart: Permanent }], .. }` must push
15784        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
15785        // (the presence bit is non-empty, so the supervisor-tree
15786        // kind-coherence gate must surface the slot as "declared"),
15787        // and a `Caixa { children: vec![], .. }` must NOT push the
15788        // label (the "author omitted the slot entirely" arm — the
15789        // empty-slice partition the serde-default folds onto). The
15790        // pair jointly pins the accessor + declared-slot enumerator
15791        // composition: any future silent detour that had the accessor
15792        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
15793        // "__reserved__")` projection) would silently absorb the
15794        // "declared but degenerate" arm at the accessor boundary and
15795        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
15796        // kind-coherence gate would silently accept a struct-literal
15797        // `Caixa` carrying the drift.
15798        //
15799        // Peer of the sibling
15800        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
15801        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
15802        // same "the enumerator gate must route through the substrate-
15803        // primitive typed dispatch" discipline extended onto the
15804        // supervisor-tree `:children` composite-slice arm.
15805        use crate::supervisor::{ChildSpec, RestartPolicy};
15806        let c = caixa_with_children(vec![ChildSpec {
15807            caixa: "w".into(),
15808            versao: "^0.1".into(),
15809            restart: RestartPolicy::Permanent,
15810        }]);
15811        let slots = c.declared_supervisor_slots();
15812        assert!(
15813            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
15814            "declared_supervisor_slots must push \
15815             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
15816             non-empty — the accessor and the enumerator gate must \
15817             route through the same substrate-primitive typed \
15818             dispatch on the outer :children presence bit (got \
15819             slots={slots:?})",
15820        );
15821        let c = caixa_with_children(vec![]);
15822        let slots = c.declared_supervisor_slots();
15823        assert!(
15824            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
15825            "declared_supervisor_slots must NOT push \
15826             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
15827             empty — the author-omitted arm must route through the \
15828             accessor's empty-slice return unchanged (got \
15829             slots={slots:?})",
15830        );
15831    }
15832
15833    #[test]
15834    fn supervisor_view_children_arm_routes_through_accessor() {
15835        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
15836        // fold-in arm must key off [`Caixa::children`], not the raw
15837        // `self.children.clone()` field-clone. Structurally: a `Caixa {
15838        // kind: Supervisor, estrategia: Some(OneForOne), children:
15839        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
15840        // per-child list through the accessor into the typed
15841        // [`SupervisorSpec`] view's `children` field verbatim — every
15842        // entry the accessor surfaces must land in the view's
15843        // `children` slot in the same order. The pair jointly pins the
15844        // accessor + view-composer composition: any future silent
15845        // detour that had the accessor return a fresh-cloned
15846        // `Vec<ChildSpec>` copy would silently break the reference-
15847        // identity pin the peer `supervisor_view` fold-in path reads
15848        // from — the fold would clone once more per accessor call
15849        // instead of borrowing the storage buffer verbatim once.
15850        //
15851        // Peer of the sibling
15852        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
15853        // family) composition pin on the peer kind-gate arm — same
15854        // "the view composer must route through the substrate-
15855        // primitive typed dispatch" discipline extended onto the
15856        // per-`:children` fold-in arm, closing the supervisor-view
15857        // composer's routing invariant on the composite-slice input.
15858        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
15859        let mut c = caixa_with_children(vec![
15860            ChildSpec {
15861                caixa: "worker-a".into(),
15862                versao: "^0.1".into(),
15863                restart: RestartPolicy::Permanent,
15864            },
15865            ChildSpec {
15866                caixa: "worker-b".into(),
15867                versao: "^0.1".into(),
15868                restart: RestartPolicy::Transient,
15869            },
15870        ]);
15871        c.kind = crate::CaixaKind::Supervisor;
15872        c.estrategia = Some(RestartStrategy::OneForOne);
15873        let view = c
15874            .supervisor_view()
15875            .expect("Supervisor kind must produce a supervisor_view");
15876        assert_eq!(
15877            view.children(),
15878            c.children(),
15879            "supervisor_view must fold Caixa::children verbatim into \
15880             SupervisorSpec::children — the accessor and the view \
15881             composer must route through the same substrate-primitive \
15882             typed dispatch on the outer :children slice (got view \
15883             children={:?}, expected {:?})",
15884            view.children(),
15885            c.children(),
15886        );
15887    }
15888
15889    #[test]
15890    fn children_projects_slice_by_borrow() {
15891        // The by-borrow pin: [`Caixa::children`] returns
15892        // `&[ChildSpec]` by borrow — the returned slice borrows the
15893        // underlying `Vec<ChildSpec>` storage of the `:children` slot
15894        // and the accessor must not clone the backing `Vec` on every
15895        // call. Peer of the sibling outer top-level [`Caixa`]
15896        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
15897        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
15898        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15899        // `exe_projects_slice_by_borrow` 65d9527,
15900        // `servicos_projects_slice_by_borrow` 611f78b,
15901        // `deps_projects_slice_by_borrow` ad34b4e,
15902        // `deps_dev_projects_slice_by_borrow` f7fd81e,
15903        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
15904        // sibling outer top-level [`Caixa`] scalar-element and
15905        // composite-element `&[T]` axes — folds on the outer-`Caixa`
15906        // composite-element `&[Composite]` axis: the accessor's
15907        // returned slice must borrow from `&self` (the returned
15908        // reference's lifetime is tied to `&self`), and calling the
15909        // accessor twice on the same [`Caixa`] must yield slices
15910        // that are pointer-equal (the underlying byte-buffer is the
15911        // storage `Vec`'s allocation, not a fresh copy) as well as
15912        // value-equal (idempotent, no side effects on `&self`).
15913        //
15914        // Pins against a future silent detour that returned an owned
15915        // `Vec<ChildSpec>` (which would type-check but silently clone
15916        // on every call), a `&Vec<ChildSpec>` return (which would leak
15917        // the backing `Vec`'s grow/push/reserve surface no downstream
15918        // consumer reaches for), or a one-arm-only accessor that
15919        // returned a saturating value on some sentinel input.
15920        use crate::supervisor::{ChildSpec, RestartPolicy};
15921        for children in [
15922            vec![],
15923            vec![ChildSpec {
15924                caixa: "w".into(),
15925                versao: "^0.1".into(),
15926                restart: RestartPolicy::Permanent,
15927            }],
15928            vec![
15929                ChildSpec {
15930                    caixa: "worker-a".into(),
15931                    versao: "^0.1".into(),
15932                    restart: RestartPolicy::Permanent,
15933                },
15934                ChildSpec {
15935                    caixa: "worker-b".into(),
15936                    versao: "^0.1".into(),
15937                    restart: RestartPolicy::Transient,
15938                },
15939            ],
15940        ] {
15941            let c = caixa_with_children(children.clone());
15942            let first = c.children();
15943            let second = c.children();
15944            assert_eq!(
15945                first, second,
15946                "Caixa::children must be idempotent — two successive \
15947                 calls on the same &self must return the same \
15948                 &[ChildSpec]",
15949            );
15950            assert_eq!(
15951                first.as_ptr(),
15952                second.as_ptr(),
15953                "Caixa::children must borrow the underlying \
15954                 Vec<ChildSpec> storage — two successive calls must \
15955                 return slices with the same backing pointer (a fresh \
15956                 Vec<ChildSpec> clone would change the pointer on \
15957                 every call)",
15958            );
15959            assert_eq!(
15960                first,
15961                children.as_slice(),
15962                "Caixa::children must return :children verbatim by \
15963                 borrow — got {first:?}, expected {children:?}",
15964            );
15965        }
15966    }
15967
15968    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
15969
15970    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
15971        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15972        c.kind = CaixaKind::Aplicacao;
15973        c.membros = membros;
15974        c
15975    }
15976
15977    #[test]
15978    fn membros_returns_membros_slice_verbatim_across_permutations() {
15979        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
15980        // composite `&[Membro]`-return slice-shape pin:
15981        // [`Caixa::membros`] must return the `:membros` typed
15982        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
15983        // same backing buffer the raw `self.membros.as_slice()` field
15984        // access borrows from, element-equal across every
15985        // representative fixture in the accept-set — `[]` (the "no
15986        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
15987        // carries by `#[serde(default)]` and every partially-authored
15988        // Aplicacao carries before the
15989        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
15990        // canonical single-member fixture (the shape a minimal
15991        // Aplicacao carries — one Servico wrapping one contained
15992        // computation), a canonical multi-member list carrying three
15993        // distinct entries (the canonical checkout-shape Aplicacao —
15994        // cart / pricing / auth — every canonical example carries), and
15995        // a past-the-guard sentinel — a duplicate `:caixa`
15996        // `[("cart", ...), ("cart", ...)]` entry pair
15997        // ([`crate::AplicacaoSpec::validate`] rejects through
15998        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
15999        // the raw slot verbatim so struct-literal fixtures continue to
16000        // expose the duplicate at the accessor boundary).
16001        //
16002        // Pins against a future silent detour that returned an owned
16003        // `Vec<Membro>` (which would type-check but silently clone on
16004        // every accessor call, breaking the zero-cost projection every
16005        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
16006        // dedup collapse (which would silently absorb the
16007        // `DuplicateMembro` refusal case at the accessor boundary and
16008        // the [`crate::StandardLayout::verify`] cross-member gate would
16009        // silently accept a struct-literal `Caixa` carrying the drift),
16010        // a reference to an operator-resolved overlay (the future per-
16011        // cluster `:membros-overrides` slot — its resolution must land
16012        // at exactly this accessor body, not silently divert the raw
16013        // slot away from a second consumer), or an axis-shuffled
16014        // projection (a future detour that reordered members through
16015        // the accessor would silently split the paired
16016        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
16017        // traversal input from the peer [`Self::aplicacao_view`] fold-
16018        // in path's clone-order input, since the canonical `:contratos`
16019        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
16020        // read the member set through the same slice).
16021        //
16022        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
16023        // accessor pin on the substrate primitive for M2 / M3 typed-
16024        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
16025        // arm of the `&[Composite]` composite-slice sub-family the
16026        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16027        // (2a1f907) and
16028        // `children_returns_children_slice_verbatim_across_permutations`
16029        // (c17b51e) pins opened, peer at the outer altitude of the
16030        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
16031        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
16032        // list axis.
16033        use crate::aplicacao::Membro;
16034        let fixtures: Vec<Vec<Membro>> = vec![
16035            vec![],
16036            vec![Membro {
16037                caixa: "cart".into(),
16038                versao: "^0.1".into(),
16039            }],
16040            vec![
16041                Membro {
16042                    caixa: "cart".into(),
16043                    versao: "^0.1".into(),
16044                },
16045                Membro {
16046                    caixa: "pricing".into(),
16047                    versao: "^0.2".into(),
16048                },
16049                Membro {
16050                    caixa: "auth".into(),
16051                    versao: "^1.0".into(),
16052                },
16053            ],
16054            vec![
16055                Membro {
16056                    caixa: "cart".into(),
16057                    versao: "^0.1".into(),
16058                },
16059                Membro {
16060                    caixa: "cart".into(),
16061                    versao: "^0.1".into(),
16062                },
16063            ],
16064        ];
16065        for membros in fixtures {
16066            let c = caixa_aplicacao_with_membros(membros.clone());
16067            assert_eq!(
16068                c.membros(),
16069                membros.as_slice(),
16070                "Caixa::membros must return :membros verbatim \
16071                 (got {:?}, expected {membros:?})",
16072                c.membros(),
16073            );
16074            assert_eq!(
16075                c.membros(),
16076                c.membros.as_slice(),
16077                "Caixa::membros must element-equal the raw \
16078                 `self.membros.as_slice()` field access across every \
16079                 value in the Vec<Membro> accept-set",
16080            );
16081            assert_eq!(
16082                c.membros().is_empty(),
16083                c.membros.is_empty(),
16084                "Caixa::membros().is_empty() must byte-equal \
16085                 self.membros.is_empty() — a presence-bit drift would \
16086                 silently split the paired Caixa::declared_mesh_slots \
16087                 mesh declared-slot enumerator's presence probe from \
16088                 the peer Caixa::aplicacao_view typed-view composer's \
16089                 fold-in path",
16090            );
16091        }
16092    }
16093
16094    #[test]
16095    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
16096        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
16097        // presence-probe arm must key off [`Caixa::membros`], not the
16098        // raw `!self.membros.is_empty()` field-probe. Structurally: a
16099        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
16100        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
16101        // declared-slot list (the presence bit is non-empty, so the
16102        // mesh kind-coherence gate must surface the slot as
16103        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
16104        // push the label (the "author omitted the slot entirely" arm
16105        // — the empty-slice partition the serde-default folds onto).
16106        // The pair jointly pins the accessor + declared-slot
16107        // enumerator composition: any future silent detour that had
16108        // the accessor collapse `[Membro { .. }]` to `[]` (a
16109        // `.filter(|m| m.nome() != "__reserved__")` projection) would
16110        // silently absorb the "declared but degenerate" arm at the
16111        // accessor boundary and the
16112        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16113        // coherence gate would silently accept a struct-literal
16114        // `Caixa` carrying the drift.
16115        //
16116        // Peer of the sibling
16117        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16118        // (2a1f907) and
16119        // `declared_supervisor_slots_children_arm_routes_through_accessor`
16120        // (c17b51e) composition pins on the M2 `:upgrade-from` /
16121        // `:children` composite-slice arms — same "the enumerator gate
16122        // must route through the substrate-primitive typed dispatch"
16123        // discipline extended onto the M3 `:membros` composite-slice
16124        // arm, opening the M3 arm of the declared-slot enumerator's
16125        // routing invariant.
16126        use crate::aplicacao::Membro;
16127        let c = caixa_aplicacao_with_membros(vec![Membro {
16128            caixa: "cart".into(),
16129            versao: "^0.1".into(),
16130        }]);
16131        let slots = c.declared_mesh_slots();
16132        assert!(
16133            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
16134            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
16135             `:membros` is non-empty — the accessor and the enumerator \
16136             gate must route through the same substrate-primitive \
16137             typed dispatch on the outer :membros presence bit (got \
16138             slots={slots:?})",
16139        );
16140        let c = caixa_aplicacao_with_membros(vec![]);
16141        let slots = c.declared_mesh_slots();
16142        assert!(
16143            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
16144            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
16145             when `:membros` is empty — the author-omitted arm must \
16146             route through the accessor's empty-slice return unchanged \
16147             (got slots={slots:?})",
16148        );
16149    }
16150
16151    #[test]
16152    fn aplicacao_view_membros_arm_routes_through_accessor() {
16153        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
16154        // fold-in arm must key off [`Caixa::membros`], not the raw
16155        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
16156        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
16157        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
16158        // member list through the accessor into the typed
16159        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
16160        // every entry the accessor surfaces must land in the view's
16161        // `membros` slot in the same order. The pair jointly pins the
16162        // accessor + view-composer composition: any future silent
16163        // detour that had the accessor return a fresh-cloned
16164        // `Vec<Membro>` copy would silently break the reference-
16165        // identity pin the peer `aplicacao_view` fold-in path reads
16166        // from — the fold would clone once more per accessor call
16167        // instead of borrowing the storage buffer verbatim once.
16168        //
16169        // Peer of the sibling
16170        // `aplicacao_view_politicas_arm_folds_through_accessor`
16171        // (5d23d29) /
16172        // `aplicacao_view_placement_arm_folds_through_accessor`
16173        // (4fb8074) /
16174        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
16175        // composition pins on the M3 `:politicas` / `:placement` /
16176        // `:entrada` outer-`Option<&Composite>` arms — extended here to
16177        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
16178        // closing the aplicacao-view composer's routing invariant on
16179        // the composite-slice input.
16180        use crate::aplicacao::Membro;
16181        let c = caixa_aplicacao_with_membros(vec![
16182            Membro {
16183                caixa: "cart".into(),
16184                versao: "^0.1".into(),
16185            },
16186            Membro {
16187                caixa: "pricing".into(),
16188                versao: "^0.2".into(),
16189            },
16190        ]);
16191        let view = c
16192            .aplicacao_view()
16193            .expect("Aplicacao kind must produce an aplicacao_view");
16194        assert_eq!(
16195            view.membros(),
16196            c.membros(),
16197            "aplicacao_view must fold Caixa::membros verbatim into \
16198             AplicacaoSpec::membros — the accessor and the view \
16199             composer must route through the same substrate-primitive \
16200             typed dispatch on the outer :membros slice (got view \
16201             membros={:?}, expected {:?})",
16202            view.membros(),
16203            c.membros(),
16204        );
16205    }
16206
16207    #[test]
16208    fn membros_projects_slice_by_borrow() {
16209        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
16210        // borrow — the returned slice borrows the underlying
16211        // `Vec<Membro>` storage of the `:membros` slot and the
16212        // accessor must not clone the backing `Vec` on every call.
16213        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
16214        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
16215        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16216        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16217        // `exe_projects_slice_by_borrow` 65d9527,
16218        // `servicos_projects_slice_by_borrow` 611f78b,
16219        // `deps_projects_slice_by_borrow` ad34b4e,
16220        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16221        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
16222        // `children_projects_slice_by_borrow` c17b51e) on the sibling
16223        // outer top-level [`Caixa`] scalar-element and composite-
16224        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
16225        // slot composite-element `&[Composite]` axis: the accessor's
16226        // returned slice must borrow from `&self` (the returned
16227        // reference's lifetime is tied to `&self`), and calling the
16228        // accessor twice on the same [`Caixa`] must yield slices that
16229        // are pointer-equal (the underlying byte-buffer is the storage
16230        // `Vec`'s allocation, not a fresh copy) as well as value-equal
16231        // (idempotent, no side effects on `&self`).
16232        //
16233        // Pins against a future silent detour that returned an owned
16234        // `Vec<Membro>` (which would type-check but silently clone on
16235        // every call), a `&Vec<Membro>` return (which would leak the
16236        // backing `Vec`'s grow/push/reserve surface no downstream
16237        // consumer reaches for), or a one-arm-only accessor that
16238        // returned a saturating value on some sentinel input.
16239        use crate::aplicacao::Membro;
16240        for membros in [
16241            vec![],
16242            vec![Membro {
16243                caixa: "cart".into(),
16244                versao: "^0.1".into(),
16245            }],
16246            vec![
16247                Membro {
16248                    caixa: "cart".into(),
16249                    versao: "^0.1".into(),
16250                },
16251                Membro {
16252                    caixa: "pricing".into(),
16253                    versao: "^0.2".into(),
16254                },
16255            ],
16256        ] {
16257            let c = caixa_aplicacao_with_membros(membros.clone());
16258            let first = c.membros();
16259            let second = c.membros();
16260            assert_eq!(
16261                first, second,
16262                "Caixa::membros must be idempotent — two successive \
16263                 calls on the same &self must return the same &[Membro]",
16264            );
16265            assert_eq!(
16266                first.as_ptr(),
16267                second.as_ptr(),
16268                "Caixa::membros must borrow the underlying Vec<Membro> \
16269                 storage — two successive calls must return slices with \
16270                 the same backing pointer (a fresh Vec<Membro> clone \
16271                 would change the pointer on every call)",
16272            );
16273            assert_eq!(
16274                first,
16275                membros.as_slice(),
16276                "Caixa::membros must return :membros verbatim by borrow \
16277                 — got {first:?}, expected {membros:?}",
16278            );
16279        }
16280    }
16281
16282    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
16283
16284    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
16285        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16286        c.kind = CaixaKind::Aplicacao;
16287        c.contratos = contratos;
16288        c
16289    }
16290
16291    fn contrato_http_for_test(
16292        de: &str,
16293        para: &str,
16294        endpoint: &str,
16295    ) -> crate::aplicacao::WitContract {
16296        crate::aplicacao::WitContract {
16297            de: de.into(),
16298            para: para.into(),
16299            wit: "wasi:http/proxy".into(),
16300            endpoint: Some(endpoint.into()),
16301            subject: None,
16302            slot: None,
16303        }
16304    }
16305
16306    #[test]
16307    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
16308        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
16309        // composite `&[WitContract]`-return slice-shape pin:
16310        // [`Caixa::contratos`] must return the `:contratos` typed
16311        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
16312        // over the same backing buffer the raw
16313        // `self.contratos.as_slice()` field access borrows from,
16314        // element-equal across every representative fixture in the
16315        // accept-set — `[]` (the "no contracts declared" arm every
16316        // non-`Aplicacao`-kind `defcaixa` carries by
16317        // `#[serde(default)]` and every leaf-Aplicacao with a single
16318        // member carries), a canonical single-edge fixture (the
16319        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
16320        // edge), and a canonical multi-edge fixture with three distinct
16321        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
16322        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
16323        //
16324        // Pins against a future silent detour that returned an owned
16325        // `Vec<WitContract>` (which would type-check but silently clone
16326        // on every accessor call, breaking the zero-cost projection
16327        // every peer sibling slice accessor carries), an axis-shuffled
16328        // projection (a future detour that reordered edges through the
16329        // accessor would silently split the paired
16330        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
16331        // traversal input from the peer [`Self::aplicacao_view`] fold-
16332        // in path's clone-order input, since every canonical
16333        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
16334        // seed dispatch reads the edge set through the same slice),
16335        // or a reference to an operator-resolved overlay (the future
16336        // per-cluster `:contratos-overrides` slot — its resolution
16337        // must land at exactly this accessor body, not silently divert
16338        // the raw slot away from a second consumer).
16339        //
16340        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
16341        // accessor pin on the substrate primitive for M2 / M3 typed-
16342        // slot vec-carry axes — closes the outer-`Caixa`
16343        // `&[Composite]` composite-slice sub-family the sibling M2
16344        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16345        // (2a1f907) and
16346        // `children_returns_children_slice_verbatim_across_permutations`
16347        // (c17b51e) pins opened and the M3
16348        // `membros_returns_membros_slice_verbatim_across_permutations`
16349        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
16350        // slot arm of the composite-slice sub-family. Peer at the outer
16351        // altitude of the closed inner-
16352        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
16353        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
16354        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
16355            vec![],
16356            vec![contrato_http_for_test("cart", "catalog", "/items")],
16357            vec![
16358                contrato_http_for_test("cart", "catalog", "/items"),
16359                contrato_http_for_test("cart", "pricing", "/price"),
16360                contrato_http_for_test("cart", "auth", "/whoami"),
16361            ],
16362        ];
16363        for contratos in fixtures {
16364            let c = caixa_aplicacao_with_contratos(contratos.clone());
16365            assert_eq!(
16366                c.contratos(),
16367                contratos.as_slice(),
16368                "Caixa::contratos must return :contratos verbatim \
16369                 (got {:?}, expected {contratos:?})",
16370                c.contratos(),
16371            );
16372            assert_eq!(
16373                c.contratos(),
16374                c.contratos.as_slice(),
16375                "Caixa::contratos must element-equal the raw \
16376                 `self.contratos.as_slice()` field access across every \
16377                 value in the Vec<WitContract> accept-set",
16378            );
16379            assert_eq!(
16380                c.contratos().is_empty(),
16381                c.contratos.is_empty(),
16382                "Caixa::contratos().is_empty() must byte-equal \
16383                 self.contratos.is_empty() — a presence-bit drift would \
16384                 silently split the paired Caixa::declared_mesh_slots \
16385                 mesh declared-slot enumerator's presence probe from \
16386                 the peer Caixa::aplicacao_view typed-view composer's \
16387                 fold-in path",
16388            );
16389        }
16390    }
16391
16392    #[test]
16393    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
16394        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
16395        // presence-probe arm must key off [`Caixa::contratos`], not the
16396        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
16397        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
16398        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
16399        // presence bit is non-empty, so the mesh kind-coherence gate
16400        // must surface the slot as "declared"), and a `Caixa {
16401        // contratos: vec![], .. }` must NOT push the label (the "author
16402        // omitted the slot entirely" arm — the empty-slice partition
16403        // the serde-default folds onto). The pair jointly pins the
16404        // accessor + declared-slot enumerator composition: any future
16405        // silent detour that had the accessor collapse
16406        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
16407        // "__reserved__")` projection) would silently absorb the
16408        // "declared but degenerate" arm at the accessor boundary and
16409        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16410        // coherence gate would silently accept a struct-literal
16411        // `Caixa` carrying the drift.
16412        //
16413        // Peer of the sibling
16414        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16415        // (2a1f907),
16416        // `declared_supervisor_slots_children_arm_routes_through_accessor`
16417        // (c17b51e), and
16418        // `declared_mesh_slots_membros_arm_routes_through_accessor`
16419        // (0f26987) composition pins on the M2 `:upgrade-from` /
16420        // `:children` / M3 `:membros` composite-slice arms — same "the
16421        // enumerator gate must route through the substrate-primitive
16422        // typed dispatch" discipline extended onto the M3 `:contratos`
16423        // composite-slice arm, closing the M3 mesh-slot arm of the
16424        // declared-slot enumerator's routing invariant on the
16425        // composite-slice inputs.
16426        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
16427            "cart", "catalog", "/items",
16428        )]);
16429        let slots = c.declared_mesh_slots();
16430        assert!(
16431            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
16432            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
16433             `:contratos` is non-empty — the accessor and the enumerator \
16434             gate must route through the same substrate-primitive \
16435             typed dispatch on the outer :contratos presence bit (got \
16436             slots={slots:?})",
16437        );
16438        let c = caixa_aplicacao_with_contratos(vec![]);
16439        let slots = c.declared_mesh_slots();
16440        assert!(
16441            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
16442            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
16443             when `:contratos` is empty — the author-omitted arm must \
16444             route through the accessor's empty-slice return unchanged \
16445             (got slots={slots:?})",
16446        );
16447    }
16448
16449    #[test]
16450    fn aplicacao_view_contratos_arm_routes_through_accessor() {
16451        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
16452        // fold-in arm must key off [`Caixa::contratos`], not the raw
16453        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
16454        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
16455        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
16456        // per-edge list through the accessor into the typed
16457        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
16458        // every entry the accessor surfaces must land in the view's
16459        // `contratos` slot in the same order. The pair jointly pins
16460        // the accessor + view-composer composition: a future silent
16461        // detour that had the accessor shuffle or drop an edge would
16462        // silently split the paired declared-slot enumerator's
16463        // presence bit from the typed-view composer's edge-list, a
16464        // two-consumer split at the enumerator and the view composer
16465        // far from the source `caixa.lisp`.
16466        //
16467        // Peer of the sibling
16468        // `aplicacao_view_membros_arm_routes_through_accessor`
16469        // (0f26987) composition pin on the M3 `:membros` outer-
16470        // `&[Composite]` composite-slice arm, closing the aplicacao-
16471        // view composer's routing invariant on the composite-slice
16472        // inputs at the outer altitude.
16473        let c = caixa_aplicacao_with_contratos(vec![
16474            contrato_http_for_test("cart", "catalog", "/items"),
16475            contrato_http_for_test("cart", "pricing", "/price"),
16476        ]);
16477        let view = c
16478            .aplicacao_view()
16479            .expect("Aplicacao kind must produce an aplicacao_view");
16480        assert_eq!(
16481            view.contratos(),
16482            c.contratos(),
16483            "aplicacao_view must fold Caixa::contratos verbatim into \
16484             AplicacaoSpec::contratos — the accessor and the view \
16485             composer must route through the same substrate-primitive \
16486             typed dispatch on the outer :contratos slice (got view \
16487             contratos={:?}, expected {:?})",
16488            view.contratos(),
16489            c.contratos(),
16490        );
16491    }
16492
16493    #[test]
16494    fn contratos_projects_slice_by_borrow() {
16495        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
16496        // by borrow — the returned slice borrows the underlying
16497        // `Vec<WitContract>` storage of the `:contratos` slot and the
16498        // accessor must not clone the backing `Vec` on every call.
16499        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
16500        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
16501        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16502        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16503        // `exe_projects_slice_by_borrow` 65d9527,
16504        // `servicos_projects_slice_by_borrow` 611f78b,
16505        // `deps_projects_slice_by_borrow` ad34b4e,
16506        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16507        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
16508        // `children_projects_slice_by_borrow` c17b51e,
16509        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
16510        // outer top-level [`Caixa`] scalar-element and composite-
16511        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
16512        // composite-element `&[Composite]` axis on the by-borrow pin:
16513        // the accessor's returned slice must borrow from `&self` (the
16514        // returned reference's lifetime is tied to `&self`), and
16515        // calling the accessor twice on the same [`Caixa`] must yield
16516        // slices that are pointer-equal (the underlying byte-buffer is
16517        // the storage `Vec`'s allocation, not a fresh copy) as well as
16518        // value-equal (idempotent, no side effects on `&self`).
16519        //
16520        // Pins against a future silent detour that returned an owned
16521        // `Vec<WitContract>` (which would type-check but silently clone
16522        // on every call), a `&Vec<WitContract>` return (which would
16523        // leak the backing `Vec`'s grow/push/reserve surface no
16524        // downstream consumer reaches for), or a one-arm-only accessor
16525        // that returned a saturating value on some sentinel input.
16526        for contratos in [
16527            vec![],
16528            vec![contrato_http_for_test("cart", "catalog", "/items")],
16529            vec![
16530                contrato_http_for_test("cart", "catalog", "/items"),
16531                contrato_http_for_test("cart", "pricing", "/price"),
16532            ],
16533        ] {
16534            let c = caixa_aplicacao_with_contratos(contratos.clone());
16535            let first = c.contratos();
16536            let second = c.contratos();
16537            assert_eq!(
16538                first, second,
16539                "Caixa::contratos must be idempotent — two successive \
16540                 calls on the same &self must return the same \
16541                 &[WitContract]",
16542            );
16543            assert_eq!(
16544                first.as_ptr(),
16545                second.as_ptr(),
16546                "Caixa::contratos must borrow the underlying \
16547                 Vec<WitContract> storage — two successive calls must \
16548                 return slices with the same backing pointer (a fresh \
16549                 Vec<WitContract> clone would change the pointer on \
16550                 every call)",
16551            );
16552            assert_eq!(
16553                first,
16554                contratos.as_slice(),
16555                "Caixa::contratos must return :contratos verbatim by \
16556                 borrow — got {first:?}, expected {contratos:?}",
16557            );
16558        }
16559    }
16560
16561    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
16562
16563    #[test]
16564    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
16565        // Load-bearing invariant: every multi-word top-level [`Caixa`]
16566        // serde-derived JSON key routes through a lifted `&'static str`
16567        // const. The Rust field names are `snake_case`
16568        // (`deps_dev` / `upgrade_from` / `max_restarts` /
16569        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
16570        // "camelCase")]` derive attribute maps each to the camelCase
16571        // byte-string the [`Caixa::to_lisp`] round-trip's
16572        // `serde_json::to_value(self)` step lands under before
16573        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
16574        // to the kebab-case `:deps-dev` / `:upgrade-from` /
16575        // `:max-restarts` / `:restart-window` author surface. Serialize
16576        // a fully-populated [`Caixa`] and pin that each canonical
16577        // byte-sequence appears verbatim in the JSON — a future
16578        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
16579        // verbatim-field-name flip at the derive attribute (any of
16580        // which would silently break every [`Caixa::to_lisp`]
16581        // round-trip and the future M4 operator-side manifest ingest's
16582        // `Value::get(<key>)` navigation) surfaces here as a build-time
16583        // test failure at `manifest.rs`, not as an apply-time
16584        // `.get(<stale-canonical-const>)` returning `None` far from the
16585        // derive-attr drift's commit. Same discipline the sibling
16586        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
16587        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
16588        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
16589        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
16590        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
16591        // [`UpgradeFromEntry`] per-entry axes — extended here to the
16592        // enclosing M0 [`Caixa`] top-level axis so the last of the four
16593        // multi-word top-level [`Caixa`] serde-derived JSON keys
16594        // (`depsDev`) joins the substrate's "one canonical byte-string
16595        // per typed serialized-key axis" discipline.
16596        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16597        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16598        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16599        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
16600        c.upgrade_from = vec![UpgradeFromEntry {
16601            from: "0.0.1".into(),
16602            instructions: vec![UpgradeInstruction::Restart],
16603        }];
16604        c.estrategia = Some(RestartStrategy::OneForOne);
16605        c.max_restarts = Some(3);
16606        c.restart_window = Some("60s".into());
16607        c.children = vec![ChildSpec {
16608            caixa: "child".into(),
16609            versao: "^0.1".into(),
16610            restart: RestartPolicy::Permanent,
16611        }];
16612        let json = serde_json::to_string(&c).unwrap();
16613        for key in [
16614            crate::render::CAIXA_KEY_DEPS_DEV,
16615            crate::render::M2_KEY_UPGRADE_FROM,
16616            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16617            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16618        ] {
16619            let quoted = format!("\"{key}\"");
16620            assert!(
16621                json.contains(&quoted),
16622                "serialized Caixa must carry the lifted top-level \
16623                 multi-word byte-sequence {quoted} verbatim in the JSON \
16624                 emission (got: {json})",
16625            );
16626        }
16627    }
16628
16629    #[test]
16630    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
16631        // Cross-axis drift-detection pin: a future collapse of the four
16632        // canonical [`Caixa`] top-level multi-word byte-strings onto the
16633        // same value (e.g. an accidental copy-paste flip of
16634        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
16635        // `"upgradeFrom"`) would silently reroute every downstream
16636        // `Value::get(<key>)` probe on one axis onto the sibling axis's
16637        // top-level entry and pass every propagation-probe test that
16638        // expected only the stale axis's value. Peer of the sibling
16639        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
16640        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
16641        let all = [
16642            crate::render::CAIXA_KEY_DEPS_DEV,
16643            crate::render::M2_KEY_UPGRADE_FROM,
16644            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16645            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16646        ];
16647        for (i, a) in all.iter().enumerate() {
16648            for b in all.iter().skip(i + 1) {
16649                assert_ne!(
16650                    a, b,
16651                    "Caixa top-level multi-word key consts must be \
16652                     pairwise-distinct canonical byte-sequences — got \
16653                     `{a}` == `{b}`",
16654                );
16655            }
16656        }
16657    }
16658
16659    #[test]
16660    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
16661        // Shape-pin: every [`Caixa`] top-level multi-word key const must
16662        // be a lowerCamelCase byte-sequence (no `snake_case`
16663        // underscores, no `kebab-case` hyphens, no leading colon, no
16664        // `PascalCase` leading capital, no whitespace / dots) — the
16665        // canonical shape the `#[serde(rename_all = "camelCase")]`
16666        // derive produces on [`Caixa`]. A future flip to a
16667        // non-camelCase attribute at the derive surfaces both here
16668        // (this test fails on the stale-constant shape) and at
16669        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16670        // (that test fails on the mismatch between const and derive).
16671        // Peer with `membro_key_consts_are_lower_camel_case_shape`
16672        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
16673        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
16674        for key in [
16675            crate::render::CAIXA_KEY_DEPS_DEV,
16676            crate::render::M2_KEY_UPGRADE_FROM,
16677            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16678            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16679        ] {
16680            assert!(
16681                !key.is_empty(),
16682                "Caixa top-level multi-word key const must be non-empty \
16683                 (got {key:?})"
16684            );
16685            let first = key.chars().next().unwrap();
16686            assert!(
16687                first.is_ascii_lowercase(),
16688                "Caixa top-level multi-word key const must lead with an \
16689                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
16690            );
16691            assert!(
16692                key.chars().all(|c| c.is_ascii_alphanumeric()),
16693                "Caixa top-level multi-word key const must be \
16694                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
16695                 whitespace (got {key:?})",
16696            );
16697        }
16698    }
16699
16700    #[test]
16701    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
16702        // Scalar-value pin: the byte-string the
16703        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
16704        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
16705        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
16706        // → `depsTest` matching a hypothetical per-test-target
16707        // vocabulary flip) lands as an edit to exactly one const AND
16708        // one derive attribute — the sibling
16709        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16710        // pin already ties the const to the derive attribute, so a
16711        // rebrand that touches only one side of the pair fails at
16712        // caixa-core build time. Same "scalar-value pin per const"
16713        // discipline the sibling
16714        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
16715        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
16716        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
16717        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
16718    }
16719
16720    #[test]
16721    fn caixa_key_deps_pins_canonical_byte_string() {
16722        // Scalar-value pin: the byte-string the
16723        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
16724        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
16725        // on the two-list dep-graph serialized-key axis — the sibling
16726        // pin covers the multi-word `deps_dev → depsDev` camelCase
16727        // arm, this pin covers the single-word `deps → deps` no-op arm
16728        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
16729        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
16730        // axis and the emitted JSON key equals the source-side field
16731        // name byte-for-byte). A future [`crate::Caixa::deps`] field
16732        // rename (`deps` → `dependencies` matching Cargo's verbatim
16733        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
16734        // hypothetical per-runtime-target vocabulary flip) OR an added
16735        // `#[serde(rename = "…")]` explicit override lands as an edit
16736        // to exactly one const AND one derive-attr / field name — the
16737        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
16738        // pin ties the const to the emitted JSON key, so a rebrand
16739        // that touches only one side of the pair fails at caixa-core
16740        // build time.
16741        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
16742    }
16743
16744    #[test]
16745    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
16746        // Load-bearing invariant on the single-word `deps` top-level
16747        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
16748        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
16749        // `serde_json::to_value(self)` step emits. Serialize a
16750        // populated [`Caixa`] whose `:deps` slot carries at least one
16751        // entry (the `#[serde(default)]` attribute on the field emits
16752        // an empty `[]` even without members, but a non-empty vec
16753        // additionally covers the codec's per-`Dep`-entry emission
16754        // path) and pin that `"deps"` appears verbatim in the JSON
16755        // emission — a future accidental `rename_all = "snake_case"` /
16756        // `"kebab-case"` flip at the derive attribute (or an added
16757        // `#[serde(rename = "…")]` explicit override on the field, or
16758        // a Rust field rename) would break every [`Caixa::to_lisp`]
16759        // round-trip and the future M4 operator-side manifest ingest's
16760        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
16761        // build-time test failure at `manifest.rs`, not as an
16762        // apply-time `.get(<stale-canonical-const>)` returning `None`
16763        // far from the drift's commit. Peer of the sibling
16764        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16765        // multi-word pin on the same M0 [`Caixa`] top-level
16766        // serialized-key axis, extended here to the single-word arm
16767        // the multi-word test's `rename_all = "camelCase"` sweep can't
16768        // reach (single-word `deps → deps` is a no-op the multi-word
16769        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
16770        // `\"restartWindow\"` byte-scan can never observe).
16771        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16772        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
16773        let json = serde_json::to_string(&c).unwrap();
16774        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
16775        assert!(
16776            json.contains(&quoted),
16777            "serialized Caixa must carry the lifted top-level `deps` \
16778             byte-sequence {quoted} verbatim in the JSON emission (got: \
16779             {json})",
16780        );
16781    }
16782
16783    #[test]
16784    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
16785        // Cross-axis drift-detection pin on the two-list dep-graph
16786        // renderer-side wire-key axis: a future collapse of the
16787        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
16788        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
16789        // same value (e.g. an accidental copy-paste flip of
16790        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
16791        // reroute every downstream `Value::get(<key>)` probe on one
16792        // axis onto the sibling axis's dep-list and pass every
16793        // propagation-probe test that expected only the stale axis's
16794        // value — a dev-only dep would land in the runtime closure at
16795        // publish time, or a runtime dep would be excluded from the
16796        // published lacre. Peer of the sibling four-way distinct pin
16797        // on the top-level multi-word tetrad
16798        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
16799        // and the two-way pin on the sibling
16800        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
16801        // author-facing arm (4da6fba's test), extended here to the
16802        // renderer-side wire-key arm of the same two-list dep-graph
16803        // axis so both halves of the "one canonical byte-string per
16804        // typed axis per (author, wire)" grid carry the same
16805        // distinct-ness discipline.
16806        assert_ne!(
16807            crate::render::CAIXA_KEY_DEPS,
16808            crate::render::CAIXA_KEY_DEPS_DEV,
16809            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
16810             canonical byte-sequences on the two-list dep-graph \
16811             renderer-side wire-key axis"
16812        );
16813    }
16814
16815    // ── DepList / Caixa::push_dep pin ────────────────────────────────
16816    //
16817    // The compounding pin: the two-arm closed-set typed enum
16818    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
16819    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
16820    // consumer of the top-level manifest's dep-mutation surface reads
16821    // through, and the typed dispatch [`Caixa::push_dep`] on the
16822    // substrate primitive folds the "select list → check within-list
16823    // dup → push" cascade onto one method call. Prior to this landing
16824    // the two axes lived across two `&'static str` constants
16825    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
16826    // set type carrying the pair; the `feira add` mutation site's
16827    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
16828    // caixa.deps }` dispatch expressed no compile-time link back to
16829    // the substrate primitive, and a future third dep-list axis would
16830    // have silently split at every open-coded mutation site.
16831
16832    #[test]
16833    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
16834        // Every arm returns the same `&'static str` the substrate's
16835        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
16836        // constants carry. A future rebrand on either constant reaches
16837        // the enum through one edit; a regression to inline literals
16838        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
16839        // quotes from the wire-format constants every consumer routes
16840        // through and this pin flags it at build time.
16841        assert_eq!(
16842            crate::dep::DepList::Prod.as_str(),
16843            crate::render::DEP_AUTHOR_KEY_DEPS
16844        );
16845        assert_eq!(
16846            crate::dep::DepList::Dev.as_str(),
16847            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
16848        );
16849    }
16850
16851    #[test]
16852    fn dep_list_display_routes_through_as_str() {
16853        // Same as-str-through-Display convergence discipline the
16854        // sibling closed-set typed enums carry — a `format!("{list}")`
16855        // call must land byte-for-byte on the accessor's return so a
16856        // future consumer that formats the enum for a diagnostic line
16857        // reaches the same wire-format constant the wire-format
16858        // producers do.
16859        assert_eq!(
16860            format!("{}", crate::dep::DepList::Prod),
16861            crate::dep::DepList::Prod.as_str()
16862        );
16863        assert_eq!(
16864            format!("{}", crate::dep::DepList::Dev),
16865            crate::dep::DepList::Dev.as_str()
16866        );
16867    }
16868
16869    #[test]
16870    fn dep_list_all_enumerates_every_variant_once() {
16871        // Exhaustive-iteration pin — every arm appears exactly once in
16872        // `ALL`, matching the closed set the compiler enforces on the
16873        // sibling `match self` arms. A future variant addition that
16874        // extends only one method's match without extending `ALL`
16875        // would silently drop the new arm from every consumer that
16876        // iterates the slice.
16877        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
16878        assert!(variants.contains(&crate::dep::DepList::Prod));
16879        assert!(variants.contains(&crate::dep::DepList::Dev));
16880        assert_eq!(variants.len(), 2);
16881    }
16882
16883    #[test]
16884    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
16885        // Reverse projection on the two-list dep-graph axis: the
16886        // author-surface wire tag the sibling `as_str` emitter walks
16887        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
16888        // `Some(DepList::Prod)`. A regression that hand-rolled the
16889        // per-arm match without routing through the lifted
16890        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
16891        // future wire-tag rebrand and this pin flags it at build time.
16892        assert_eq!(
16893            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
16894            Some(crate::dep::DepList::Prod)
16895        );
16896    }
16897
16898    #[test]
16899    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
16900        // Peer of the `Prod`-arm pin on the dev-only axis: the
16901        // author-surface wire tag the sibling `as_str` emitter walks
16902        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
16903        // back to `Some(DepList::Dev)`. Same drift-detection posture
16904        // as the peer arm — the sibling method `match` arms are
16905        // compiler-checked exhaustive so a future variant addition
16906        // trips at build time.
16907        assert_eq!(
16908            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
16909            Some(crate::dep::DepList::Dev)
16910        );
16911    }
16912
16913    #[test]
16914    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
16915        // Every input outside the closed-set arm-string set the
16916        // sibling `as_str` emitter walks lands on the terminal `None`
16917        // fallback — no silent-accept surface. Sweeps a set of
16918        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
16919        // rebrand candidates, foreign wire tags, empty string) so a
16920        // future variant addition that widened one wire form without
16921        // extending the emitter's arm-set would trip the sibling
16922        // round-trip pin below rather than silently accepting the new
16923        // form here.
16924        for candidate in [
16925            "",
16926            "deps",
16927            "deps-dev",
16928            ":deps ",
16929            ":Deps",
16930            ":DEPS",
16931            ":build-dep",
16932            ":tool-dep",
16933            "prod",
16934            "dev",
16935        ] {
16936            assert_eq!(
16937                crate::dep::DepList::from_wire(candidate),
16938                None,
16939                "from_wire({candidate:?}) must return None; every input outside \
16940                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
16941                 the sibling as_str emitter walks lands on the terminal fallback",
16942            );
16943        }
16944    }
16945
16946    #[test]
16947    fn dep_list_round_trips_through_as_str_and_from_wire() {
16948        // Load-bearing round-trip pin: every arm the `ALL` iteration
16949        // exposes survives the `as_str` → `from_wire` composition
16950        // byte-for-byte. Same discipline the sibling closed-set enums
16951        // carry — `CaixaKind` /
16952        // `RestartStrategy` / `RestartPolicy` /
16953        // `PlacementStrategy` — extended onto the two-list dep-graph
16954        // axis. A future variant addition that extends `ALL` +
16955        // `as_str` without extending `from_wire` (or vice versa)
16956        // trips at build time on this iteration because the compiler
16957        // enforces exhaustiveness on the sibling `match self` arms.
16958        for &list in crate::dep::DepList::ALL {
16959            assert_eq!(
16960                crate::dep::DepList::from_wire(list.as_str()),
16961                Some(list),
16962                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
16963                 a silent split between the forward emitter and the reverse parser \
16964                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
16965            );
16966        }
16967    }
16968
16969    #[test]
16970    fn push_dep_routes_to_deps_slot_on_prod_arm() {
16971        // The `Prod` arm dispatches to the runtime-closure `:deps`
16972        // slot every downstream lacre-pipeline consumer resolves at
16973        // build time. A future arm that regressed to inline `&mut
16974        // self.deps_dev` on the `Prod` path would silently reroute
16975        // every runtime dep into the dev-only closure at publish time
16976        // — this pin refuses that regression.
16977        let src = Caixa::template("host");
16978        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16979        let before_deps = caixa.deps().len();
16980        let before_deps_dev = caixa.deps_dev().len();
16981        let dep = Dep {
16982            nome: "caixa-teia".to_string(),
16983            versao: "^0.1".to_string(),
16984            fonte: None,
16985            opcional: false,
16986            caracteristicas: Vec::new(),
16987        };
16988        caixa
16989            .push_dep(crate::dep::DepList::Prod, dep)
16990            .expect("first push into :deps succeeds");
16991        assert_eq!(caixa.deps().len(), before_deps + 1);
16992        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
16993        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
16994    }
16995
16996    #[test]
16997    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
16998        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
16999        // must dispatch to the dev-only-closure `:deps-dev` slot every
17000        // downstream test-facing artifact resolver reads. A future
17001        // regression that inverted the two arms would silently route
17002        // every dev-only dep into the runtime closure at publish time
17003        // and this pin catches it before the drift ships.
17004        let src = Caixa::template("host");
17005        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17006        let dep = Dep {
17007            nome: "tatara-check".to_string(),
17008            versao: "*".to_string(),
17009            fonte: None,
17010            opcional: false,
17011            caracteristicas: Vec::new(),
17012        };
17013        caixa
17014            .push_dep(crate::dep::DepList::Dev, dep)
17015            .expect("first push into :deps-dev succeeds");
17016        assert!(caixa.deps().is_empty());
17017        assert_eq!(caixa.deps_dev().len(), 1);
17018        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
17019    }
17020
17021    #[test]
17022    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
17023        // Within-list dup check routes through the canonical
17024        // [`DepError::DuplicateNome`] carrier — the substrate's typed
17025        // diagnostic for the same axis [`Caixa::validate_deps`]'s
17026        // parse-time [`crate::render::insert_first_seen`] walk raises
17027        // on. Prior to the lift the mutation site's inline
17028        // `bail!("dep '{}' already declared", …)` string-diagnostic
17029        // path expressed no through-line back to the typed error;
17030        // routing every dep-list refusal through one carrier means an
17031        // author reading a `feira add` refusal and a `feira build`
17032        // refusal reaches for the same corrective surface without
17033        // switching diagnostic idioms.
17034        let src = Caixa::template("host");
17035        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17036        let dep = Dep {
17037            nome: "caixa-teia".to_string(),
17038            versao: "^0.1".to_string(),
17039            fonte: None,
17040            opcional: false,
17041            caracteristicas: Vec::new(),
17042        };
17043        caixa
17044            .push_dep(crate::dep::DepList::Prod, dep.clone())
17045            .expect("first push succeeds");
17046        let dup = Dep {
17047            nome: "caixa-teia".to_string(),
17048            versao: "^0.2".to_string(),
17049            fonte: None,
17050            opcional: false,
17051            caracteristicas: Vec::new(),
17052        };
17053        let err = caixa
17054            .push_dep(crate::dep::DepList::Prod, dup)
17055            .expect_err("second push with same :nome refuses");
17056        assert_eq!(
17057            err,
17058            DepError::DuplicateNome {
17059                nome: "caixa-teia".to_string(),
17060                list: crate::render::DEP_AUTHOR_KEY_DEPS,
17061            }
17062        );
17063        // The refused mutation must not corrupt the target list —
17064        // exactly one entry lives past the refusal, matching the
17065        // canonical single-source-of-truth invariant `Caixa::deps()`
17066        // carries.
17067        assert_eq!(caixa.deps().len(), 1);
17068    }
17069
17070    #[test]
17071    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
17072        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
17073        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
17074        // `list` payload so a future author reading the refusal grep's
17075        // for the correct `:deps-dev` block in their `caixa.lisp`,
17076        // not the sibling `:deps` block the runtime closure resolves.
17077        let src = Caixa::template("host");
17078        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17079        let dep = Dep {
17080            nome: "tatara-check".to_string(),
17081            versao: "*".to_string(),
17082            fonte: None,
17083            opcional: false,
17084            caracteristicas: Vec::new(),
17085        };
17086        caixa
17087            .push_dep(crate::dep::DepList::Dev, dep.clone())
17088            .expect("first push succeeds");
17089        let err = caixa
17090            .push_dep(crate::dep::DepList::Dev, dep)
17091            .expect_err("second push with same :nome refuses");
17092        assert!(matches!(
17093            err,
17094            DepError::DuplicateNome {
17095                ref nome,
17096                list,
17097            } if nome == "tatara-check"
17098                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
17099        ));
17100    }
17101
17102    #[test]
17103    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
17104        // The within-list dup check is scoped to the target arm — a
17105        // caixa may legitimately carry the same `:nome` under both
17106        // `:deps` and `:deps-dev` (though the substrate's peer
17107        // [`crate::Caixa::validate_deps`] walk still refuses the
17108        // shape at parse time; the mutation-site refusal is scoped to
17109        // the mutation-site's list to match the peer parse-time
17110        // per-list [`crate::render::insert_first_seen`] discipline).
17111        // The two arms hold independent seen-sets.
17112        let src = Caixa::template("host");
17113        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17114        let dep_prod = Dep {
17115            nome: "shared".to_string(),
17116            versao: "^0.1".to_string(),
17117            fonte: None,
17118            opcional: false,
17119            caracteristicas: Vec::new(),
17120        };
17121        let dep_dev = Dep {
17122            nome: "shared".to_string(),
17123            versao: "*".to_string(),
17124            fonte: None,
17125            opcional: false,
17126            caracteristicas: Vec::new(),
17127        };
17128        caixa
17129            .push_dep(crate::dep::DepList::Prod, dep_prod)
17130            .expect("push into :deps succeeds");
17131        caixa
17132            .push_dep(crate::dep::DepList::Dev, dep_dev)
17133            .expect("push same :nome into :deps-dev succeeds");
17134        assert_eq!(caixa.deps().len(), 1);
17135        assert_eq!(caixa.deps_dev().len(), 1);
17136    }
17137
17138    #[test]
17139    fn deps_of_prod_returns_the_deps_slot_verbatim() {
17140        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
17141        // accessor must project onto the runtime-closure `:deps` slot —
17142        // element-equal and length-equal to the sibling per-slot
17143        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
17144        // A future arm that regressed to `self.deps_dev()` on the `Prod`
17145        // path would silently reroute every downstream typed-dispatch
17146        // walker (the [`Caixa::validate_deps`] per-list
17147        // [`crate::render::insert_first_seen`] dedup walk, any future
17148        // per-axis-parametrised consumer) into the sibling dev-only
17149        // closure and this pin refuses that regression.
17150        let src = Caixa::template("host");
17151        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17152        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
17153        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
17154        let dep = Dep {
17155            nome: "caixa-teia".to_string(),
17156            versao: "^0.1".to_string(),
17157            fonte: None,
17158            opcional: false,
17159            caracteristicas: Vec::new(),
17160        };
17161        caixa
17162            .push_dep(crate::dep::DepList::Prod, dep.clone())
17163            .expect("push into :deps succeeds");
17164        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
17165        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
17166        assert_eq!(
17167            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
17168            "caixa-teia"
17169        );
17170    }
17171
17172    #[test]
17173    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
17174        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
17175        // [`Caixa::deps_of`] must project onto the dev-only-closure
17176        // `:deps-dev` slot, element-equal and length-equal to the
17177        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
17178        // future regression that inverted the two arms would silently
17179        // route every dev-list walker onto the runtime closure and this
17180        // pin catches it before the drift ships.
17181        let src = Caixa::template("host");
17182        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17183        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
17184        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
17185        let dep = Dep {
17186            nome: "tatara-check".to_string(),
17187            versao: "*".to_string(),
17188            fonte: None,
17189            opcional: false,
17190            caracteristicas: Vec::new(),
17191        };
17192        caixa
17193            .push_dep(crate::dep::DepList::Dev, dep)
17194            .expect("push into :deps-dev succeeds");
17195        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
17196        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
17197        assert_eq!(
17198            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
17199            "tatara-check"
17200        );
17201    }
17202
17203    #[test]
17204    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
17205        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
17206        // [`Caixa::deps_of`] must land on the same two-slot partition the
17207        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
17208        // expose — the canonical dispatch a future per-axis-parametrised
17209        // walker (a future `feira app graph` per-list dep summary, a
17210        // future M4 per-cluster dev-closure-audit overlay the CR
17211        // materializer resolves per-CR) reads through. Prior to the
17212        // lift the two-block iteration lived open-coded at every walker,
17213        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
17214        // §I) would have had to grow a third block at every consumer.
17215        // A regression that dropped the `Dev` arm from `ALL` would flip
17216        // the collected pairs to `[(":deps", &[])]` alone and this pin
17217        // refuses that shape.
17218        let src = Caixa::template("host");
17219        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17220        let prod_dep = Dep {
17221            nome: "caixa-teia".to_string(),
17222            versao: "^0.1".to_string(),
17223            fonte: None,
17224            opcional: false,
17225            caracteristicas: Vec::new(),
17226        };
17227        let dev_dep = Dep {
17228            nome: "tatara-check".to_string(),
17229            versao: "*".to_string(),
17230            fonte: None,
17231            opcional: false,
17232            caracteristicas: Vec::new(),
17233        };
17234        caixa
17235            .push_dep(crate::dep::DepList::Prod, prod_dep)
17236            .expect("push into :deps succeeds");
17237        caixa
17238            .push_dep(crate::dep::DepList::Dev, dev_dep)
17239            .expect("push into :deps-dev succeeds");
17240        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
17241            .iter()
17242            .map(|&list| {
17243                let slice = caixa.deps_of(list);
17244                (list.as_str(), slice.len(), slice[0].nome())
17245            })
17246            .collect();
17247        assert_eq!(
17248            collected,
17249            vec![
17250                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
17251                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
17252            ]
17253        );
17254    }
17255
17256    #[test]
17257    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
17258        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
17259        // must route its per-list [`crate::render::insert_first_seen`]
17260        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
17261        // rather than the pre-lift open-coded two-block iteration over
17262        // `self.deps()` + `self.deps_dev()`. A regression that dropped
17263        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
17264        // stop refusing within-list dups on the sibling arm; a
17265        // regression that flipped the arm-to-list-key mapping
17266        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
17267        // diagnostic surface. Both drifts surface here through a paired
17268        // duplicate-name refusal per arm plus an offending-list-key
17269        // check on the emitted [`DepError::DuplicateNome`] carrier.
17270        for &list in crate::dep::DepList::ALL {
17271            let src = Caixa::template("host");
17272            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17273            let dup = Dep {
17274                nome: "twin".to_string(),
17275                versao: "^0.1".to_string(),
17276                fonte: None,
17277                opcional: false,
17278                caracteristicas: Vec::new(),
17279            };
17280            match list {
17281                crate::dep::DepList::Prod => {
17282                    caixa.deps.push(dup.clone());
17283                    caixa.deps.push(dup);
17284                }
17285                crate::dep::DepList::Dev => {
17286                    caixa.deps_dev.push(dup.clone());
17287                    caixa.deps_dev.push(dup);
17288                }
17289            }
17290            let err = caixa
17291                .validate_deps()
17292                .expect_err("within-list duplicate :nome must refuse");
17293            assert_eq!(
17294                err,
17295                DepError::DuplicateNome {
17296                    nome: "twin".to_string(),
17297                    list: list.as_str(),
17298                },
17299                "validate_deps on {list} arm must emit \
17300                 DepError::DuplicateNome carrying the arm's own \
17301                 as_str() diagnostic — the arm-to-list-key mapping \
17302                 flowed through DepList::ALL + Caixa::deps_of"
17303            );
17304        }
17305    }
17306}