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    /// Returns the typed [`tatara_lisp::KeywordCollision`] on the second
341    /// (and every subsequent) call in the same process — one keyword,
342    /// one type, per process is a hard invariant of the upstream
343    /// registry, and a caller that hits it must fix its crate graph
344    /// rather than swallowing the error. Peer of the sibling per-crate
345    /// `register()` entry points at `caixa-flake/src/flake.rs`,
346    /// `caixa-fmt/src/lisp_config.rs`, `caixa-lacre/src/lock.rs`,
347    /// `caixa-lint/src/lisp_config.rs`, `caixa-resolver/src/lisp_config.rs`
348    /// — every substrate crate that owns a tatara-lisp keyword now
349    /// propagates the same typed error verbatim, so a downstream binary
350    /// that seeds the registry (`tatara-check`, the future LSP) reaches
351    /// for one shape at every call site.
352    ///
353    /// # Errors
354    ///
355    /// [`tatara_lisp::KeywordCollision`] when a peer type has already
356    /// claimed the `defcaixa` keyword in this process.
357    pub fn register() -> Result<(), tatara_lisp::KeywordCollision> {
358        tatara_lisp::domain::register::<Self>()
359    }
360
361    /// Substrate-canonical per-`Caixa` `:licenca` SPDX-expression scalar
362    /// accessor every consumer of the top-level manifest's license axis
363    /// keys off — returns the author-declared `:licenca` byte-string
364    /// verbatim as an `Option<&str>`, borrowed from the typed slot's own
365    /// `Option<String>` storage. `None` when the slot is absent (the
366    /// canonical "omit to defer to the caixa-helm renderer's `MIT`
367    /// fallback" shape [`Self::validate_licenca`] documents at
368    /// caixa-core/src/manifest.rs:1560; the peer [`caixa-helm`]
369    /// `build_readme` fold at caixa-helm/src/lib.rs:962 reads this
370    /// predicate too, so an authored-but-unset `:licenca` round-trips to
371    /// a rendered `lareira-<nome>` chart's `README.md` `## License`
372    /// section structurally identical to one that omits the slot).
373    ///
374    /// The `:licenca` slot carries the universal-axis SPDX-expression
375    /// license identifier every kind of caixa emits under (CAIXA-SDLC
376    /// §I — the author-facing surface every `defcaixa` form supplies) —
377    /// the typed slot's `Option<String>` accept-set (empty-string
378    /// rejected through [`ManifestError::LicencaEmpty`], SPDX-alphabet-
379    /// invalid rejected through [`ManifestError::LicencaInvalid`]) maps
380    /// onto the `lareira-<nome>` Helm chart's `README.md` `## License`
381    /// section (caixa-helm/src/lib.rs:962) and (through future
382    /// tightening documented at [`Self::validate_licenca`]) the
383    /// Chart.yaml `annotations["artifacthub.io/license"]` axis every
384    /// registry-facing chart carries. Every downstream consumer that
385    /// reads the license byte-string keys off this scalar (the
386    /// [`Self::validate_licenca`] empty-arm + SPDX-shape gate that
387    /// routes through `self.licenca.as_deref()`, the caixa-helm
388    /// `build_readme` `unwrap_or_else(|| "MIT".into())` fold that keys
389    /// the fallback off the `Option::is_none()` arm, every future
390    /// per-`Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
391    /// acknowledges).
392    ///
393    /// Prior to this lift the `.licenca` field was accessed inline at
394    /// two production sites — [`Self::validate_licenca`]'s
395    /// `self.licenca.as_deref()` empty-and-shape gate binding and the
396    /// caixa-helm `build_readme` `caixa.licenca.clone().unwrap_or_else(||
397    /// "MIT".into())` `README.md` `## License` fold — two open-coded
398    /// field-accesses that expressed no compile-time link back to the
399    /// typed slot. A future extension of the `:licenca` axis to a
400    /// richer author surface — a per-`:licenca` structured SPDX
401    /// expression parser + license-id allowlist (the future tightening
402    /// [`Self::validate_licenca`]'s docstring acknowledges), a
403    /// per-cluster license-default overlay the M4 CR materializer
404    /// resolves per-CR (the "cluster policy pins `Apache-2.0` for every
405    /// unlisted caixa" arm), a promotion of the plain
406    /// `Option<String>` byte-string to a richer `SpdxExpression` enum
407    /// once the SPDX-expression parser lands — would have had to be
408    /// threaded through both open-coded copies in lockstep or the
409    /// validate gate and the caixa-helm emit path would silently
410    /// disagree on which license a given [`Caixa`] resolves to (an
411    /// author's `:licenca "MIT OR Apache-2.0"` would satisfy validate
412    /// while the emit path silently rendered a stale `MIT` fallback,
413    /// or vice versa). Lifting the resolution to a typed method on the
414    /// substrate primitive means every downstream consumer of the
415    /// caixa's per-`Caixa` license surface reaches for exactly one
416    /// typed dispatch — the resolver's accept-set migrates as a unit
417    /// on any future axis addition.
418    ///
419    /// First `Option<&str>`-return top-level [`Caixa`] scalar accessor —
420    /// opens the "outer [`Caixa`] `Option<&str>` scalar" projection
421    /// pattern the sibling per-`Caixa` `:descricao` / `:repositorio` /
422    /// `:edicao` future lifts fold on. Same "one typed dispatch on the
423    /// substrate primitive, thin projections at each consumer"
424    /// discipline the peer per-`:placement` [`crate::aplicacao::Placement::shard_key`]
425    /// (7cd2a28) / [`crate::aplicacao::Placement::affinity`] (74ec2d3)
426    /// / per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
427    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
428    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
429    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
430    /// typed-slot atom axes, extended here to the outer top-level
431    /// `Caixa` universal-axis surface. Named `licenca()` to match the
432    /// storage field's name; the accessor's identity maps onto the
433    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
434    /// carries.
435    #[must_use]
436    pub fn licenca(&self) -> Option<&str> {
437        self.licenca.as_deref()
438    }
439
440    /// Substrate-canonical per-`Caixa` `:repositorio` git-repo-URL scalar
441    /// accessor every consumer of the top-level manifest's homepage /
442    /// source-of-truth axis keys off — returns the author-declared
443    /// `:repositorio` byte-string verbatim as an `Option<&str>`, borrowed
444    /// from the typed slot's own `Option<String>` storage. `None` when
445    /// the slot is absent (the canonical "omit to defer to the renderer's
446    /// per-target placeholder" shape — [`caixa-helm`]'s `ChartYaml.home`
447    /// carries the `Option<String>` through verbatim so an author-omitted
448    /// `:repositorio` renders a `Chart.yaml` without a `home:` field
449    /// (`skip_serializing_if = "Option::is_none"`), while [`caixa-flux`]'s
450    /// `ClusterBundleOpts::for_caixa` folds the omitted slot through a
451    /// `format!("https://github.com/{DEFAULT_PLEME_GIT_ORG}/{nome}")`
452    /// fallback derived from `caixa.nome`).
453    ///
454    /// The `:repositorio` slot carries the universal-axis git-repo-URL
455    /// homepage identifier every kind of caixa emits under (CAIXA-SDLC
456    /// §I — the author-facing surface every `defcaixa` form supplies) —
457    /// the typed slot's `Option<String>` accept-set (empty-string
458    /// rejected through [`ManifestError::RepositorioEmpty`], git-repo-URL-
459    /// shape-invalid rejected through [`ManifestError::RepositorioInvalid`]
460    /// past the shared [`crate::render::is_git_repo_url`] predicate the
461    /// peer per-`:deps :fonte :repo` axis also routes through) maps onto
462    /// four load-bearing downstream consumers:
463    ///
464    ///   - [`Self::validate_repositorio`]'s empty-arm + shape-predicate
465    ///     gate binding at caixa-core/src/manifest.rs:1456 — the
466    ///     universal-axis identity gate wired at caixa-build time.
467    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.home` fold at
468    ///     caixa-helm/src/lib.rs:840 — the rendered `lareira-<nome>`
469    ///     Helm chart's `Chart.yaml` `home:` field, which every registry
470    ///     that ingests the chart (ArtifactHub, chartmuseum,
471    ///     `helm search repo`) surfaces as the chart's canonical source-
472    ///     of-truth link.
473    ///   - [`caixa-helm`]'s `build_readme` `## Source` fold at
474    ///     caixa-helm/src/lib.rs:957 — the rendered `lareira-<nome>`
475    ///     chart's `README.md` header link back to the source repo,
476    ///     which every author who inspects the rendered chart bundle
477    ///     lands at.
478    ///   - [`caixa-flux`]'s `ClusterBundleOpts::for_caixa`
479    ///     `GitRepository.spec.url` fold at caixa-flux/src/lib.rs:2006 —
480    ///     the rendered `GitRepository` CR's `spec.url` field, which
481    ///     FluxCD's `source-controller` polls to reconcile the caixa's
482    ///     manifest bundle from git.
483    ///
484    /// Prior to this lift the `.repositorio` field was accessed inline
485    /// at four production sites — [`Self::validate_repositorio`]'s
486    /// `self.repositorio.as_deref()` empty-and-shape gate binding, the
487    /// caixa-helm `build_chart_yaml` `caixa.repositorio.clone()`
488    /// `Chart.yaml` `home:` field fold, the caixa-helm `build_readme`
489    /// `caixa.repositorio.clone().unwrap_or_else(|| caixa.nome.clone())`
490    /// `README.md` `## Source` fold, and the caixa-flux
491    /// `ClusterBundleOpts::for_caixa`
492    /// `caixa.repositorio.clone().unwrap_or_else(|| format!(...))`
493    /// `GitRepository.spec.url` fold — four open-coded field-accesses
494    /// that expressed no compile-time link back to the typed slot. A
495    /// future extension of the `:repositorio` axis to a richer author
496    /// surface — a per-`:repositorio` structured
497    /// [`crate::render::GitRepoUrl`]-shaped scheme+host+path parse
498    /// (the future tightening [`Self::validate_repositorio`]'s
499    /// docstring anticipates alongside the peer per-`:deps :fonte
500    /// :repo` axis), a per-cluster repo-mirror overlay the M4 CR
501    /// materializer resolves per-CR (the "cluster policy rewrites
502    /// `github:pleme-io/...` to `git.internal/mirror/pleme-io/...`"
503    /// arm the private-registry story acknowledges), a promotion of
504    /// the plain `Option<String>` byte-string to a richer
505    /// `RepoUrl` enum discriminated on scheme — would have had to be
506    /// threaded through all four open-coded copies in lockstep or the
507    /// validate gate and the three emit paths would silently disagree
508    /// on which URL a given [`Caixa`] resolves to (an author's
509    /// `:repositorio "github:pleme-io/checkout"` would satisfy validate
510    /// while one of the emit paths silently rendered a stale URL, or
511    /// vice versa). Lifting the resolution to a typed method on the
512    /// substrate primitive means every downstream consumer of the
513    /// caixa's per-`Caixa` repo-URL surface reaches for exactly one
514    /// typed dispatch — the resolver's accept-set migrates as a unit on
515    /// any future axis addition.
516    ///
517    /// Second outer top-level [`Caixa`] `Option<&str>`-return scalar
518    /// accessor — sibling of [`Self::licenca`] (6d5bc28), the accessor
519    /// that opened the "outer [`Caixa`] `Option<&str>` scalar"
520    /// projection pattern this lift folds on. Same "one typed dispatch
521    /// on the substrate primitive, thin projections at each consumer"
522    /// discipline the peer per-`:placement`
523    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
524    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
525    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
526    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
527    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
528    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
529    /// typed-slot atom axes, extended here to the second outer top-level
530    /// `Caixa` universal-axis surface. Named `repositorio()` to match
531    /// the storage field's name; the accessor's identity maps onto the
532    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
533    /// carries.
534    #[must_use]
535    pub fn repositorio(&self) -> Option<&str> {
536        self.repositorio.as_deref()
537    }
538
539    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
540    /// chart-description scalar accessor every consumer of the top-level
541    /// manifest's Chart.yaml `description:` axis keys off — returns the
542    /// author-declared `:descricao` byte-string verbatim as an
543    /// `Option<&str>`, borrowed from the typed slot's own
544    /// `Option<String>` storage. `None` when the slot is absent (the
545    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
546    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
547    /// omitted slot through a `format!("Generated chart for caixa Servico
548    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
549    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
550    /// and [`caixa-feira`]'s `render_flake` folds it through a
551    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
552    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
553    ///
554    /// The `:descricao` slot carries the universal-axis free-form-prose
555    /// chart-description identifier every kind of caixa emits under
556    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
557    /// supplies) — the typed slot's `Option<String>` accept-set
558    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
559    /// chart-description-shape-invalid rejected through
560    /// [`ManifestError::DescricaoInvalid`] past the shared
561    /// [`crate::render::is_chart_description_shape`] predicate the peer
562    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
563    /// load-bearing downstream consumers:
564    ///
565    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
566    ///     gate binding — the universal-axis identity gate wired at
567    ///     caixa-build time.
568    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
569    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
570    ///     chart's `Chart.yaml` `description:` field, which
571    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
572    ///     `WARNING [chart.metadata.description]: description is required`
573    ///     when absent) and which every registry that ingests the chart
574    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
575    ///     chart's canonical one-line prose descriptor.
576    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
577    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
578    ///     header directly beneath the `# <chart-name>` title, which
579    ///     every author who inspects the rendered chart bundle lands at.
580    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
581    ///     top-level fold — the emitted `flake.nix`'s `description`
582    ///     field, which every Nix consumer (`nix flake show`,
583    ///     `nix flake metadata`, downstream flake-registry ingestors)
584    ///     surfaces as the flake's canonical descriptor.
585    ///
586    /// Prior to this lift the `.descricao` field was accessed inline at
587    /// four production sites — [`Self::validate_descricao`]'s
588    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
589    /// caixa-helm `build_chart_yaml`
590    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
591    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
592    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
593    /// `README.md` header fold, and the caixa-feira `render_flake`
594    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
595    /// `description = ""` fold — four open-coded field-accesses that
596    /// expressed no compile-time link back to the typed slot. A future
597    /// extension of the `:descricao` axis to a richer author surface —
598    /// a per-`:descricao` locale-tagged multi-language descriptor map
599    /// (the "one caixa, N language-tagged prose descriptions" arm
600    /// author-tooling internationalization anticipates), a
601    /// per-registry-target length-and-shape overlay the M4 CR
602    /// materializer resolves per-CR (the "ArtifactHub caps description
603    /// at 512 bytes but the internal registry caps at 256" arm), a
604    /// promotion of the plain `Option<String>` byte-string to a richer
605    /// `ChartDescription` newtype guaranteeing the
606    /// `is_chart_description_shape` predicate at the type level — would
607    /// have had to be threaded through all four open-coded copies in
608    /// lockstep or the validate gate and the three emit paths would
609    /// silently disagree on which prose string a given [`Caixa`]
610    /// resolves to (an author's
611    /// `:descricao "Checkout flow orchestration."` would satisfy
612    /// validate while one of the emit paths silently rendered a stale
613    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
614    /// resolution to a typed method on the substrate primitive means
615    /// every downstream consumer of the caixa's per-`Caixa`
616    /// chart-description surface reaches for exactly one typed dispatch
617    /// — the resolver's accept-set migrates as a unit on any future
618    /// axis addition.
619    ///
620    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
621    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
622    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
623    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
624    /// lift folds on. Same "one typed dispatch on the substrate
625    /// primitive, thin projections at each consumer" discipline the
626    /// peer per-`:placement`
627    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
628    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
629    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
630    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
631    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
632    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
633    /// typed-slot atom axes, extended here to the third outer top-level
634    /// `Caixa` universal-axis surface. Named `descricao()` to match the
635    /// storage field's name; the accessor's identity maps onto the
636    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
637    /// carries. The one remaining universal `Option<String>` slot
638    /// (`:edicao`) folds on this pattern next.
639    #[must_use]
640    pub fn descricao(&self) -> Option<&str> {
641        self.descricao.as_deref()
642    }
643
644    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
645    /// accessor every consumer of the top-level manifest's tatara-lisp
646    /// edition-selector axis keys off — returns the author-declared
647    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
648    /// the typed slot's own `Option<String>` storage. `None` when the
649    /// slot is absent (the canonical "omit the slot to defer to the
650    /// substrate's default edition" shape every existing
651    /// [`caixa-resolver`] integration test fixture carries via
652    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
653    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
654    /// arm by construction, so an author-omitted `:edicao` round-trips
655    /// to a build without triggering the year-shape predicate).
656    ///
657    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
658    /// decimal-year language-edition identifier every kind of caixa
659    /// emits under (CAIXA-SDLC §I — the author-facing surface every
660    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
661    /// accept-set (empty-string rejected through
662    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
663    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
664    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
665    /// onto one load-bearing downstream consumer today
666    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
667    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
668    /// future edition-aware substrate consumer the CAIXA-SDLC §I
669    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
670    /// selector every edition-aware build step keys off, the future
671    /// per-edition compatibility-flag overlay the M4 CR materializer
672    /// resolves per-CR, the peer [`Caixa::template`] canonical
673    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
674    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
675    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
676    /// carry `edicao: Some("2026".into())` by construction).
677    ///
678    /// Prior to this lift the `.edicao` field was accessed inline at
679    /// one production site — [`Self::validate_edicao`]'s
680    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
681    /// open-coded field-access that expressed no compile-time link
682    /// back to the typed slot. A future extension of the `:edicao`
683    /// axis to a richer author surface — a per-`:edicao` known-
684    /// edition allowlist (the future tightening
685    /// [`Self::validate_edicao`]'s docstring acknowledges past the
686    /// structural year-shape floor, rejecting year-shaped values that
687    /// don't name a tatara-lisp edition the substrate actually
688    /// understands — `"1999"` is year-shaped but no `1999` edition
689    /// exists), a per-edition compatibility-flag overlay the M4 CR
690    /// materializer resolves per-CR (the "edition `"2026"` enables
691    /// macro-surface features the sibling `"2018"` gates behind a
692    /// feature flag" arm the edition-selector story anticipates), a
693    /// promotion of the plain `Option<String>` byte-string to a
694    /// richer `CaixaEdition` enum discriminated on year once a sibling
695    /// edition to `"2026"` lands — would have had to be threaded
696    /// through the open-coded copy in lockstep with every future
697    /// edition-aware consumer, or the validate gate and the future
698    /// edition-aware consumer path would silently disagree on which
699    /// edition a given [`Caixa`] resolves to (an author's
700    /// `:edicao "2026"` would satisfy validate while a future
701    /// edition-aware consumer silently defaulted to a stale edition,
702    /// or vice versa). Lifting the resolution to a typed method on
703    /// the substrate primitive means every downstream consumer of the
704    /// caixa's per-`Caixa` edition surface reaches for exactly one
705    /// typed dispatch — the resolver's accept-set migrates as a unit
706    /// on any future axis addition.
707    ///
708    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
709    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
710    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
711    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
712    /// `Option<&str>` scalar" projection pattern this lift folds on.
713    /// Same "one typed dispatch on the substrate primitive, thin
714    /// projections at each consumer" discipline the peer per-`:placement`
715    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
716    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
717    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
718    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
719    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
720    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
721    /// typed-slot atom axes, extended here to close the outer top-level
722    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
723    /// slot. Named `edicao()` to match the storage field's name; the
724    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
725    /// vocabulary the slot's docstring already carries.
726    #[must_use]
727    pub fn edicao(&self) -> Option<&str> {
728        self.edicao.as_deref()
729    }
730
731    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
732    /// label caixa-identity scalar accessor every consumer of the top-
733    /// level manifest's identity axis keys off — returns the author-
734    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
735    /// the typed slot's own `String` storage. Non-optional (`:nome` is
736    /// a required-axis scalar every `defcaixa` form must supply; the
737    /// [`Self::from_lisp`] derive rejects an omitted / non-string
738    /// `:nome` at parse time, so a `Caixa` past parse definitionally
739    /// carries a non-`None` `:nome`).
740    ///
741    /// The `:nome` slot carries the universal-axis DNS-1123-label
742    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
743    /// the primary identity axis every `defcaixa` form supplies
744    /// alongside `:versao` / `:kind`; the substrate-wide identity every
745    /// other typed surface that names a caixa reaches through — `:deps`
746    /// entries, `:membros` entries, `:children` entries, the
747    /// `lareira-<nome>` Helm chart name every per-Servico renderer
748    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
749    /// renderer emits) — the typed slot's `String` accept-set (empty
750    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
751    /// invalid rejected through [`ManifestError::NomeInvalid`] past
752    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
753    /// the peer name axes each land on, joint-length-with-`lareira-`-
754    /// prefix rejected through
755    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
756    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
757    /// load-bearing downstream consumer the substrate carries — the
758    /// two universal-axis validate gates at caixa-build time
759    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
760    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
761    /// derivation every per-Servico renderer keys off, the caixa-helm
762    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
763    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
764    /// `HTTPRoute` per-Aplicacao name axes at
765    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
766    /// [`crate::pleme_program_selector`] /
767    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
768    /// derivations, and every future substrate renderer that emits an
769    /// artifact keyed by the caixa's identity.
770    ///
771    /// Prior to this lift the `.nome` field was accessed inline at a
772    /// dozen production sites across `caixa-core` (the two universal-
773    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
774    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
775    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
776    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
777    /// entry `name:` fold, the `flux_kustomization_source_subtree`
778    /// per-cluster subpath derivation), and `caixa-mesh` (the
779    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
780    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
781    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
782    /// insert) — a dozen open-coded field-accesses that expressed no
783    /// compile-time link back to the typed slot. A future extension of
784    /// the `:nome` axis to a richer author surface — a per-`:nome`
785    /// structured `CaixaIdentity` newtype that carries the joint-
786    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
787    /// enforces at the type level (rather than as a validate-time
788    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
789    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
790    /// `partner-org/checkout` collision" arm the multi-tenant-registry
791    /// story acknowledges), a promotion of the plain `String` byte-
792    /// string to a richer `CaixaNome` newtype discriminated on
793    /// namespace prefix — would have had to be threaded through every
794    /// open-coded copy in lockstep or the two validate gates and the
795    /// dozen emit paths would silently disagree on which identity a
796    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
797    /// would satisfy validate while one of the emit paths silently
798    /// rendered a drifted other identity, or vice versa). Lifting the
799    /// resolution to a typed method on the substrate primitive means
800    /// every downstream consumer of the caixa's per-`Caixa` identity
801    /// surface reaches for exactly one typed dispatch — the resolver's
802    /// accept-set migrates as a unit on any future axis addition.
803    ///
804    /// First outer top-level [`Caixa`] `&str`-return required-scalar
805    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
806    /// projection pattern the sibling per-`Caixa` `:versao` future lift
807    /// folds on. Sibling in shape to the peer per-`:membros`
808    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
809    /// [`crate::aplicacao::WitContract::source`] /
810    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
811    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
812    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
813    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
814    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
815    /// per-sub-struct required-axis accessors carry on the sibling M3
816    /// mesh-slot-atom scalar-value axes, extended here to open the
817    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
818    /// Named `nome()` to match the storage field's name; the accessor's
819    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
820    /// slot's docstring already carries.
821    #[must_use]
822    pub fn nome(&self) -> &str {
823        &self.nome
824    }
825
826    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
827    /// pinned-version scalar accessor every consumer of the top-level
828    /// manifest's version axis keys off — returns the author-declared
829    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
830    /// typed slot's own `String` storage. Non-optional (`:versao` is a
831    /// required-axis scalar every `defcaixa` form must supply alongside
832    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
833    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
834    /// parse definitionally carries a non-`None` `:versao`).
835    ///
836    /// The `:versao` slot carries the universal-axis SemVer-2
837    /// concrete-version body every kind of caixa emits under
838    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
839    /// supplies alongside `:nome` / `:kind`; the substrate-wide
840    /// pinned-version every downstream artifact-emitting consumer
841    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
842    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
843    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
844    /// prefix composes on top of, the programs.yaml entry's `versao:`
845    /// value the `lareira-fleet-programs` aggregator carries onto each
846    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
847    /// tags every substrate-side `skopeo push` writes, the lacre
848    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
849    /// prior-version references peers in the exact same SemVer-2 shape).
850    /// The typed slot's `String` accept-set (empty rejected through
851    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
852    /// through [`ManifestError::VersaoInvalid`] past
853    /// [`semver::Version::parse`]) maps onto every load-bearing
854    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
855    /// universal-axis validate gate at caixa-build time, the
856    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
857    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
858    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
859    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
860    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
861    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
862    /// tag derivation (`format!("{prefix}{versao}")`), and every future
863    /// substrate renderer that emits an artifact keyed by the caixa's
864    /// pinned version.
865    ///
866    /// Prior to this lift the `.versao` field was accessed inline at a
867    /// dozen production sites across `caixa-core` (the universal-axis
868    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
869    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
870    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
871    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
872    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
873    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
874    /// (the `feira publish` git-tag derivation + the `feira app graph` /
875    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
876    /// field-accesses that expressed no compile-time link back to the
877    /// typed slot. A future extension of the `:versao` axis to a richer
878    /// author surface — a per-`:versao` structured `CaixaVersion` at the
879    /// storage layer (the substrate already carries a `CaixaVersion`
880    /// newtype at [`crate::version::CaixaVersion`], deferred until the
881    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
882    /// a per-registry `:versao` immutability overlay the M4 CR
883    /// materializer enforces per-CR, a promotion of the plain `String`
884    /// byte-string to a richer `PinnedVersao` newtype discriminated on
885    /// SemVer-2 pre-release / build-metadata presence — would have had
886    /// to be threaded through every open-coded copy in lockstep or the
887    /// validate gate and the dozen emit paths would silently disagree
888    /// on which version a given [`Caixa`] resolves to (an author's
889    /// `:versao "0.1.0"` would satisfy validate while one of the emit
890    /// paths silently rendered a drifted other version, or vice versa).
891    /// Lifting the resolution to a typed method on the substrate
892    /// primitive means every downstream consumer of the caixa's
893    /// per-`Caixa` pinned-version surface reaches for exactly one typed
894    /// dispatch — the resolver's accept-set migrates as a unit on any
895    /// future axis addition.
896    ///
897    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
898    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
899    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
900    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
901    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
902    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
903    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
904    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
905    /// on the sibling per-typed-slot version-carrier axes, extended here
906    /// to close the second outer top-level [`Caixa`] required-`&str`-
907    /// carrying axis so the two universal-axis identity-carrying
908    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
909    /// share the same "one typed dispatch per axis" discipline. Named
910    /// `versao()` to match the storage field's name; the accessor's
911    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
912    /// slot's docstring already carries.
913    #[must_use]
914    pub fn versao(&self) -> &str {
915        &self.versao
916    }
917
918    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
919    /// closed-set-enum discriminant accessor every consumer of the top-
920    /// level manifest's kind axis keys off — returns the author-declared
921    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
922    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
923    /// (`:kind` is a required-axis discriminant every `defcaixa` form
924    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
925    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
926    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
927    /// variant).
928    ///
929    /// The `:kind` slot carries the universal-axis closed-set typed-
930    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
931    /// §I — the primary shape gate every renderer / verifier /
932    /// operator branches on; the five variants `Biblioteca` /
933    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
934    /// the caixa surface into disjoint runtime contracts) — the typed
935    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
936    /// values through the derive-macro's symbol-arm gate, exhaustively
937    /// matched at every downstream dispatch site) maps onto every
938    /// load-bearing downstream consumer the substrate carries:
939    ///
940    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
941    ///     predicate — the canonical two-line
942    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
943    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
944    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
945    ///     ComputeUnit` CR materializer) runs at its entry-point,
946    ///     alongside the [`crate::render::KindMismatch`] error carrier's
947    ///     `actual:` field the diagnostic surfaces to name the offending
948    ///     caixa's variant.
949    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
950    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
951    ///     `_view` composers that fold the flat mesh-slot / supervisor-
952    ///     slot columns into their typed sub-spec only when the kind
953    ///     matches (returns `None` otherwise); the future per-Servico
954    ///     M2-view composer (`servico_view`) will follow the same shape.
955    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
956    ///     coherence gate — the `!self.kind.requires_exe()` /
957    ///     `!self.kind.requires_servicos()` predicates that fence
958    ///     each code-surface slot from the wrong owning kind.
959    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
960    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
961    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
962    ///     coherence error carriers (`SupervisorOwnsCode` /
963    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
964    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
965    ///     / `ForeignCodeSlot`) which each name the offending caixa's
966    ///     variant in their `kind:` field.
967    ///
968    /// Prior to this lift the `.kind` field was accessed inline at
969    /// twenty-plus production sites across `caixa-core` (the
970    /// [`crate::render::require_kind`] entry-gate predicate + the
971    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
972    /// composers, the `declared_foreign_code_slots` per-slot kind-
973    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
974    /// kind ↔ code-surface predicates + four error carriers) — a score
975    /// of open-coded field-accesses that expressed no compile-time link
976    /// back to the typed slot. A future extension of the `:kind` axis
977    /// to a richer author surface — a per-`:kind` sub-variant discriminant
978    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
979    /// variant across the wasm-component / legacy-container / native-
980    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
981    /// kind-overlay the M4 CR materializer resolves per-CR (the
982    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
983    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
984    /// enum to a richer `KindWithRuntime` discriminated on the
985    /// component-model world axis — would have had to be threaded
986    /// through every open-coded copy in lockstep or the entry gate,
987    /// the view composers, and the layout invariants would silently
988    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
989    /// the resolution to a typed method on the substrate primitive
990    /// means every downstream consumer of the caixa's per-`Caixa`
991    /// kind surface reaches for exactly one typed dispatch — the
992    /// resolver's accept-set migrates as a unit on any future axis
993    /// addition.
994    ///
995    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
996    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
997    /// required-discriminant" projection pattern. Sibling in shape to
998    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
999    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
1000    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
1001    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
1002    /// on the sibling nested-spec typed-slot discriminator axes,
1003    /// extended here to the outer top-level [`Caixa`] universal-axis
1004    /// surface. Named `kind()` to match the storage field's name;
1005    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
1006    /// vocabulary the slot's docstring already carries.
1007    #[must_use]
1008    pub fn kind(&self) -> CaixaKind {
1009        self.kind
1010    }
1011
1012    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
1013    /// maintainer-name-list slice-accessor every consumer of the top-
1014    /// level manifest's maintainer axis keys off — returns the author-
1015    /// declared `:autores` list verbatim as a `&[String]` slice-view over
1016    /// the same backing buffer the raw `self.autores.as_slice()` field
1017    /// access borrows from. Empty-list-carrying (`:autores` is a default-
1018    /// empty axis every `defcaixa` form supplies with an empty `()` when
1019    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
1020    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1021    /// parse definitionally carries a `Vec<String>` slot — possibly
1022    /// empty — and the returned `&[String]` degenerates to an empty
1023    /// slice on that arm without any silent `None` collapse).
1024    ///
1025    /// The `:autores` slot carries the universal-axis maintainer-name
1026    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1027    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1028    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1029    /// every downstream registry-facing artifact emits under) — the
1030    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1031    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1032    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1033    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1034    /// onto every load-bearing downstream consumer the substrate carries
1035    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1036    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1037    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1038    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1039    /// name, email: None }` record, every future per-`Caixa` registry-
1040    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1041    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1042    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1043    /// the future per-cluster author-notification overlay the M4 CR
1044    /// materializer resolves per-CR).
1045    ///
1046    /// Prior to this lift the `.autores` field was accessed inline at
1047    /// two production sites — [`Self::validate_autores`]'s `for autor
1048    /// in &self.autores` walk that gates every entry through
1049    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1050    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1051    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1052    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1053    /// two open-coded field-accesses that expressed no compile-time link
1054    /// back to the typed slot. A future extension of the `:autores` axis
1055    /// to a richer author surface — a per-`:autores` structured
1056    /// `Maintainer { name, email, url }` at the storage layer once the
1057    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1058    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1059    /// enforces per-CR (the "cluster policy demands every author declare
1060    /// an on-file `mailto:` contact" arm), a promotion of the plain
1061    /// `Vec<String>` byte-string list to a richer
1062    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1063    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1064    /// predicate already resolves through — would have had to be
1065    /// threaded through both open-coded copies in lockstep or the
1066    /// validate gate and the caixa-helm emit path would silently
1067    /// disagree on which authors a given [`Caixa`] resolves to (an
1068    /// author's `:autores ("alice" "bob")` would satisfy validate while
1069    /// the caixa-helm emit path silently rendered a drifted other
1070    /// maintainer list, or vice versa). Lifting the resolution to a
1071    /// typed method on the substrate primitive means every downstream
1072    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1073    /// for exactly one typed dispatch — the resolver's accept-set
1074    /// migrates as a unit on any future axis addition.
1075    ///
1076    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1077    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1078    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1079    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1080    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1081    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1082    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1083    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1084    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1085    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1086    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1087    /// per-M3 typed-slot list axes, extended here to the outer top-level
1088    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1089    /// `&Vec<String>`) because every downstream consumer of the author
1090    /// list treats it as a read-only sequence — the slice-view is the
1091    /// narrowest borrow that supports every present + roadmapped consumer
1092    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1093    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1094    /// reaches for (the storage-side `Vec` remains reachable through the
1095    /// `pub autores` field for the mutation-carrying serde round-trip and
1096    /// per-test fixture-mutation paths). Named `autores()` to match the
1097    /// storage field's name; the accessor's identity maps onto the
1098    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1099    /// carries.
1100    #[must_use]
1101    pub fn autores(&self) -> &[String] {
1102        self.autores.as_slice()
1103    }
1104
1105    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1106    /// registry-search-tag-list slice-accessor every consumer of the
1107    /// top-level manifest's topical-tag axis keys off — returns the
1108    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1109    /// slice-view over the same backing buffer the raw
1110    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1111    /// list-carrying (`:etiquetas` is a default-empty axis every
1112    /// `defcaixa` form supplies with an empty `()` when unset; the
1113    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1114    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1115    /// definitionally carries a `Vec<String>` slot — possibly empty —
1116    /// and the returned `&[String]` degenerates to an empty slice on
1117    /// that arm without any silent `None` collapse).
1118    ///
1119    /// The `:etiquetas` slot carries the universal-axis topical-tag
1120    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1121    /// author-facing surface every `defcaixa` form supplies alongside
1122    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1123    /// search-facing axis every downstream registry-facing artifact
1124    /// emits under) — the typed slot's `Vec<String>` accept-set
1125    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1126    /// non-chart-keyword-shape rejected through
1127    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1128    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1129    /// every load-bearing downstream consumer the substrate carries —
1130    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1131    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1132    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1133    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1134    /// `Chart.yaml` `keywords:` array (chained with the
1135    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1136    /// dedup'd through a `BTreeSet` at emit time), every future per-
1137    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1138    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1139    /// annotation, the future per-cluster tag-notification overlay the
1140    /// M4 CR materializer resolves per-CR).
1141    ///
1142    /// Prior to this lift the `.etiquetas` field was accessed inline at
1143    /// two production sites — [`Self::validate_etiquetas`]'s `for
1144    /// etiqueta in &self.etiquetas` walk that gates every entry through
1145    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1146    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1147    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1148    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1149    /// two open-coded field-accesses that expressed no compile-time
1150    /// link back to the typed slot. A future extension of the
1151    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1152    /// structured `ChartKeyword { name, uri, category }` at the storage
1153    /// layer once the substrate absorbs `artifacthub.io/keywords`
1154    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1155    /// CR materializer enforces per-CR (the "cluster policy demands
1156    /// every tag come from a substrate-approved taxonomy" arm), a
1157    /// promotion of the plain `Vec<String>` byte-string list to a
1158    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1159    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1160    /// already resolves through — would have had to be threaded through
1161    /// both open-coded copies in lockstep or the validate gate and the
1162    /// caixa-helm emit path would silently disagree on which tags a
1163    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1164    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1165    /// path silently rendered a drifted other keyword list, or vice
1166    /// versa). Lifting the resolution to a typed method on the
1167    /// substrate primitive means every downstream consumer of the
1168    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1169    /// typed dispatch — the resolver's accept-set migrates as a unit
1170    /// on any future axis addition.
1171    ///
1172    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1173    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1174    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1175    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1176    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1177    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1178    /// fold onto the same pattern in future lifts. Sibling in shape to
1179    /// the peer per-`:supervisor`
1180    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1181    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1182    /// (a6e18d7), per-`:membros`
1183    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1184    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1185    /// (0dcc926), and per-`:upgrade-from :instructions`
1186    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1187    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1188    /// typed-slot list axes, extended here to the outer top-level
1189    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1190    /// `&Vec<String>`) because every downstream consumer of the tag
1191    /// list treats it as a read-only sequence — the slice-view is the
1192    /// narrowest borrow that supports every present + roadmapped
1193    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1194    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1195    /// the typed view reaches for (the storage-side `Vec` remains
1196    /// reachable through the `pub etiquetas` field for the mutation-
1197    /// carrying serde round-trip and per-test fixture-mutation paths).
1198    /// Named `etiquetas()` to match the storage field's name; the
1199    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1200    /// vocabulary the slot's docstring already carries.
1201    #[must_use]
1202    pub fn etiquetas(&self) -> &[String] {
1203        self.etiquetas.as_slice()
1204    }
1205
1206    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1207    /// library-source-path-list slice-accessor every consumer of the
1208    /// top-level manifest's Biblioteca-source axis keys off — returns
1209    /// the author-declared `:bibliotecas` list verbatim as a
1210    /// `&[String]` slice-view over the same backing buffer the raw
1211    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1212    /// list-carrying (`:bibliotecas` is a default-empty axis every
1213    /// `defcaixa` form supplies with an empty `()` when unset; the
1214    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1215    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1216    /// parse definitionally carries a `Vec<String>` slot — possibly
1217    /// empty — and the returned `&[String]` degenerates to an empty
1218    /// slice on that arm without any silent `None` collapse).
1219    ///
1220    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1221    /// entry-path list every `:kind Biblioteca` caixa emits under
1222    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1223    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1224    /// substrate-wide library-carrier axis every downstream
1225    /// authoring-facing consumer keys off) — the typed slot's
1226    /// `Vec<String>` accept-set (empty-per-entry rejected through
1227    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1228    /// non-sandboxed-relative-shape rejected through
1229    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1230    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1231    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1232    /// maps onto every load-bearing downstream consumer the substrate
1233    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1234    /// empty-check + per-entry file-exists loop at
1235    /// caixa-core/src/layout.rs that gates each entry through
1236    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1237    /// [`Self::validate_code_paths`] per-slot shape gate at
1238    /// caixa-core/src/manifest.rs that walks each entry through the
1239    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1240    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1241    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1242    /// declared library file for lexical / structural errors before
1243    /// downstream `importar` resolution, every future per-`Caixa`
1244    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1245    /// (the future `tatara-lispc` compilation entry the docstring at
1246    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1247    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1248    /// the future `caixa-lsp` per-library semantic-token stream the
1249    /// caixa-lsp docstring roadmaps).
1250    ///
1251    /// Prior to this lift the `.bibliotecas` field was accessed inline
1252    /// at three production sites — [`crate::LayoutInvariants`]'s
1253    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1254    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1255    /// declared library path through the on-disk-existence check,
1256    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1257    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1258    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1259    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1260    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1261    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1262    /// coded field-accesses that expressed no compile-time link back
1263    /// to the typed slot. A future extension of the `:bibliotecas`
1264    /// axis to a richer library surface — a per-`:bibliotecas`
1265    /// structured `BibliotecaEntry { path, edition, exports }` at the
1266    /// storage layer once the substrate absorbs the per-library
1267    /// language-edition + explicit-exports tuple the tatara-lisp
1268    /// module-system roadmap acknowledges, a per-registry
1269    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1270    /// per-CR (the "cluster policy demands every biblioteca declare
1271    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1272    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1273    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1274    /// [`crate::render::is_sandboxed_relative_path`] +
1275    /// [`crate::render::is_lisp_extension`] predicates already resolve
1276    /// through — would have had to be threaded through all three
1277    /// open-coded copies in lockstep or the layout gate, the shape
1278    /// validator, and the `feira build` phase-1 parse walk would
1279    /// silently disagree on which library paths a given [`Caixa`]
1280    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1281    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1282    /// silently parsed a drifted other list, or vice versa). Lifting
1283    /// the resolution to a typed method on the substrate primitive
1284    /// means every downstream consumer of the caixa's per-`Caixa`
1285    /// library-source surface reaches for exactly one typed dispatch
1286    /// — the resolver's accept-set migrates as a unit on any future
1287    /// axis addition.
1288    ///
1289    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1290    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1291    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1292    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1293    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1294    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1295    /// `:children` / `:membros` / `:contratos`) fold onto the same
1296    /// pattern in future lifts. Sibling in shape to the peer
1297    /// per-`:supervisor`
1298    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1299    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1300    /// (a6e18d7), per-`:membros`
1301    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1302    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1303    /// (0dcc926), and per-`:upgrade-from :instructions`
1304    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1305    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1306    /// typed-slot list axes, extended here to the outer top-level
1307    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1308    /// `&Vec<String>`) because every downstream consumer of the
1309    /// library-source list treats it as a read-only sequence — the
1310    /// slice-view is the narrowest borrow that supports every
1311    /// present + roadmapped consumer (`.iter()`, `.len()`,
1312    /// `.is_empty()`) without leaking the backing `Vec`'s
1313    /// grow/push/reserve surface no consumer of the typed view
1314    /// reaches for (the storage-side `Vec` remains reachable through
1315    /// the `pub bibliotecas` field for the mutation-carrying serde
1316    /// round-trip and per-test fixture-mutation paths). Named
1317    /// `bibliotecas()` to match the storage field's name; the
1318    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1319    /// vocabulary the slot's docstring already carries.
1320    #[must_use]
1321    pub fn bibliotecas(&self) -> &[String] {
1322        self.bibliotecas.as_slice()
1323    }
1324
1325    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1326    /// nix-built-executable-entry-path-list slice-accessor every consumer
1327    /// of the top-level manifest's Binario-executable axis keys off —
1328    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1329    /// slice-view over the same backing buffer the raw
1330    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1331    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1332    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1333    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1334    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1335    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1336    /// degenerates to an empty slice on that arm without any silent
1337    /// `None` collapse).
1338    ///
1339    /// The `:exe` slot carries the universal-axis nix-built executable
1340    /// entry-path list every `:kind Binario` caixa emits under
1341    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1342    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1343    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1344    /// downstream flake-build-facing consumer keys off) — the typed
1345    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1346    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1347    /// non-sandboxed-relative-shape rejected through
1348    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1349    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1350    /// directory paths rejected past the layout's
1351    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1352    /// onto every load-bearing downstream consumer the substrate carries
1353    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1354    /// per-entry file-exists + `exe/`-directory-fence loop at
1355    /// caixa-core/src/layout.rs that gates each entry through
1356    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1357    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1358    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1359    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1360    /// that fences code-surface slots off from the two no-code kinds,
1361    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1362    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1363    /// fences the `:exe` code surface off from every non-Binario code-
1364    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1365    /// that walks each entry through the sandbox-relative / cross-entry
1366    /// duplicate gates, every future per-`Caixa` executable-facing
1367    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1368    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1369    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1370    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1371    /// future `feira nix` per-executable Binario-target emit path).
1372    ///
1373    /// Prior to this lift the `.exe` field was accessed inline at three
1374    /// production sites — the compound-code-path `has_code =
1375    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1376    /// !caixa.servicos.is_empty()` OR-fold on the
1377    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1378    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1379    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1380    /// gate, the per-entry `for p in &caixa.exe`
1381    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1382    /// [`Self::declared_foreign_code_slots`]'s
1383    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1384    /// open-coded field-accesses that expressed no compile-time link
1385    /// back to the typed slot. A future extension of the `:exe` axis
1386    /// to a richer executable surface — a per-`:exe` structured
1387    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1388    /// layer once the substrate absorbs the per-executable
1389    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1390    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1391    /// the M4 CR materializer enforces per-CR (the "cluster policy
1392    /// demands every Binario declare an explicit `:wrapper`" arm), a
1393    /// promotion of the plain `Vec<String>` byte-string list to a
1394    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1395    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1396    /// fence already resolves through — would have had to be threaded
1397    /// through all four open-coded copies in lockstep or the layout
1398    /// gate, the shape validator, and the `feira nix` emit path would
1399    /// silently disagree on which executable paths a given [`Caixa`]
1400    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1401    /// satisfy layout while `feira nix` silently packaged a drifted
1402    /// other list, or vice versa). Lifting the resolution to a typed
1403    /// method on the substrate primitive means every downstream
1404    /// consumer of the caixa's per-`Caixa` executable-source surface
1405    /// reaches for exactly one typed dispatch — the resolver's accept-
1406    /// set migrates as a unit on any future axis addition.
1407    ///
1408    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1409    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1410    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1411    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1412    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1413    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1414    /// future lift closes onto (per the trio of code-surface list slots
1415    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1416    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1417    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1418    /// last unlifted code-surface slot). Sibling in shape to the peer
1419    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1420    /// (bc92bce), per-`:placement`
1421    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1422    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1423    /// (6c77e36), per-`:contratos`
1424    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1425    /// per-`:upgrade-from :instructions`
1426    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1427    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1428    /// typed-slot list axes, extended here to the outer top-level
1429    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1430    /// `&Vec<String>`) because every downstream consumer of the
1431    /// executable-source list treats it as a read-only sequence — the
1432    /// slice-view is the narrowest borrow that supports every
1433    /// present + roadmapped consumer (`.iter()`, `.len()`,
1434    /// `.is_empty()`) without leaking the backing `Vec`'s
1435    /// grow/push/reserve surface no consumer of the typed view
1436    /// reaches for (the storage-side `Vec` remains reachable through
1437    /// the `pub exe` field for the mutation-carrying serde
1438    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1439    /// to match the storage field's name; the accessor's identity
1440    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1441    /// docstring already carries.
1442    #[must_use]
1443    pub fn exe(&self) -> &[String] {
1444        self.exe.as_slice()
1445    }
1446
1447    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1448    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1449    /// of the top-level manifest's Servico-component axis keys off —
1450    /// returns the author-declared `:servicos` list verbatim as a
1451    /// `&[String]` slice-view over the same backing buffer the raw
1452    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1453    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1454    /// form supplies with an empty `()` when unset; the
1455    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1456    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1457    /// definitionally carries a `Vec<String>` slot — possibly empty —
1458    /// and the returned `&[String]` degenerates to an empty slice on
1459    /// that arm without any silent `None` collapse).
1460    ///
1461    /// The `:servicos` slot carries the universal-axis
1462    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1463    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1464    /// author-facing surface every `defcaixa` form supplies alongside
1465    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1466    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1467    /// Servico-facing renderer keys off) — the typed slot's
1468    /// `Vec<String>` accept-set (empty-per-entry rejected through
1469    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1470    /// non-sandboxed-relative-shape rejected through
1471    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1472    /// extension rejected through
1473    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1474    /// entry duplicate rejected through
1475    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1476    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1477    /// renderer entry-points, out-of-`servicos/`-directory paths
1478    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1479    /// `starts_with` fence) maps onto every load-bearing downstream
1480    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1481    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1482    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1483    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1484    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1485    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1486    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1487    /// that fences code-surface slots off from the two no-code kinds,
1488    /// [`Self::declared_foreign_code_slots`]'s
1489    /// `!self.servicos.is_empty()` arm on the
1490    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1491    /// `:servicos` code surface off from every non-Servico code-running
1492    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1493    /// walks each entry through the sandbox-relative / `.computeunit.
1494    /// yaml`-extension / cross-entry duplicate gates, the
1495    /// [`crate::require_single_servico`] V0 singularity gate every
1496    /// per-Servico renderer entry-point runs through
1497    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1498    /// `feira deploy` per-verb `first_servico_path` walk at
1499    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1500    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1501    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1502    /// per-Servico OCI packager, the future M4
1503    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1504    /// per-Servico OTel collector-config emit).
1505    ///
1506    /// Prior to this lift the `.servicos` field was accessed inline at
1507    /// five production sites — the compound-code-path `has_code =
1508    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1509    /// !caixa.servicos.is_empty()` OR-fold on the
1510    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1511    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1512    /// `caixa.servicos.is_empty()`
1513    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1514    /// per-entry `for p in &caixa.servicos`
1515    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1516    /// [`Self::declared_foreign_code_slots`]'s
1517    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1518    /// and the [`crate::require_single_servico`] V0 count gate's
1519    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1520    /// projection (both the accept-arm predicate and the
1521    /// diagnostic-carrying `ServicoCountMismatch { count }`
1522    /// projection) — five open-coded field-accesses across three
1523    /// crates that expressed no compile-time link back to the typed
1524    /// slot. A future extension of the `:servicos` axis to a richer
1525    /// component surface — a per-`:servicos` structured
1526    /// `ServicoEntry { path, world, capabilities }` at the storage
1527    /// layer once the substrate absorbs the per-component WIT-world +
1528    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1529    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1530    /// materializer enforces per-CR (the "cluster policy demands every
1531    /// Servico declare an explicit `:world`" arm), a promotion of the
1532    /// plain `Vec<String>` byte-string list to a richer
1533    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1534    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1535    /// `starts_with(servicos_dir)` fence and the
1536    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1537    /// already resolve through, a promotion of the V0 singleton
1538    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1539    /// component-model multi-world boundary — would have had to be
1540    /// threaded through all five open-coded copies in lockstep or the
1541    /// layout gate, the shape validator, the V0 count gate, and the
1542    /// `feira chart` / `feira deploy` entry-point walks would silently
1543    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1544    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1545    /// yaml")` would satisfy layout while `feira chart` silently
1546    /// packaged a drifted other list, or vice versa). Lifting the
1547    /// resolution to a typed method on the substrate primitive means
1548    /// every downstream consumer of the caixa's per-`Caixa`
1549    /// ComputeUnit-CR-source surface reaches for exactly one typed
1550    /// dispatch — the resolver's accept-set migrates as a unit on any
1551    /// future axis addition.
1552    ///
1553    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1554    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1555    /// projection pattern [`Self::autores`] (b5d813f) opened,
1556    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1557    /// (8a36c23) closed the universal-axis text-tag family of, and
1558    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1559    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1560    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1561    /// a substrate-canonical slice accessor, the trio of code-surface
1562    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1563    /// tuple carries is complete on the typed dispatch surface (the
1564    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1565    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1566    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1567    /// per-element accessor swap in isolation — a future companion lift
1568    /// promotes the tuple's element type to `&[String]` and threads the
1569    /// triple of typed dispatches through as a unit). Sibling in shape
1570    /// to the peer per-`:supervisor`
1571    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1572    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1573    /// (a6e18d7), per-`:membros`
1574    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1575    /// per-`:contratos`
1576    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1577    /// per-`:upgrade-from :instructions`
1578    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1579    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1580    /// typed-slot list axes, extended here to the outer top-level
1581    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1582    /// `&Vec<String>`) because every downstream consumer of the
1583    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1584    /// the slice-view is the narrowest borrow that supports every
1585    /// present + roadmapped consumer (`.iter()`, `.len()`,
1586    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1587    /// grow/push/reserve surface no consumer of the typed view reaches
1588    /// for (the storage-side `Vec` remains reachable through the
1589    /// `pub servicos` field for the mutation-carrying serde round-trip
1590    /// and per-test fixture-mutation paths, and for the
1591    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1592    /// homogeneous-element-type shape carries the raw field access
1593    /// until the trio-closure lift promotes the tuple as a unit).
1594    /// Named `servicos()` to match the storage field's name; the
1595    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1596    /// vocabulary the slot's docstring already carries.
1597    #[must_use]
1598    pub fn servicos(&self) -> &[String] {
1599        self.servicos.as_slice()
1600    }
1601
1602    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
1603    /// runtime-dependency-declaration-list slice-accessor every consumer
1604    /// of the top-level manifest's runtime-dep-graph axis keys off —
1605    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
1606    /// slice-view over the same backing buffer the raw
1607    /// `self.deps.as_slice()` field access borrows from. Empty-list-
1608    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
1609    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1610    /// derive folds an omitted `:deps` through `#[serde(default)]` to
1611    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1612    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
1613    /// degenerates to an empty slice on that arm without any silent
1614    /// `None` collapse).
1615    ///
1616    /// The `:deps` slot carries the universal-axis runtime dependency
1617    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1618    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1619    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
1620    /// every downstream resolver-facing artifact emits under) — the
1621    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
1622    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
1623    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
1624    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
1625    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
1626    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
1627    /// maps onto every load-bearing downstream consumer the substrate
1628    /// carries — the [`Self::validate_deps`] per-entry
1629    /// [`Dep::validate`] + within-list dedup walk at
1630    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
1631    /// cross-list self-reference gate at caixa-core/src/layout.rs that
1632    /// checks each entry against the caixa's own `:nome`, the
1633    /// caixa-resolver `for dep in &root.deps` closure walk at
1634    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
1635    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
1636    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
1637    /// caixa-crd/src/conversion.rs that materializes each entry into the
1638    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
1639    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1640    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
1641    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
1642    /// closure emit walk the caixa-resolver docstring roadmaps).
1643    ///
1644    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1645    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1646    /// sibling `:deps-dev` future lift closes on. Peer of the closed
1647    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
1648    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
1649    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
1650    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
1651    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
1652    /// pattern onto a novel element-type axis (`Dep` composite vs the
1653    /// prior sibling family's `String` scalar). Sibling in shape to the
1654    /// peer per-`:supervisor`
1655    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1656    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1657    /// (a6e18d7), per-`:membros`
1658    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1659    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1660    /// (0dcc926), and per-`:upgrade-from :instructions`
1661    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1662    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1663    /// typed-slot list axes, extended here to the outer top-level
1664    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
1665    /// (not `&Vec<Dep>`) because every downstream consumer of the
1666    /// runtime-dep list treats it as a read-only sequence — the slice-
1667    /// view is the narrowest borrow that supports every present +
1668    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1669    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1670    /// of the typed view reaches for (the storage-side `Vec` remains
1671    /// reachable through the `pub deps` field for the mutation-carrying
1672    /// serde round-trip and per-test fixture-mutation paths). Named
1673    /// `deps()` to match the storage field's name; the accessor's
1674    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1675    /// slot's docstring already carries.
1676    #[must_use]
1677    pub fn deps(&self) -> &[Dep] {
1678        self.deps.as_slice()
1679    }
1680
1681    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
1682    /// development-only-dependency-declaration-list slice-accessor every
1683    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
1684    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
1685    /// slice-view over the same backing buffer the raw
1686    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
1687    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
1688    /// form supplies with an empty `()` when unset; the
1689    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
1690    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1691    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
1692    /// the returned `&[Dep]` degenerates to an empty slice on that arm
1693    /// without any silent `None` collapse).
1694    ///
1695    /// The `:deps-dev` slot carries the universal-axis dev-only
1696    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
1697    /// the author-facing sibling of `:deps` that every `defcaixa` form
1698    /// supplies to declare tests / lint / bench closures the runtime
1699    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
1700    /// axis every downstream test-facing artifact emits under, matching
1701    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
1702    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
1703    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
1704    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
1705    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
1706    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
1707    /// within-list duplicate `:nome` rejected through
1708    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
1709    /// load-bearing downstream consumer the substrate carries — the
1710    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
1711    /// dedup walk at caixa-core/src/manifest.rs, the
1712    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
1713    /// gate at caixa-core/src/layout.rs that checks each entry against
1714    /// the caixa's own `:nome`, the caixa-resolver
1715    /// `for dep in &root.deps_dev` closure walk at
1716    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
1717    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
1718    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
1719    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
1720    /// overlay the M4 CR materializer resolves per-CR, the future
1721    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
1722    /// roadmaps).
1723    ///
1724    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1725    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1726    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
1727    /// jointly close the two-list dep-graph surface every downstream
1728    /// resolver-facing consumer keys off (runtime `:deps` +
1729    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
1730    /// pair the [`Self::validate_deps`] gate already walks in canonical
1731    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
1732    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
1733    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
1734    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
1735    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
1736    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
1737    /// dev-dep composite-element axis (`Dep` composite, matching the
1738    /// [`Self::deps`] element type). Sibling in shape to the peer
1739    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1740    /// (bc92bce), per-`:placement`
1741    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1742    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1743    /// (6c77e36), per-`:contratos`
1744    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1745    /// per-`:upgrade-from :instructions`
1746    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1747    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1748    /// typed-slot list axes, folded here to the outer top-level
1749    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
1750    /// (not `&Vec<Dep>`) because every downstream consumer of the
1751    /// dev-dep list treats it as a read-only sequence — the slice-view
1752    /// is the narrowest borrow that supports every present +
1753    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1754    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1755    /// of the typed view reaches for (the storage-side `Vec` remains
1756    /// reachable through the `pub deps_dev` field for the mutation-
1757    /// carrying serde round-trip and per-test fixture-mutation paths).
1758    /// Named `deps_dev()` to match the storage field's `snake_case` name;
1759    /// the kebab-case author-surface tag `:deps-dev` is the same axis
1760    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
1761    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1762    /// docstring already carries.
1763    #[must_use]
1764    pub fn deps_dev(&self) -> &[Dep] {
1765        self.deps_dev.as_slice()
1766    }
1767
1768    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
1769    /// every consumer that walks one of the two dep-list axes keyed on a
1770    /// [`crate::dep::DepList`] discriminant reaches for — routes the
1771    /// `(list: DepList) -> &[Dep]` projection through one typed method on
1772    /// the substrate primitive rather than the prior open-coded
1773    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
1774    /// inline dispatch every per-axis walker would otherwise carry.
1775    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
1776    /// `&[Dep]` slice-view over the same backing buffer the sibling
1777    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
1778    /// accessors borrow from, preserving the empty-list-carrying invariant
1779    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
1780    /// are default-empty axes every `defcaixa` form supplies with an empty
1781    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
1782    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
1783    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
1784    /// returned `&[Dep]` degenerates to an empty slice on either arm
1785    /// without any silent `None` collapse).
1786    ///
1787    /// The [`crate::dep::DepList`] closed-set typed enum is the
1788    /// substrate's canonical discriminator for the "runtime-closure
1789    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
1790    /// consumer dispatches on — the compiler-checked exhaustiveness on
1791    /// the enum's `match` arms is the build-time guarantee that no future
1792    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
1793    /// that a future third dep-list axis (a `:deps-build` build-only
1794    /// closure once the substrate grows cross-artifact heterogeneous
1795    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
1796    /// consumer. Prior to this the read side carried two per-slot
1797    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
1798    /// typed dispatch that a per-axis walker could parametrise on, so
1799    /// every per-list walker (the [`Self::validate_deps`] per-list
1800    /// [`crate::render::insert_first_seen`] dedup walk, a future
1801    /// `feira app graph` per-list dep summary, a future M4 per-cluster
1802    /// dev-closure-audit overlay the CR materializer resolves per-CR)
1803    /// open-coded the same two-block "run over `:deps`, then run over
1804    /// `:deps-dev`" pattern — a silent duplication that a future third
1805    /// dep-list axis would have had to grow a third block at every site.
1806    ///
1807    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
1808    /// (359fba5) — closes the two-side dispatch symmetry on the outer
1809    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
1810    /// side, `deps_of` on the read side, both keyed on the same
1811    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
1812    /// the substrate primitive, thin projections at each consumer"
1813    /// discipline the sibling per-slot read accessors ([`Self::nome`]
1814    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
1815    /// the outer-[`Caixa`] typed-dispatch read surface.
1816    #[must_use]
1817    pub fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
1818        match list {
1819            crate::dep::DepList::Prod => self.deps(),
1820            crate::dep::DepList::Dev => self.deps_dev(),
1821        }
1822    }
1823
1824    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
1825    /// consumer that appends to one of the two dep-list axes keys off
1826    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
1827    /// method on the substrate primitive rather than the prior
1828    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
1829    /// else { &mut caixa.deps }` inline dispatch + open-coded
1830    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
1831    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
1832    /// a within-list name collision — the same `list: &'static str`
1833    /// diagnostic shape [`Self::validate_deps`]'s per-list
1834    /// [`crate::render::insert_first_seen`] walk raises on the peer
1835    /// parse-time within-list dedup axis, so a future author reading a
1836    /// `feira add` refusal and a `feira build` refusal reaches for the
1837    /// same corrective surface without switching diagnostic idioms.
1838    ///
1839    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
1840    /// closed-set typed carrier for the "runtime-closure `:deps` vs
1841    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
1842    /// dispatches on — the compiler-checked exhaustiveness on the
1843    /// enum's `match` arms is the build-time guarantee that no future
1844    /// per-list mutation-site regresses to a bare-`bool`-flag
1845    /// (`is_dev: bool`) inline dispatch that a future third
1846    /// dep-list axis (a `:deps-build` build-only closure once the
1847    /// substrate grows cross-artifact heterogeneous dep-graphs, per
1848    /// CAIXA-SDLC §I) would silently split at every consumer.
1849    ///
1850    /// Same "one typed dispatch on the substrate primitive, thin
1851    /// projections at each consumer" discipline the sibling per-slot
1852    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
1853    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
1854    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
1855    /// the substrate's first typed-mutation dispatch on the top-level
1856    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
1857    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
1858    /// diagnostic path routed no through-line back to the typed slot,
1859    /// so a future extension of either dep-list axis to a richer author
1860    /// surface (a per-cluster override the operator pins through a
1861    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
1862    /// roadmap acknowledges, an M4
1863    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
1864    /// admission-webhook that normalized the list at admission time)
1865    /// would have had to be threaded through the `feira add` mutation
1866    /// site in lockstep with every read consumer or one path would
1867    /// silently disagree with the other on which list a given dep lands
1868    /// in. Lifting the resolution rule to a typed method on the
1869    /// substrate primitive means every downstream dep-list-mutating
1870    /// consumer of the top-level manifest reaches for exactly one typed
1871    /// dispatch — the resolver's accept-set migrates as a unit on any
1872    /// future axis addition.
1873    ///
1874    /// # Errors
1875    ///
1876    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
1877    /// when another entry in the same list already carries the same
1878    /// `:nome` — the mutation is refused and the caller can surface the
1879    /// typed diagnostic to the author (the `feira add` verb routes the
1880    /// error through `anyhow::Error::from`, which preserves the
1881    /// canonical `#[error(...)]`-templated diagnostic body).
1882    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
1883        let target = match list {
1884            crate::dep::DepList::Prod => &mut self.deps,
1885            crate::dep::DepList::Dev => &mut self.deps_dev,
1886        };
1887        if target.iter().any(|d| d.nome() == dep.nome()) {
1888            return Err(DepError::DuplicateNome {
1889                nome: dep.nome().to_string(),
1890                list: list.as_str(),
1891            });
1892        }
1893        target.push(dep);
1894        Ok(())
1895    }
1896
1897    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
1898    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
1899    /// composite-reference accessor every consumer of the top-level
1900    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
1901    /// off — returns the author-declared `:limits` typed composite
1902    /// verbatim as an `Option<&LimitsSpec>` reference over the same
1903    /// backing storage the raw `self.limits.as_ref()` field access
1904    /// borrows from, with `None` naming the "no `:limits` block
1905    /// authored — every per-axis Lunatic-sandbox cap defers to the
1906    /// wasm-engine-default arm named on the per-axis
1907    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
1908    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
1909    /// docstrings" partition every downstream Servico-M2-overlay
1910    /// emitter treats as "emit nothing" and the sibling
1911    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
1912    /// treats as "skip the per-axis
1913    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
1914    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
1915    ///
1916    /// The outer `:limits` slot carries the M2 Servico-runtime typed
1917    /// composite — the load-bearing container of every Lunatic-shaped
1918    /// per-process wasm32-sandbox cap axis every long-running wasm
1919    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
1920    /// Lunatic per-process linear-memory / fuel / wall-clock /
1921    /// millicore cap primitives translated onto pleme-io's typed
1922    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
1923    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
1924    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
1925    /// chart both fan on). Every per-`:limits` axis threads through a
1926    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
1927    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
1928    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
1929    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
1930    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
1931    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
1932    /// consumer that reaches for a limits axis first passes through
1933    /// this outer accessor onto the composite and then dispatches
1934    /// onto the per-axis accessor — the two-level dispatch means
1935    /// every per-`:limits` reader now routes through a typed dispatch
1936    /// on the substrate primitive at both altitudes.
1937    ///
1938    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
1939    /// was accessed inline at three production sites — the
1940    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
1941    /// `if let Some(l) = &caixa.limits { … }` traversal head
1942    /// (caixa-core/src/layout.rs:882, which drives the per-axis
1943    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
1944    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
1945    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
1946    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
1947    /// [`LimitsSpec::validate`] fans onto), the
1948    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
1949    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
1950    /// head (caixa-core/src/render.rs:18504, which drives the
1951    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
1952    /// projection every `caixa-helm` / `caixa-flux` Servico values-
1953    /// block emitter fans on), and the
1954    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
1955    /// set enumerator's `self.limits.is_some()` presence probe
1956    /// (caixa-core/src/manifest.rs:1788, which drives the
1957    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
1958    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
1959    /// gate reads) — three open-coded outer-field accesses that
1960    /// expressed no compile-time link back to the typed slot at the
1961    /// [`Caixa`] altitude. A future extension of the `:limits` outer
1962    /// axis to a richer author surface (a multi-`:limits` list the M4
1963    /// CR materializer resolves per-CR at admission time so a Servico
1964    /// can expose a compute-heavy + IO-heavy limits pair, a per-
1965    /// cluster `:limits-overrides` slot the operator pins so a
1966    /// cluster-specific policy can tighten a caixa-declared cap
1967    /// without re-authoring the `caixa.lisp`, a promotion of the
1968    /// plain `Option<LimitsSpec>` to a richer
1969    /// `{static, dynamic}` partition once the wasm-engine's runtime-
1970    /// resolved dynamic-cap surface lands) would have had to be
1971    /// threaded through all three open-coded copies in lockstep or
1972    /// one consumer would silently disagree with the peers on which
1973    /// limits composite a given Caixa resolves to — the layout gate's
1974    /// per-axis bracket-dispatch seed reading the raw slot while the
1975    /// peer `servico_m2_overlay` emitter read an operator-resolved
1976    /// slot would silently split the build-time sandbox-shape gate
1977    /// from the runtime `ComputeUnit` CR emission gate, a three-
1978    /// consumer split at the layout gate, the M2 overlay emitter, and
1979    /// the declared-slot enumerator far from the source `caixa.lisp`
1980    /// with no field naming the limits-drift root cause. Lifting the
1981    /// resolution rule to a typed method on the substrate primitive
1982    /// means every downstream consumer of the caixa's per-`Caixa`
1983    /// Lunatic-sandboxing outer-composite surface reaches for exactly
1984    /// one typed dispatch — the resolver's accept-set migrates as a
1985    /// unit on any future axis addition.
1986    ///
1987    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
1988    /// composite-reference accessor — opens the outer-`Caixa`
1989    /// `Option<&Composite>` composite-reference projection pattern the
1990    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
1991    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
1992    /// [`crate::aplicacao::Placement`] / `:entrada`
1993    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
1994    /// fold on. Peer of the M3 mesh-slot outer-composite family the
1995    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
1996    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
1997    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
1998    /// accessors already close on the outer [`crate::AplicacaoSpec`]
1999    /// altitude — extends that "one typed dispatch on the substrate
2000    /// primitive, thin projections at each consumer" discipline onto
2001    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2002    /// runtime slot family's outer-composite axis. Returns
2003    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2004    /// clone) because every downstream consumer of the limits
2005    /// composite treats it as a read-only per-axis dispatch source —
2006    /// the reference-view is the narrowest borrow that supports every
2007    /// present + roadmapped consumer (per-axis accessor dispatch,
2008    /// `.is_empty()`-gated overlay projection, presence-probe early
2009    /// return on the "author-omitted `:limits` ⇒ engine-default
2010    /// applies" partition) without cloning the composite through
2011    /// every consumer's fast path. The `Option` half of the return-
2012    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2013    /// engine-default applies" partition (not a default composite the
2014    /// downstream must reject on emptiness) — the accessor projects
2015    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2016    /// reference-return unchanged. Named `limits()` to match the
2017    /// storage field's name verbatim and the tatara-lisp author-
2018    /// surface term (`:limits`) the field's own docstring already
2019    /// carries.
2020    #[must_use]
2021    pub fn limits(&self) -> Option<&LimitsSpec> {
2022        self.limits.as_ref()
2023    }
2024
2025    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2026    /// composite OTP-`gen_server`-shaped callback-table optional-
2027    /// composite-reference accessor every consumer of the top-level
2028    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2029    /// keys off — returns the author-declared `:behavior` typed
2030    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2031    /// the same backing storage the raw `self.behavior.as_ref()` field
2032    /// access borrows from, with `None` naming the "no `:behavior`
2033    /// block authored — every per-callback OTP-shaped hook defers to
2034    /// the wasm-engine's runtime default arm named on the per-axis
2035    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2036    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2037    /// [`BehaviorSpec::on_state_change`] /
2038    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2039    /// partition every downstream Servico-M2-overlay emitter treats as
2040    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2041    /// per-`:behavior` shape gate treats as "skip the per-arm
2042    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2043    /// per-callback on-disk `MissingEntry` existence check".
2044    ///
2045    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2046    /// composite — the load-bearing container of every OTP-shaped
2047    /// per-Servico lifecycle-callback path axis every long-running wasm
2048    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2049    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2050    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2051    /// translated onto pleme-io's typed `:behavior :on-init` /
2052    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2053    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2054    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2055    /// chart both fan on). Every per-`:behavior` axis threads through a
2056    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2057    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2058    /// Every downstream consumer that reaches for a behavior axis
2059    /// first passes through this outer accessor onto the composite
2060    /// and then dispatches onto the per-callback accessor — the
2061    /// two-level dispatch means every per-`:behavior` reader now
2062    /// routes through a typed dispatch on the substrate primitive at
2063    /// both altitudes.
2064    ///
2065    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2066    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2067    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2068    /// keys the "per-version `:state-change` instruction must have a
2069    /// `:on-state-change` callback" precondition off this accessor's
2070    /// composite (the callback-side counterpart to the
2071    /// `:upgrade-from :instructions :state-change :script` refusal at
2072    /// the appup-side). Threading that gate's traversal input through
2073    /// this accessor closes the cross-slot invariant on the substrate
2074    /// primitive, not on the raw field.
2075    ///
2076    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2077    /// composite was accessed inline at four production sites — the
2078    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2079    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2080    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2081    /// `BehaviorError` refusal cascade + the per-callback on-disk
2082    /// [`crate::LayoutError::MissingEntry`] existence check under
2083    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2084    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2085    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2086    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2087    /// drives the `:state-change` ↔ `:on-state-change` precondition
2088    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2089    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2090    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2091    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2092    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2093    /// Servico values-block emitter fans on), and the
2094    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2095    /// set enumerator's `self.behavior.is_some()` presence probe
2096    /// (caixa-core/src/manifest.rs:1919, which drives the
2097    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2098    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2099    /// gate reads) — four open-coded outer-field accesses that
2100    /// expressed no compile-time link back to the typed slot at the
2101    /// [`Caixa`] altitude. A future extension of the `:behavior`
2102    /// outer axis to a richer author surface (a per-callback overlay
2103    /// resolver the operator materializes at admission time so a
2104    /// cluster-specific policy can inject a per-callback tracing
2105    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2106    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2107    /// dynamic}` partition once a runtime-resolved behavior-swap
2108    /// surface lands, the M4 per-callback middleware chain the
2109    /// caixa-operator's per-Servico admission webhook keys off) would
2110    /// have had to be threaded through all four open-coded copies in
2111    /// lockstep or one consumer would silently disagree with the
2112    /// peers on which behavior composite a given Caixa resolves to —
2113    /// the layout gate's per-callback existence-check seed reading
2114    /// the raw slot while the peer `servico_m2_overlay` emitter read
2115    /// an operator-resolved slot would silently split the build-time
2116    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2117    /// gate from the cross-slot `:state-change` composition gate from
2118    /// the M2 declared-slot enumerator, a four-consumer split far
2119    /// from the source `caixa.lisp` with no field naming the
2120    /// behavior-drift root cause. Lifting the resolution rule to a
2121    /// typed method on the substrate primitive means every downstream
2122    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2123    /// composite surface reaches for exactly one typed dispatch — the
2124    /// resolver's accept-set migrates as a unit on any future axis
2125    /// addition.
2126    ///
2127    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2128    /// composite-reference accessor — sibling to the opening
2129    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2130    /// `Option<&Composite>` composite-reference sub-family, extends
2131    /// the "one typed dispatch on the substrate primitive, thin
2132    /// projections at each consumer" discipline onto the second of
2133    /// the three M2 Servico-runtime slots. The remaining
2134    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2135    /// altitude — the M3 mesh-slot family (`:politicas`,
2136    /// `:placement`, `:entrada` — already closed on the inner
2137    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2138    /// d32111c) — remain the future sibling lifts on the outer
2139    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2140    /// the owning composite by copy or clone) because every
2141    /// downstream consumer of the behavior composite treats it as a
2142    /// read-only per-callback dispatch source — the reference-view is
2143    /// the narrowest borrow that supports every present + roadmapped
2144    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2145    /// overlay projection, presence-probe early return on the
2146    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2147    /// partition, cross-slot `:state-change` composition input)
2148    /// without cloning the composite through every consumer's fast
2149    /// path. The `Option` half of the return-type preserves the
2150    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2151    /// applies" partition (not a default composite the downstream
2152    /// must reject on emptiness) — the accessor projects the raw
2153    /// `Option<BehaviorSpec>` slot's presence bit through the
2154    /// reference-return unchanged. Named `behavior()` to match the
2155    /// storage field's name verbatim and the tatara-lisp author-
2156    /// surface term (`:behavior`) the field's own docstring already
2157    /// carries.
2158    #[must_use]
2159    pub fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2160        self.behavior.as_ref()
2161    }
2162
2163    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2164    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2165    /// reference accessor every consumer of the top-level manifest's
2166    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2167    /// reader keys off — returns the author-declared `:politicas` typed
2168    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2169    /// same backing storage the raw `self.politicas.as_ref()` field
2170    /// access borrows from, with `None` naming the "no `:politicas`
2171    /// block authored — every per-axis mesh-policy scalar defers to the
2172    /// cluster-default arm named on the per-axis
2173    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2174    /// [`crate::aplicacao::MeshPolicy::retries`] /
2175    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2176    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2177    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2178    /// docstrings" partition every downstream caixa-mesh /
2179    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2180    /// "emit no per-`:politicas` overlay" and the sibling
2181    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2182    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2183    /// arm.
2184    ///
2185    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2186    /// Aplicacao typed composite — the load-bearing container of every
2187    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2188    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2189    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2190    /// composite; §V — the "no infinite blocking" per-call deadline +
2191    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2192    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2193    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2194    /// threads through a lifted per-slot accessor on the
2195    /// [`crate::aplicacao::MeshPolicy`] type: the
2196    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2197    /// mTLS-enforcement toggle, the
2198    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2199    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2200    /// (7073d0f) Gateway-API per-call deadline, the
2201    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2202    /// Envoy-outlier-detection composite. Every downstream consumer
2203    /// that reaches for a mesh-policy axis first passes through this
2204    /// outer accessor onto the composite and then dispatches onto the
2205    /// per-axis accessor — the two-level dispatch means every per-
2206    /// `:politicas` reader now routes through a typed dispatch on the
2207    /// substrate primitive at both altitudes.
2208    ///
2209    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2210    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2211    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2212    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2213    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2214    /// composite whether or not the author declared the outer slot.
2215    /// The outer accessor preserves the "author-omitted vs authored-
2216    /// empty" partition the inner accessor's `is_empty()`-gated
2217    /// renderer overlay collapses — routing the presence bit through
2218    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2219    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2220    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2221    ///
2222    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2223    /// composite was accessed inline at two production sites — the
2224    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2225    /// `self.politicas.clone().unwrap_or_default()` traversal head
2226    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2227    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2228    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2229    /// then observes), and the [`Self::declared_mesh_slots`] M3
2230    /// declared-slot-set enumerator's `self.politicas.is_some()`
2231    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2232    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2233    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2234    /// coherence gate reads) — two open-coded outer-field accesses
2235    /// that expressed no compile-time link back to the typed slot at
2236    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2237    /// outer axis to a richer author surface (a per-cluster
2238    /// `:politicas-overrides` slot the operator materializes at
2239    /// admission time so a cluster-specific policy can tighten the
2240    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2241    /// promotion of the plain `Option<MeshPolicy>` to a richer
2242    /// `{static, dynamic}` partition once the M4 per-edge
2243    /// contrato-scoped policy-override surface lands, the M5 traffic-
2244    /// shaping composition the caixa-operator's per-Aplicacao mesh
2245    /// admission webhook keys off) would have had to be threaded
2246    /// through both open-coded copies in lockstep or the Aplicacao-
2247    /// composition seed's default-fold arm would silently disagree
2248    /// with the M3 declared-slot enumerator on which policy composite
2249    /// a given Caixa resolves to — the seed reading an operator-
2250    /// resolved slot while the enumerator's presence probe read the
2251    /// raw slot would silently split the build-time mesh-artifact
2252    /// emission gate from the M3 declared-slot enumerator's kind-
2253    /// coherence gate, a two-consumer split far from the source
2254    /// `caixa.lisp` with no field naming the policy-drift root cause.
2255    /// Lifting the resolution rule to a typed method on the substrate
2256    /// primitive means every downstream consumer of the caixa's per-
2257    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2258    /// reaches for exactly one typed dispatch — the resolver's
2259    /// accept-set migrates as a unit on any future axis addition.
2260    ///
2261    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2262    /// composite-reference accessor — sibling to the opening
2263    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2264    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2265    /// reference sub-family, extends the "one typed dispatch on the
2266    /// substrate primitive, thin projections at each consumer"
2267    /// discipline onto the first of the three M3 mesh-slot axes.
2268    /// Peer of the closed inner mesh-slot outer-composite family the
2269    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2270    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2271    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2272    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2273    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2274    /// mesh-slot arm of the composite-reference family the remaining
2275    /// two axes (`:placement`, `:entrada`) fold onto in future
2276    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2277    /// composite by copy or clone) because every downstream consumer
2278    /// of the mesh-policy composite treats it as a read-only per-axis
2279    /// dispatch source — the reference-view is the narrowest borrow
2280    /// that supports every present + roadmapped consumer (per-axis
2281    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2282    /// presence-probe early return on the "author-omitted `:politicas`
2283    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2284    /// seed's default-fold arm) without cloning the composite through
2285    /// every consumer's fast path. The `Option` half of the return-
2286    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2287    /// cluster-default applies" partition (not a default composite
2288    /// the downstream must reject on emptiness) — the accessor
2289    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2290    /// through the reference-return unchanged. Named `politicas()` to
2291    /// match the storage field's name verbatim and the tatara-lisp
2292    /// author-surface term (`:politicas`) the field's own docstring
2293    /// already carries.
2294    #[must_use]
2295    pub fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2296        self.politicas.as_ref()
2297    }
2298
2299    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2300    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2301    /// reference accessor every consumer of the top-level manifest's
2302    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2303    /// reader keys off — returns the author-declared `:placement` typed
2304    /// composite verbatim as an `Option<&Placement>` reference over the
2305    /// same backing storage the raw `self.placement.as_ref()` field
2306    /// access borrows from, with `None` naming the "no `:placement`
2307    /// block authored — every per-axis placement scalar defers to the
2308    /// cluster-default arm named on the per-axis
2309    /// [`crate::aplicacao::Placement::estrategia`] /
2310    /// [`crate::aplicacao::Placement::clusters`] /
2311    /// [`crate::aplicacao::Placement::affinity`] /
2312    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2313    /// docstrings" partition every downstream caixa-mesh /
2314    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2315    /// "emit no per-`:placement` overlay" and the sibling
2316    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2317    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2318    ///
2319    /// The outer `:placement` slot carries the M3 mesh-slot per-
2320    /// Aplicacao typed distribution composite — the load-bearing
2321    /// container of every where-does-this-Aplicacao-run axis every
2322    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2323    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2324    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2325    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2326    /// Aplicacao's typed distribution composite; §V CSE invariants —
2327    /// "distribution is a first-class typed composite, not a runtime
2328    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2329    /// typed inter-Servico contrato-edge overlay the per-cluster
2330    /// mesh renderer keys off). Every per-`:placement` axis threads
2331    /// through a lifted per-slot accessor on the
2332    /// [`crate::aplicacao::Placement`] type: the
2333    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2334    /// MESH-COMPOSITION distribution-strategy scalar, the
2335    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2336    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2337    /// M3-Adaptive-compression-hint optional-scalar, and the
2338    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2339    /// sharding extractor-expression optional-scalar. Every downstream
2340    /// consumer that reaches for a placement axis first passes through
2341    /// this outer accessor onto the composite and then dispatches onto
2342    /// the per-axis accessor — the two-level dispatch means every per-
2343    /// `:placement` reader now routes through a typed dispatch on the
2344    /// substrate primitive at both altitudes.
2345    ///
2346    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2347    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2348    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2349    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2350    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2351    /// whether or not the author declared the outer slot. The outer
2352    /// accessor preserves the "author-omitted vs authored-empty" partition
2353    /// the inner accessor collapses at the cluster-default fold —
2354    /// routing the presence bit through this accessor keeps the
2355    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2356    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2357    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2358    /// dispatch.
2359    ///
2360    /// Prior to this lift the `.placement` `Option<Placement>`
2361    /// composite was accessed inline at two production sites — the
2362    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2363    /// `self.placement.clone().unwrap_or_default()` traversal head
2364    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2365    /// the [`crate::aplicacao::Placement::default`] cluster-default
2366    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2367    /// then observes), and the [`Self::declared_mesh_slots`] M3
2368    /// declared-slot-set enumerator's `self.placement.is_some()`
2369    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2370    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2371    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2372    /// coherence gate reads) — two open-coded outer-field accesses
2373    /// that expressed no compile-time link back to the typed slot at
2374    /// the [`Caixa`] altitude. A future extension of the `:placement`
2375    /// outer axis to a richer author surface (a per-cluster
2376    /// `:placement-overrides` slot the operator materializes at
2377    /// admission time so a cluster-specific placement can tighten the
2378    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2379    /// per-tenant placement-alias table the M4
2380    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2381    /// per-CR at admission time, a promotion of the plain
2382    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2383    /// once Orleans-style virtual-actor dynamic placement comes into
2384    /// typed scope) would have had to be threaded through both open-
2385    /// coded copies in lockstep or the Aplicacao-composition seed's
2386    /// default-fold arm would silently disagree with the M3 declared-
2387    /// slot enumerator on which distribution composite a given Caixa
2388    /// resolves to — the seed reading an operator-resolved slot while
2389    /// the enumerator's presence probe read the raw slot would
2390    /// silently split the build-time distribution-artifact emission
2391    /// gate from the M3 declared-slot enumerator's kind-coherence
2392    /// gate, a two-consumer split far from the source `caixa.lisp`
2393    /// with no field naming the distribution-drift root cause.
2394    /// Lifting the resolution rule to a typed method on the substrate
2395    /// primitive means every downstream consumer of the caixa's per-
2396    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2397    /// reaches for exactly one typed dispatch — the resolver's
2398    /// accept-set migrates as a unit on any future axis addition.
2399    ///
2400    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2401    /// composite-reference accessor — sibling to the opening
2402    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2403    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2404    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2405    /// composite-reference sub-family, folds on the "one typed
2406    /// dispatch on the substrate primitive, thin projections at each
2407    /// consumer" discipline extended onto the second of the three M3
2408    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2409    /// composite family the sibling
2410    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2411    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2412    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2413    /// accessor pins already close on the inner
2414    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2415    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2416    /// [`Self::politicas`] opened, extending the discipline onto the
2417    /// second of the three M3 mesh-slot axes. The remaining M3
2418    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2419    /// discipline in the final sibling lift, closing the outer top-
2420    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2421    /// Returns `Option<&Placement>` (not the owning composite by copy
2422    /// or clone) because every downstream consumer of the placement
2423    /// composite treats it as a read-only per-axis dispatch source —
2424    /// the reference-view is the narrowest borrow that supports every
2425    /// present + roadmapped consumer (per-axis accessor dispatch,
2426    /// serde composite-serialization on the programs.yaml overlay,
2427    /// presence-probe early return on the "author-omitted `:placement`
2428    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2429    /// seed's default-fold arm) without cloning the composite through
2430    /// every consumer's fast path. The `Option` half of the return-
2431    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2432    /// cluster-default applies" partition (not a default composite
2433    /// the downstream must reject on emptiness) — the accessor
2434    /// projects the raw `Option<Placement>` slot's presence bit
2435    /// through the reference-return unchanged. Named `placement()` to
2436    /// match the storage field's name verbatim and the tatara-lisp
2437    /// author-surface term (`:placement`) the field's own docstring
2438    /// already carries.
2439    #[must_use]
2440    pub fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2441        self.placement.as_ref()
2442    }
2443
2444    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2445    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2446    /// composite-reference accessor every consumer of the top-level
2447    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2448    /// composite reader keys off — returns the author-declared
2449    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2450    /// reference over the same backing storage the raw
2451    /// `self.entrada.as_ref()` field access borrows from, with `None`
2452    /// naming the "no `:entrada` block authored — this Aplicacao is
2453    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2454    /// partition every downstream caixa-mesh Gateway-API artifact
2455    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2456    /// backend for this Aplicacao" and the sibling
2457    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2458    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2459    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2460    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2461    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2462    /// the same `Option<&Entrada>` presence bit unchanged).
2463    ///
2464    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2465    /// Aplicacao typed external-gateway composite — the load-bearing
2466    /// container of every how-does-the-outside-world-reach-this-
2467    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2468    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2469    /// external-entry composite; §V CSE invariants — "the external
2470    /// gateway is a first-class typed composite, not a per-Servico
2471    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2472    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2473    /// API renderer keys off). Every per-`:entrada` axis threads
2474    /// through a lifted per-slot accessor on the
2475    /// [`crate::aplicacao::Entrada`] type: the
2476    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2477    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2478    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2479    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2480    /// backend `trigger.service.port` scalar, and the
2481    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2482    /// resolver every HTTPRoute-aware renderer consumes. Every
2483    /// downstream consumer that reaches for an entry axis first passes
2484    /// through this outer accessor onto the composite and then
2485    /// dispatches onto the per-axis accessor — the two-level dispatch
2486    /// means every per-`:entrada` reader now routes through a typed
2487    /// dispatch on the substrate primitive at both altitudes.
2488    ///
2489    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2490    /// seed: the Aplicacao-view builder forwards the outer `Option`
2491    /// arm verbatim (no default fold — `:entrada` is inherently
2492    /// optional; a cluster-internal Aplicacao has no external gateway
2493    /// at all, not "an external gateway that defaults to nothing"), so
2494    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2495    /// `Option<&Entrada>`-return accessor observes the same presence
2496    /// bit whether or not the author declared the outer slot. Routing
2497    /// the presence bit through this accessor keeps the
2498    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2499    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2500    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2501    /// hostname/backend/path emission dispatch.
2502    ///
2503    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2504    /// was accessed inline at two production sites — the
2505    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2506    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2507    /// which drives the forward onto the peer inner
2508    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2509    /// Gateway-API fan-out then observes), and the
2510    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2511    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2512    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2513    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2514    /// kind-coherence gate reads) — two open-coded outer-field
2515    /// accesses that expressed no compile-time link back to the typed
2516    /// slot at the [`Caixa`] altitude. A future extension of the
2517    /// `:entrada` outer axis to a richer author surface (a per-cluster
2518    /// `:entrada-overrides` slot the operator materializes at admission
2519    /// time so a cluster-specific hostname can pin the caixa-declared
2520    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2521    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2522    /// CR materializer resolves per-CR at admission time, a promotion
2523    /// of the plain `Option<Entrada>` to a richer
2524    /// `{public, private, internal}` partition once Cilium-identity-
2525    /// scoped internal gateways come into typed scope) would have had
2526    /// to be threaded through both open-coded copies in lockstep or the
2527    /// Aplicacao-composition seed's forward arm would silently
2528    /// disagree with the M3 declared-slot enumerator on which external-
2529    /// gateway composite a given Caixa resolves to — the seed reading
2530    /// an operator-resolved slot while the enumerator's presence probe
2531    /// read the raw slot would silently split the build-time gateway-
2532    /// artifact emission gate from the M3 declared-slot enumerator's
2533    /// kind-coherence gate, a two-consumer split far from the source
2534    /// `caixa.lisp` with no field naming the entry-drift root cause.
2535    /// Lifting the resolution rule to a typed method on the substrate
2536    /// primitive means every downstream consumer of the caixa's per-
2537    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2538    /// surface reaches for exactly one typed dispatch — the resolver's
2539    /// accept-set migrates as a unit on any future axis addition.
2540    ///
2541    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2542    /// return composite-reference accessor — closes the outer-`Caixa`
2543    /// `Option<&Composite>` composite-reference sub-family opened by
2544    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2545    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2546    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2547    /// folds on the "one typed dispatch on the substrate primitive,
2548    /// thin projections at each consumer" discipline extended onto the
2549    /// third and final M3 mesh-slot axis. Peer of the closed inner
2550    /// mesh-slot outer-composite family the sibling
2551    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2552    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2553    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2554    /// accessor pins already close on the inner
2555    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2556    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2557    /// altitudes of the outer-composite reference-return discipline
2558    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2559    /// slot presence) now carry the full five-arm accept-set behind a
2560    /// typed dispatch on the substrate primitive. Returns
2561    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2562    /// because every downstream consumer of the entrada composite
2563    /// treats it as a read-only per-axis dispatch source — the
2564    /// reference-view is the narrowest borrow that supports every
2565    /// present + roadmapped consumer (per-axis accessor dispatch,
2566    /// serde composite-serialization on the programs.yaml overlay,
2567    /// presence-probe early return on the "author-omitted `:entrada`
2568    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2569    /// seed's forward arm) without cloning the composite through every
2570    /// consumer's fast path. The `Option` half of the return-type
2571    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2572    /// cluster-internal Aplicacao" partition (not a default composite
2573    /// the downstream must reject on emptiness — a cluster-internal
2574    /// Aplicacao has no external gateway at all, not "a default gateway
2575    /// that emits nothing"); the accessor projects the raw
2576    /// `Option<Entrada>` slot's presence bit through the reference-
2577    /// return unchanged. Named `entrada()` to match the storage field's
2578    /// name verbatim and the tatara-lisp author-surface term
2579    /// (`:entrada`) the field's own docstring already carries.
2580    #[must_use]
2581    pub fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
2582        self.entrada.as_ref()
2583    }
2584
2585    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
2586    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
2587    /// an `Option<&CiRun>`, borrowed from the typed slot's own
2588    /// `Option<CiRun>` storage. `None` when the slot is absent (every
2589    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
2590    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
2591    /// not silently accepted).
2592    ///
2593    /// Named `ci()` to match the storage field's name and the
2594    /// tatara-lisp author surface (`:ci`); mirrors the sibling
2595    /// `Option<&Composite>` accessors on this same `Caixa` altitude
2596    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
2597    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
2598    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
2599    /// at every consumer.
2600    #[must_use]
2601    pub fn ci(&self) -> Option<&canteiro_types::CiRun> {
2602        self.ci.as_ref()
2603    }
2604
2605    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
2606    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
2607    /// accessor every consumer of the top-level manifest's per-Supervisor
2608    /// restart-strategy axis keys off — returns the author-declared
2609    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
2610    /// `Copy`-projected from the typed slot's own
2611    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
2612    /// (`:estrategia` is a flat-spread supervisor-only slot every
2613    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
2614    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2615    /// still omit to defer to [`RestartStrategy::default`] —
2616    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
2617    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
2618    /// [`SupervisorSpec::default`]-inherited strategy without any silent
2619    /// promotion to a fresh explicit variant at the accessor boundary).
2620    ///
2621    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
2622    /// restart-strategy discriminant every substrate-side per-Supervisor
2623    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
2624    /// closed-set `one_for_one | one_for_all | rest_for_one |
2625    /// simple_one_for_one` algebra translated onto pleme-io's typed
2626    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
2627    /// slot algebra the operator's hierarchical reconciliation scheduler
2628    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
2629    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
2630    /// supervisor slots are flat on Caixa (vs nested under a
2631    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
2632    /// level of nesting"), so the accessor's altitude is the outer
2633    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
2634    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
2635    /// (eafb619) accessor keys off. The two typed axes — the outer
2636    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
2637    /// (author-omitted arm carried as `None`) and the inner post-
2638    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
2639    /// (`Option` collapsed through the [`Self::supervisor_view`]
2640    /// `unwrap_or_default()` fold) — now share one accessor discipline for
2641    /// the shared substrate concept "the author-declared OTP-shaped
2642    /// sibling-restart-strategy variant that partitions the downstream
2643    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
2644    /// `None` arm is the pre-composition presence bit every declared-slot
2645    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
2646    /// inner-altitude non-`Option` `RestartStrategy` is the post-
2647    /// composition partition-dispatch input every strategy-arm consumer
2648    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
2649    /// Supervisor sibling-restart branch, the future M4
2650    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
2651    /// webhook) fans on.
2652    ///
2653    /// Prior to this lift the `.estrategia` field was accessed inline at
2654    /// two production sites in `caixa-core/src/manifest.rs` — the
2655    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
2656    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
2657    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2658    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
2659    /// `SupervisorSpec` construction site at `estrategia:
2660    /// self.estrategia.unwrap_or_default()` (which composes the flat-
2661    /// spread outer author-surface `Option<RestartStrategy>` onto the
2662    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
2663    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
2664    /// coded field-accesses that expressed no compile-time link back to
2665    /// the typed slot. A future extension of the outer `:estrategia` axis
2666    /// to a richer author surface (a per-cluster strategy override the
2667    /// operator pins through a future `:estrategia-overrides` overlay the
2668    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
2669    /// a per-tenant strategy-alias table the M4 CR materializer resolves
2670    /// per-CR, a per-Supervisor dynamic strategy derivation the future
2671    /// adaptive-supervision engine computes from child-failure-history
2672    /// topology, a per-child-cohort strategy split the future
2673    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
2674    /// absorption roadmap acknowledges, a promotion of the plain
2675    /// `Option<RestartStrategy>` to a richer
2676    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
2677    /// operator-resolved overlay lands) would have had to be threaded
2678    /// through both open-coded copies in lockstep or the enumerator's
2679    /// presence probe and the composition site's `unwrap_or_default()`
2680    /// fold would silently disagree on which strategy a given [`Caixa`]
2681    /// resolves to (an author's `:estrategia OneForAll` would satisfy
2682    /// the enumerator's presence probe while the composition site
2683    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
2684    /// the resolution rule to a typed method on the substrate primitive
2685    /// means every downstream consumer of the caixa's per-`Caixa` outer-
2686    /// altitude sibling-restart-strategy surface reaches for exactly one
2687    /// typed dispatch — the resolver's accept-set migrates as a unit on
2688    /// any future axis addition.
2689    ///
2690    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2691    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2692    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
2693    /// projection pattern the sibling per-`Caixa` `:max-restarts`
2694    /// `Option<u32>` and (through the future duration-newtype landing)
2695    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
2696    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
2697    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
2698    /// the post-composition [`SupervisorSpec`] altitude — same "one
2699    /// typed dispatch on the substrate primitive, thin projections at
2700    /// each consumer" discipline extended onto the pre-composition outer
2701    /// author-surface [`Caixa`] altitude for the same OTP-shaped
2702    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
2703    /// `Option<&Composite>` composite-reference family the sibling
2704    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
2705    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
2706    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
2707    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
2708    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
2709    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
2710    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2711    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
2712    /// pins on the inner-altitude per-`:placement` composite. Named
2713    /// `estrategia()` to match the storage field's name and the
2714    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
2715    /// / per-[`crate::aplicacao::Placement`] peer
2716    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
2717    /// verbatim; the accessor's identity name maps onto the canonical
2718    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
2719    /// docstring already carries.
2720    #[must_use]
2721    pub fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
2722        self.estrategia
2723    }
2724
2725    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
2726    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
2727    /// scalar accessor every consumer of the top-level manifest's per-
2728    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
2729    /// returns the author-declared `:max-restarts` typed `Option<u32>`
2730    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
2731    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
2732    /// accessor returns by value; no borrow of `&self` past the call).
2733    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
2734    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
2735    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2736    /// still omit to defer to the [`Self::supervisor_view`]
2737    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
2738    ///
2739    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
2740    /// `MaxIntensity` restart-budget count that pairs with the sibling
2741    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
2742    /// restart-intensity ratio the supervisor trips its own escalation on
2743    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
2744    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
2745    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
2746    /// reconciliation scheduler fans on). The slot is *flat-spread* on
2747    /// the outer top-level `Caixa` (per the field-shape docstring at
2748    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
2749    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
2750    /// accessor's altitude is the outer [`Caixa`] surface rather than the
2751    /// composed [`SupervisorSpec`] altitude the sibling
2752    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
2753    /// off. The two typed axes — the outer author-surface `Option<u32>`
2754    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
2755    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
2756    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
2757    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
2758    /// shared substrate concept "the author-declared OTP-shaped
2759    /// restart-budget count every downstream per-Supervisor consumer's
2760    /// restart-intensity budget-vs-count comparator fans on".
2761    ///
2762    /// Prior to this lift the `.max_restarts` field was accessed inline
2763    /// at two production sites in `caixa-core/src/manifest.rs` — the
2764    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
2765    /// presence-probe arm at `if self.max_restarts.is_some()` (which
2766    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
2767    /// kind-coherence gate's per-slot label push) and the
2768    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
2769    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
2770    /// flat-spread outer author-surface `Option<u32>` onto the inner
2771    /// post-composition [`SupervisorSpec`] `u32` field the
2772    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
2773    /// coded field-accesses that expressed no compile-time link back to
2774    /// the typed slot. A future extension of the outer `:max-restarts`
2775    /// axis to a richer author surface (a per-cluster restart-budget
2776    /// override the operator pins through a future `:max-restarts-overrides`
2777    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
2778    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
2779    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
2780    /// budget derivation the future adaptive-supervision engine computes
2781    /// from child-failure-history topology, a promotion of the plain
2782    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
2783    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
2784    /// per-child-cohort roadmap lands) would have had to be threaded
2785    /// through both open-coded copies in lockstep or the enumerator's
2786    /// presence probe and the composition site's `unwrap_or(5)` fold
2787    /// would silently disagree on which restart-budget a given [`Caixa`]
2788    /// resolves to (an author's `:max-restarts 10` would satisfy the
2789    /// enumerator's presence probe while the composition site silently
2790    /// composed the OTP-canonical `5`, or vice versa). Lifting the
2791    /// resolution rule to a typed method on the substrate primitive means
2792    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
2793    /// restart-budget-count surface reaches for exactly one typed dispatch
2794    /// — the resolver's accept-set migrates as a unit on any future axis
2795    /// addition.
2796    ///
2797    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2798    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2799    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
2800    /// projection pattern the sibling per-`Caixa`
2801    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
2802    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
2803    /// Peer of the inner-altitude
2804    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
2805    /// on the post-composition [`SupervisorSpec`] altitude — same "one
2806    /// typed dispatch on the substrate primitive, thin projections at
2807    /// each consumer" discipline extended onto the pre-composition outer
2808    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
2809    /// shaped restart-budget-count axis. Named `max_restarts()` to match
2810    /// the storage field's name and the per-[`SupervisorSpec`] peer
2811    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
2812    /// discipline verbatim; the accessor's identity maps onto the
2813    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
2814    /// field's docstring already carries.
2815    #[must_use]
2816    pub const fn max_restarts(&self) -> Option<u32> {
2817        self.max_restarts
2818    }
2819
2820    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
2821    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
2822    /// denominator raw-duration-string scalar accessor every consumer of
2823    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
2824    /// window axis keys off — returns the author-declared `:restart-window`
2825    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
2826    /// from the typed slot's own `Option<String>` storage. `None` when
2827    /// the slot is absent (the canonical "never reset — every restart
2828    /// across the supervisor's lifetime counts against the sibling
2829    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
2830    /// `defcaixa` carries by `#[serde(default)]` and every
2831    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
2832    /// [`Self::supervisor_view`] `restart_window: None` composition
2833    /// through the [`crate::supervisor::duration_codec::parse`] soft-
2834    /// swallow `.and_then(|s| … .ok())` fold).
2835    ///
2836    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
2837    /// shaped `Period` sliding-observation-interval duration string that
2838    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
2839    /// budget count to form the `MaxIntensity / Period` restart-intensity
2840    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
2841    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
2842    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
2843    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
2844    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
2845    /// holds an `Option<Duration>` routed through the shared
2846    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
2847    /// — so the outer altitude's accessor returns `Option<&str>` (raw
2848    /// authoring surface) while the inner altitude's
2849    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
2850    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
2851    /// is closed by the sibling [`Self::validate_restart_window`] gate
2852    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
2853    /// the offending value; the view-construction path
2854    /// [`Self::supervisor_view`] soft-swallows the same parse error to
2855    /// `None` to keep the view best-effort.
2856    ///
2857    /// Prior to this lift the `.restart_window` field was accessed inline
2858    /// at three production sites in `caixa-core/src/manifest.rs` — the
2859    /// [`Self::declared_supervisor_slots`]
2860    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
2861    /// `if self.restart_window.is_some()` (which drives the
2862    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2863    /// coherence gate's per-slot label push), the
2864    /// [`Self::validate_restart_window`] `let Some(s) =
2865    /// self.restart_window.as_deref()` empty-and-shape gate binding
2866    /// (which folds the raw string through the shared
2867    /// [`crate::supervisor::duration_codec::parse`] to surface
2868    /// [`ManifestError::RestartWindowMalformed`] naming the offending
2869    /// value), and the [`Self::supervisor_view`] `self.restart_window
2870    /// .as_deref().and_then(…)` view-construction fold (which composes
2871    /// the flat-spread outer author-surface `Option<String>` onto the
2872    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
2873    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
2874    /// three open-coded field-accesses that expressed no compile-time
2875    /// link back to the typed slot. A future extension of the outer
2876    /// `:restart-window` axis to a richer author surface (a per-cluster
2877    /// window override, a per-tenant window-alias table, a per-Supervisor
2878    /// dynamic window derivation the future adaptive-supervision engine
2879    /// computes from child-failure-history topology, a promotion of the
2880    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
2881    /// once the future author-surface parser lands at the [`Caixa`]
2882    /// altitude and the raw-string form is retired) would have had to be
2883    /// threaded through every open-coded copy in lockstep or the three
2884    /// consumers would silently disagree on which raw string a given
2885    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
2886    /// method on the substrate primitive means every downstream consumer
2887    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
2888    /// string surface reaches for exactly one typed dispatch — the
2889    /// resolver's accept-set migrates as a unit on any future axis
2890    /// addition.
2891    ///
2892    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
2893    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
2894    /// spread projection pattern the sibling per-`Caixa`
2895    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
2896    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
2897    /// the sub-family onto the sibling `Option<&str>` raw-duration-
2898    /// string arm (the outer altitude's raw-string form; the inner
2899    /// altitude's parsed [`Duration`] form is the peer
2900    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
2901    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
2902    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
2903    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
2904    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
2905    /// sub-family already carries — same "one typed dispatch on the
2906    /// substrate primitive, thin projections at each consumer"
2907    /// discipline extended onto the M2 supervisor-tree flat-spread
2908    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
2909    /// to match the storage field's name and the per-[`SupervisorSpec`]
2910    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
2911    /// method-name discipline verbatim; the accessor's identity maps
2912    /// onto the canonical OTP-shape supervision vocabulary the
2913    /// `:restart-window` field's docstring already carries.
2914    #[must_use]
2915    pub fn restart_window(&self) -> Option<&str> {
2916        self.restart_window.as_deref()
2917    }
2918
2919    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
2920    /// outer-composite OTP-appup-shaped per-prior-version migration-
2921    /// entry-list slice accessor every consumer of the top-level
2922    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
2923    /// slice-view keys off — returns the author-declared `:upgrade-from`
2924    /// typed `Vec<UpgradeFromEntry>` verbatim as a
2925    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
2926    /// the raw `self.upgrade_from.as_slice()` field access borrows
2927    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
2928    /// arm every `defcaixa` without an `:upgrade-from` block carries;
2929    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
2930    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
2931    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
2932    /// possibly empty — and the returned `&[UpgradeFromEntry]`
2933    /// degenerates to an empty slice on that arm without any silent
2934    /// `None` collapse).
2935    ///
2936    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
2937    /// migration block — the load-bearing container of every per-
2938    /// prior-`:versao` migration-instruction list the wasm-operator
2939    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
2940    /// `.appup` per-prior-version `LoadModule | StateChange |
2941    /// SoftPurge | Purge | Restart` instruction algebra translated
2942    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
2943    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
2944    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
2945    /// threads through a lifted per-entry accessor on the
2946    /// [`UpgradeFromEntry`] type: the
2947    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
2948    /// version scalar accessor and the
2949    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
2950    /// return per-entry instruction-list accessor (0137e5a). Every
2951    /// downstream consumer of the hot-upgrade path first passes
2952    /// through this outer accessor onto the slice and then dispatches
2953    /// per-entry through the inner accessors — the two-level dispatch
2954    /// means every per-`:upgrade-from` reader now routes through a
2955    /// typed dispatch on the substrate primitive at both altitudes.
2956    ///
2957    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
2958    /// slot was accessed inline at production sites across three
2959    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
2960    /// enumerator's `self.upgrade_from.is_empty()` presence probe
2961    /// (caixa-core/src/manifest.rs, which drives the
2962    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
2963    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2964    /// gate reads), the [`crate::StandardLayout::verify`] per-
2965    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
2966    /// layout.rs, which fans onto the
2967    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
2968    /// cross-entry duplicate gate, the
2969    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
2970    /// SemVer-precedence cross-slot gate, the
2971    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2972    /// `:state-change` ↔ `:on-state-change` cross-slot composition
2973    /// gate, and the per-instruction script-path existence-probe walk
2974    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
2975    /// resolve every declared migration script against the layout
2976    /// root), and the [`crate::render::servico_m2_overlay`] per-
2977    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
2978    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
2979    /// projection (caixa-core/src/render.rs, which drives the
2980    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
2981    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
2982    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
2983    /// A future extension of the outer `:upgrade-from` axis (a per-
2984    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
2985    /// resolves at admission time so a cluster-specific migration
2986    /// policy can tighten a caixa-declared step without re-authoring
2987    /// the `caixa.lisp`, promotion of the plain
2988    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
2989    /// partition once runtime-resolved hot-upgrade instructions land,
2990    /// per-entry priority annotation once multi-strategy fan-out
2991    /// lands) would have had to be threaded through all six open-
2992    /// coded copies in lockstep or one consumer would silently
2993    /// disagree with the peers on which upgrade slice a given Caixa
2994    /// resolves to — a six-consumer split at the enumerator, the
2995    /// three-stage validate pass, the script-path probe walk, and the
2996    /// M2 overlay emitter, far from the source `caixa.lisp` with no
2997    /// field naming the upgrade-drift root cause. Lifting the
2998    /// resolution rule to a typed method on the substrate primitive
2999    /// means every downstream consumer of the caixa's per-`Caixa`
3000    /// OTP-appup outer-slice surface reaches for exactly one typed
3001    /// dispatch — the resolver's accept-set migrates as a unit on any
3002    /// future axis addition.
3003    ///
3004    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3005    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3006    /// outer-`Caixa` `&[Composite]` composite-slice projection
3007    /// pattern the sibling `:children`
3008    /// [`crate::supervisor::ChildSpec`] / `:membros`
3009    /// [`crate::aplicacao::Membro`] / `:contratos`
3010    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3011    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3012    /// `Option<&Composite>` composite-reference family the sibling
3013    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3014    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3015    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3016    /// `Option<&Composite>` altitude, extended here to the outer-
3017    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3018    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3019    /// (0137e5a) — same "one typed dispatch on the substrate
3020    /// primitive, thin projections at each consumer" discipline
3021    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3022    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3023    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3024    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3025    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3026    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3027    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3028    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3029    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3030    /// slice" projection pattern onto the sibling M2 typed-composite-
3031    /// element axis (`UpgradeFromEntry` composite, matching the
3032    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3033    /// different altitude).
3034    ///
3035    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3036    /// because every downstream consumer of the hot-upgrade list
3037    /// treats it as a read-only sequence — the slice-view is the
3038    /// narrowest borrow that supports every present + roadmapped
3039    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3040    /// serialization through
3041    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3042    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3043    /// the typed view reaches for (the storage-side `Vec` remains
3044    /// reachable through the `pub upgrade_from` field for the
3045    /// mutation-carrying serde round-trip and per-test fixture-
3046    /// mutation paths). Named `upgrade_from()` to match the storage
3047    /// field's `snake_case` name; the kebab-case author-surface tag
3048    /// `:upgrade-from` is the same axis after tatara-lisp's
3049    /// kebab↔snake fold and the accessor's identity maps onto the
3050    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3051    /// already carries.
3052    #[must_use]
3053    pub fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3054        self.upgrade_from.as_slice()
3055    }
3056
3057    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3058    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3059    /// slice accessor every consumer of the top-level manifest's per-
3060    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3061    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3062    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3063    /// the same backing buffer the raw `self.children.as_slice()` field
3064    /// access borrows from. Empty-slice-carrying (the "no static children
3065    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3066    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3067    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3068    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3069    /// on those arms without any silent `None` collapse).
3070    ///
3071    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3072    /// static-child list — the load-bearing container of every per-
3073    /// child `{caixa, versao, restart}` triple the wasm-operator's
3074    /// hierarchical reconciler dispatches on at supervisor-tree
3075    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3076    /// static-child list translated onto pleme-io's typed
3077    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3078    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3079    /// dispatch fans on). Every per-child axis threads through a lifted
3080    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3081    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3082    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3083    /// version-requirement scalar accessor, and the
3084    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3085    /// per-child post-exit restart-decision-policy discriminant
3086    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3087    /// tree path first passes through this outer accessor onto the
3088    /// slice and then dispatches per-child through the inner accessors
3089    /// — the two-level dispatch means every per-`:children` reader now
3090    /// routes through a typed dispatch on the substrate primitive at
3091    /// both altitudes.
3092    ///
3093    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3094    /// accessed inline at three production sites across two files —
3095    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3096    /// declared-slot enumerator's `!self.children.is_empty()` presence
3097    /// probe (caixa-core/src/manifest.rs, which drives the
3098    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3099    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3100    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3101    /// per-supervisor typed-view composer's `self.children.clone()`
3102    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3103    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3104    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3105    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3106    /// `:children :caixa` self-parent refusal probe's
3107    /// `&caixa.children`-borrowed
3108    /// [`crate::supervisor::validate_no_self_supervision`] input
3109    /// (caixa-core/src/layout.rs, which pins the "no child names the
3110    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3111    /// extension of the outer `:children` axis (a per-cluster
3112    /// `:children-overrides` overlay the wasm-engine operator resolves
3113    /// at admission time so a cluster-specific child-set can tighten
3114    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3115    /// promotion of the plain `Vec<ChildSpec>` to a richer
3116    /// `{static, dynamic}` partition once Erlang/OTP's
3117    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3118    /// axis, per-child priority annotation once multi-strategy fan-out
3119    /// lands) would have had to be threaded through all three open-
3120    /// coded copies in lockstep or one consumer would silently
3121    /// disagree with the peers on which child slice a given Caixa
3122    /// resolves to — the enumerator's presence probe reading the raw
3123    /// slot while the peer view-composer's fold-in path read an
3124    /// operator-resolved slot would silently split the paired
3125    /// declared-slot enumerator and typed-view composition, and the
3126    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3127    /// refusal probe reading a third borrow would silently drift the
3128    /// cross-slot coherence gate's traversal input from the two peers,
3129    /// a three-consumer split at the enumerator, the view composer,
3130    /// and the self-parent gate far from the source `caixa.lisp` with
3131    /// no field naming the child-set-drift root cause. Lifting the
3132    /// resolution rule to a typed method on the substrate primitive
3133    /// means every downstream consumer of the caixa's per-`Caixa`
3134    /// OTP-supervisor outer-slice surface reaches for exactly one
3135    /// typed dispatch — the resolver's accept-set migrates as a unit
3136    /// on any future axis addition.
3137    ///
3138    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3139    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3140    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3141    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3142    /// at the outer altitude of the closed inner-`SupervisorSpec`
3143    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3144    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3145    /// borrow-shared" outer-accessor discipline extended onto the
3146    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3147    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3148    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3149    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3150    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3151    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3152    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3153    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3154    /// M2 typed-composite-element axis
3155    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3156    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3157    /// different altitude).
3158    ///
3159    /// Returns `&[crate::supervisor::ChildSpec]` (not
3160    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3161    /// child list treats it as a read-only sequence — the slice-view
3162    /// is the narrowest borrow that supports every present +
3163    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3164    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3165    /// input, `serde` slice-serialization) without leaking the backing
3166    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3167    /// reaches for (the storage-side `Vec` remains reachable through
3168    /// the `pub children` field for the mutation-carrying serde round-
3169    /// trip and per-test fixture-mutation paths, including the
3170    /// [`Self::supervisor_view`] fold-in path that clones the slot
3171    /// into the typed view). Named `children()` to match the storage
3172    /// field's name verbatim and the tatara-lisp author-surface term
3173    /// (`:children`) the field's own docstring already carries; the
3174    /// accessor's identity maps onto the canonical OTP supervision
3175    /// vocabulary the [`Caixa::children`] field's docstring already
3176    /// reaches for ("Static children of a supervisor").
3177    #[must_use]
3178    pub fn children(&self) -> &[crate::supervisor::ChildSpec] {
3179        self.children.as_slice()
3180    }
3181
3182    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3183    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3184    /// accessor every consumer of the top-level manifest's per-Aplicacao
3185    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3186    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3187    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3188    /// same backing buffer the raw `self.membros.as_slice()` field access
3189    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3190    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3191    /// and every partially-authored Aplicacao carries before the
3192    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3193    /// `&[Membro]` degenerates to an empty slice on those arms without any
3194    /// silent `None` collapse).
3195    ///
3196    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3197    /// per-Aplicacao member list — the load-bearing container of every
3198    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3199    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3200    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3201    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3202    /// the `:entrada :para` external-gateway destination validates
3203    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3204    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3205    /// threads through a lifted per-entry accessor on the
3206    /// [`crate::aplicacao::Membro`] type: the
3207    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3208    /// identity scalar accessor (4a32abf) and the peer
3209    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3210    /// version-requirement scalar accessor (a40b0e3). Every downstream
3211    /// consumer of the mesh-graph path first passes through this outer
3212    /// accessor onto the slice and then dispatches per-member through
3213    /// the inner accessors — the two-level dispatch means every per-
3214    /// `:membros` reader now routes through a typed dispatch on the
3215    /// substrate primitive at both altitudes.
3216    ///
3217    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3218    /// inline at three production sites across two files — the
3219    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3220    /// enumerator's `!self.membros.is_empty()` presence probe
3221    /// (caixa-core/src/manifest.rs, which drives the
3222    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3223    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3224    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3225    /// composer's `self.membros.clone()` per-member fold-in path
3226    /// (caixa-core/src/manifest.rs, which materializes the typed
3227    /// [`crate::aplicacao::AplicacaoSpec`] view every
3228    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3229    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3230    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3231    /// [`crate::aplicacao::validate_no_self_membership`] input
3232    /// (caixa-core/src/layout.rs, which pins the "no member names the
3233    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3234    /// extension of the outer `:membros` axis (a per-cluster
3235    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3236    /// admission time so a cluster-specific member-set can tighten a
3237    /// caixa-declared list without re-authoring the `caixa.lisp`,
3238    /// promotion of the plain `Vec<Membro>` to a richer
3239    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3240    /// members land as a typed axis, per-member priority annotation once
3241    /// multi-strategy fan-out lands) would have had to be threaded
3242    /// through all three open-coded copies in lockstep or one consumer
3243    /// would silently disagree with the peers on which member slice a
3244    /// given Caixa resolves to — the enumerator's presence probe reading
3245    /// the raw slot while the peer view-composer's fold-in path read an
3246    /// operator-resolved slot would silently split the paired
3247    /// declared-slot enumerator and typed-view composition, and the
3248    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3249    /// refusal probe reading a third borrow would silently drift the
3250    /// cross-slot coherence gate's traversal input from the two peers, a
3251    /// three-consumer split at the enumerator, the view composer, and
3252    /// the self-membership gate far from the source `caixa.lisp` with no
3253    /// field naming the member-set-drift root cause. Lifting the
3254    /// resolution rule to a typed method on the substrate primitive
3255    /// means every downstream consumer of the caixa's per-`Caixa`
3256    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3257    /// typed dispatch — the resolver's accept-set migrates as a unit on
3258    /// any future axis addition.
3259    ///
3260    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3261    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3262    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3263    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3264    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3265    /// altitude. Peer at the outer altitude of the closed inner-
3266    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3267    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3268    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3269    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3270    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3271    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3272    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3273    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3274    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3275    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3276    /// pattern onto the sibling M3 typed-composite-element axis
3277    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3278    /// [`crate::AplicacaoSpec::membros`] element type at a different
3279    /// altitude).
3280    ///
3281    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3282    /// because every downstream consumer of the member list treats it
3283    /// as a read-only sequence — the slice-view is the narrowest borrow
3284    /// that supports every present + roadmapped consumer (`.iter()`,
3285    /// `.len()`, `.is_empty()`, the
3286    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3287    /// input, `serde` slice-serialization) without leaking the backing
3288    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3289    /// reaches for (the storage-side `Vec` remains reachable through the
3290    /// `pub membros` field for the mutation-carrying serde round-trip
3291    /// and per-test fixture-mutation paths, including the
3292    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3293    /// the typed view). Named `membros()` to match the storage field's
3294    /// name verbatim and the tatara-lisp author-surface term
3295    /// (`:membros`) the field's own docstring already carries; the
3296    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3297    /// vocabulary the [`Caixa::membros`] field's docstring already
3298    /// reaches for ("Member Servicos that make up this Aplicacao").
3299    #[must_use]
3300    pub fn membros(&self) -> &[crate::aplicacao::Membro] {
3301        self.membros.as_slice()
3302    }
3303
3304    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3305    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3306    /// inter-Servico contract-list slice accessor every consumer of the
3307    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3308    /// slice-view keys off — returns the author-declared `:contratos`
3309    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3310    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3311    /// backing buffer the raw `self.contratos.as_slice()` field access
3312    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3313    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3314    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3315    /// single member with no inter-Servico edge carries; the returned
3316    /// `&[WitContract]` degenerates to an empty slice on those arms
3317    /// without any silent `None` collapse).
3318    ///
3319    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3320    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3321    /// container of every per-edge `{de, para, wit, endpoint | subject |
3322    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3323    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3324    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3325    /// adjacency-list seed dispatch on at mesh-artifact materialization
3326    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3327    /// `:membros` vertex set resolves against, closed by the
3328    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3329    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3330    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3331    /// per-edge axis threads through a lifted per-entry accessor on the
3332    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3333    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3334    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3335    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3336    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3337    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3338    /// and the WIT-world discriminant. Every downstream consumer of the
3339    /// mesh-graph edge path first passes through this outer accessor
3340    /// onto the slice and then dispatches per-contract through the
3341    /// inner accessors — the two-level dispatch means every
3342    /// per-`:contratos` reader now routes through a typed dispatch on
3343    /// the substrate primitive at both altitudes.
3344    ///
3345    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3346    /// accessed inline at two production sites in
3347    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3348    /// mesh-slot declared-slot enumerator's
3349    /// `!self.contratos.is_empty()` presence probe (which drives the
3350    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3351    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3352    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3353    /// typed-view composer's `self.contratos.clone()` per-contract
3354    /// fold-in path (which materializes the typed
3355    /// [`crate::aplicacao::AplicacaoSpec`] view every
3356    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3357    /// downstream `caixa-mesh` renderer dispatches on). A future
3358    /// extension of the outer `:contratos` axis (a per-cluster
3359    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3360    /// at admission time so a cluster-specific edge-set can tighten a
3361    /// caixa-declared list without re-authoring the `caixa.lisp`,
3362    /// promotion of the plain `Vec<WitContract>` to a richer
3363    /// `{static, dynamic}` partition once runtime-resolved contract
3364    /// edges land, per-edge policy annotation once the M4 per-edge
3365    /// policy overlay axis lands) would have had to be threaded through
3366    /// both open-coded copies in lockstep or one consumer would
3367    /// silently disagree with the peer on which edge slice a given
3368    /// Caixa resolves to — the enumerator's presence probe reading the
3369    /// raw slot while the peer view-composer's fold-in path read an
3370    /// operator-resolved slot would silently split the paired
3371    /// declared-slot enumerator and typed-view composition, a
3372    /// two-consumer split at the enumerator and the view composer far
3373    /// from the source `caixa.lisp` with no field naming the edge-set-
3374    /// drift root cause. Lifting the resolution rule to a typed method
3375    /// on the substrate primitive means every downstream consumer of
3376    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3377    /// reaches for exactly one typed dispatch — the resolver's
3378    /// accept-set migrates as a unit on any future axis addition.
3379    ///
3380    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3381    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3382    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3383    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3384    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3385    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3386    /// mesh-slot arm of the composite-slice sub-family the sibling
3387    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3388    /// Peer at the outer altitude of the closed inner-
3389    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3390    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3391    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3392    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3393    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3394    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3395    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3396    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3397    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3398    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3399    /// pattern onto the sibling M3 typed-composite-element axis
3400    /// ([`crate::aplicacao::WitContract`] composite, matching the
3401    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3402    /// different altitude).
3403    ///
3404    /// Returns `&[crate::aplicacao::WitContract]` (not
3405    /// `&Vec<WitContract>`) because every downstream consumer of the
3406    /// contract list treats it as a read-only sequence — the slice-view
3407    /// is the narrowest borrow that supports every present + roadmapped
3408    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3409    /// discriminant dispatch, `serde` slice-serialization) without
3410    /// leaking the backing `Vec`'s grow/push/reserve surface no
3411    /// consumer of the typed view reaches for (the storage-side `Vec`
3412    /// remains reachable through the `pub contratos` field for the
3413    /// mutation-carrying serde round-trip and per-test fixture-mutation
3414    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3415    /// clones the slot into the typed view). Named `contratos()` to
3416    /// match the storage field's name verbatim and the tatara-lisp
3417    /// author-surface term (`:contratos`) the field's own docstring
3418    /// already carries; the accessor's identity maps onto the canonical
3419    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3420    /// docstring already reaches for ("WIT-typed inter-Servico
3421    /// contracts").
3422    #[must_use]
3423    pub fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3424        self.contratos.as_slice()
3425    }
3426
3427    /// Compose the Aplicacao-related flat slots into a single typed
3428    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3429    /// downstream renderer consumption. Returns `None` when the
3430    /// caixa isn't a `:kind Aplicacao`.
3431    #[must_use]
3432    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3433        if !self.kind().is_aplicacao() {
3434            return None;
3435        }
3436        Some(crate::aplicacao::AplicacaoSpec {
3437            membros: self.membros().to_vec(),
3438            contratos: self.contratos().to_vec(),
3439            politicas: self.politicas().cloned().unwrap_or_default(),
3440            placement: self.placement().cloned().unwrap_or_default(),
3441            entrada: self.entrada().cloned(),
3442        })
3443    }
3444
3445    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3446    /// *declares* a value on, in canonical declaration order
3447    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3448    /// `:entrada`). A slot counts as declared when its backing field
3449    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3450    ///
3451    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3452    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3453    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3454    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3455    /// caixa-flux / caixa-helm renderers only emit them for an
3456    /// Aplicacao. On any *other* kind a declared mesh slot is the
3457    /// manifest field's documented "ignored otherwise" (see the
3458    /// `:membros` … `:entrada` field docs): it silently passes
3459    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3460    /// rendered — far from the source caixa.lisp.
3461    /// [`crate::StandardLayout::verify`] consults this to reject that
3462    /// silent-drop at caixa-build time
3463    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3464    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3465    /// a slot foreign to the kind is a build error, not a silent drop.
3466    ///
3467    /// Lifted as a typed method (rather than an inline disjunction at
3468    /// the verify call site) so the mesh-slot set lives in one place —
3469    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3470    /// overlay, distributed-app takeover config) is one push here, and
3471    /// every consumer reaching for "which mesh slots are set" (the
3472    /// verify gate, a future `feira lint` kind-coherence advisory)
3473    /// inherits the canonical order without rolling its own.
3474    ///
3475    /// Each per-arm kebab-case label is routed through the peer
3476    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3477    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3478    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3479    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3480    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3481    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3482    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3483    /// kebab-case label + renderer-side artifact key) route through one
3484    /// canonical declaration per arm — same discipline the peer
3485    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3486    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3487    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3488    /// axis, extended here to close the M3 mesh-slot author-facing-label
3489    /// axis so both altitudes of the typed-slot algebra
3490    /// (per-Servico M2 + per-Aplicacao M3) share the same
3491    /// "one canonical byte-string per arm, next to the axis" discipline.
3492    #[must_use]
3493    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3494        let mut slots = Vec::new();
3495        if !self.membros().is_empty() {
3496            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3497        }
3498        if !self.contratos().is_empty() {
3499            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3500        }
3501        if self.politicas().is_some() {
3502            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3503        }
3504        if self.placement().is_some() {
3505            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3506        }
3507        if self.entrada().is_some() {
3508            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3509        }
3510        slots
3511    }
3512
3513    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3514    /// caixa *declares* a value on, in canonical declaration order
3515    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3516    /// `:children`). A slot counts as declared when its backing field
3517    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3518    ///
3519    /// The supervisor-tree slots compose the typed OTP supervisor of a
3520    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3521    /// `:children` field docs above). [`Self::supervisor_view`] only
3522    /// folds them into a validatable [`SupervisorSpec`] when the kind
3523    /// matches (returns `None` otherwise), and the wasm-operator's
3524    /// hierarchical reconciler only consumes them for a Supervisor. On
3525    /// any *other* kind a declared supervisor slot is the manifest
3526    /// field's documented "ignored otherwise" (see the `:estrategia` …
3527    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3528    /// and then vanishes — never validated, never reconciled — far from
3529    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3530    /// this to reject that silent-drop at caixa-build time
3531    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3532    /// exact mirror of the [`Self::declared_mesh_slots`] /
3533    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3534    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3535    /// error, not a silent drop.
3536    #[must_use]
3537    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3538        let mut slots = Vec::new();
3539        if self.estrategia().is_some() {
3540            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3541        }
3542        if self.max_restarts().is_some() {
3543            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3544        }
3545        if self.restart_window().is_some() {
3546            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3547        }
3548        if !self.children().is_empty() {
3549            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3550        }
3551        slots
3552    }
3553
3554    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3555    /// caixa *declares* a value on, in canonical declaration order
3556    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3557    /// declared when its backing field carries a value — a `Some(...)`,
3558    /// or a non-empty `Vec`.
3559    ///
3560    /// The M2 slots configure the runtime of a long-running wasm
3561    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3562    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3563    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3564    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3565    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3566    /// emit these slots for a Servico; on any *other* kind a declared M2
3567    /// slot is the manifest field's documented "ignored otherwise": its
3568    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3569    /// but the value is never rendered into a chart / programs.yaml entry
3570    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
3571    /// vanishes, far from the source caixa.lisp.
3572    /// [`crate::StandardLayout::verify`] consults this to reject that
3573    /// silent-drop at caixa-build time
3574    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
3575    /// mirror of the [`Self::declared_mesh_slots`] /
3576    /// [`Self::declared_supervisor_slots`] gates on the peer
3577    /// kind-exclusive slot sets: a slot foreign to the kind is a build
3578    /// error, not a silent drop.
3579    ///
3580    /// Each per-arm kebab-case label is routed through the peer
3581    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
3582    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
3583    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
3584    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
3585    /// both halves of the M2 top-level slot's dual axis (author-facing
3586    /// kebab-case label + renderer-side camelCase overlay-container wire
3587    /// key) route through one canonical declaration per arm — same
3588    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
3589    /// author-label consts (889dc18) establish on the sibling
3590    /// per-callback axis inside the `:behavior` overlay block.
3591    #[must_use]
3592    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
3593        let mut slots = Vec::new();
3594        if self.limits().is_some() {
3595            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
3596        }
3597        if self.behavior().is_some() {
3598            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
3599        }
3600        if !self.upgrade_from().is_empty() {
3601            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
3602        }
3603        slots
3604    }
3605
3606    /// The kebab-case `:slot` tags of every code-surface slot this caixa
3607    /// declares a value on that its [`CaixaKind`] doesn't natively own,
3608    /// in canonical declaration order (`:exe` → `:servicos`). A
3609    /// code-surface slot is owned by exactly one kind: `:exe` by
3610    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
3611    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
3612    /// `ComputeUnit` daemon surface).
3613    ///
3614    /// Each is silently ignored when declared on the wrong kind: the
3615    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
3616    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
3617    /// code-running kind a declared `:exe` / `:servicos` is the manifest
3618    /// field's documented "ignored otherwise" — its path is checked for
3619    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
3620    /// (which run after [`Caixa::from_lisp`]), but the value is never
3621    /// rendered into a build target or programs.yaml entry. It silently
3622    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
3623    /// caixa.lisp, with no field naming which slot is foreign.
3624    ///
3625    /// [`crate::StandardLayout::verify`] consults this to reject that
3626    /// silent-drop at caixa-build time
3627    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
3628    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
3629    /// gates ([`Self::declared_servico_slots`] /
3630    /// [`Self::declared_supervisor_slots`] /
3631    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
3632    /// axis to be closed on the typed surface. The Supervisor /
3633    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
3634    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
3635    /// diagnostics — they fire ahead of this gate on the same `verify`
3636    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
3637    /// and this method is moot. For Biblioteca / Binario / Servico, this
3638    /// gate fires when a code-running kind declares another code-running
3639    /// kind's exclusive code surface.
3640    ///
3641    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
3642    /// may legitimately ship a `lib/` helper that the underlying
3643    /// substrate (the nix flake for Binario, the wasm component build
3644    /// for Servico) bundles into its build, so the slot's
3645    /// declared-on-wrong-kind cardinality isn't a structural error on
3646    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
3647    /// is the native case (the slot's owning kind). Supervisor /
3648    /// Aplicacao declaring `:bibliotecas` is gated upstream by
3649    /// [`crate::LayoutError::SupervisorOwnsCode`] /
3650    /// [`crate::LayoutError::AplicacaoOwnsCode`].
3651    ///
3652    /// Lifted as a typed method (rather than an inline disjunction at
3653    /// the verify call site) so the foreign-code-slot set lives in one
3654    /// place — a future kind that gains its own code-surface slot is
3655    /// one push here, and every consumer reaching for "which code
3656    /// surfaces are foreign to this kind" (the verify gate, a future
3657    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
3658    /// per-caixa build-target classifier) inherits the canonical order
3659    /// without rolling its own.
3660    #[must_use]
3661    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
3662        let mut slots = Vec::new();
3663        if !self.exe().is_empty() && !self.kind().requires_exe() {
3664            slots.push(":exe");
3665        }
3666        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
3667            slots.push(":servicos");
3668        }
3669        slots
3670    }
3671
3672    /// Validate every entry of `:deps` and `:deps-dev` through
3673    /// [`Dep::validate`] — closing the parity loop with the per-axis
3674    /// `:versao` gates already wired into the typed-graph
3675    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
3676    /// 9888b13) and typed supervisor tree
3677    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
3678    ///
3679    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
3680    /// were the only `:versao` axes still untyped past
3681    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
3682    /// as a String without parsing it, so a malformed-but-non-empty
3683    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
3684    /// silently passed parse and the `semver::Error` surfaced at
3685    /// lacre-resolve time, far from the source caixa.lisp, with no
3686    /// field naming which `:deps` entry carried the typo. Lifting the
3687    /// gate here makes the four `:versao` typed surfaces (`:deps`,
3688    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
3689    /// every requirement string past `validate_deps` is round-trippable
3690    /// through [`crate::parse_requirement`] without re-checking at the
3691    /// resolver layer.
3692    ///
3693    /// Both lists run through the same per-entry validator so a typo
3694    /// in `:deps-dev` surfaces with the same diagnostic as one in
3695    /// `:deps` — neither axis is a second-class citizen of the typed
3696    /// surface.
3697    ///
3698    /// Within each list, [`DepError::DuplicateNome`] closes the
3699    /// set-not-multiset discipline on the `:nome` axis: two entries
3700    /// naming the same caixa carry two `:versao` / `:fonte` / feature
3701    /// triples that the caixa-resolver's lacre pipeline collapses to one
3702    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
3703    /// silently overwrites the first at `concrete_versao`-resolve time
3704    /// (the same "second wins / one silently overwrites the other"
3705    /// shape the peer typed-graph duplicate gates already close on every
3706    /// other Vec-shaped authoring surface that keys by name). The
3707    /// duplicate check fires per-list and runs *after* each per-entry
3708    /// [`Dep::validate`] call so a malformed-and-duplicated entry
3709    /// surfaces its narrower per-entry diagnostic
3710    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
3711    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
3712    /// diagnostic — the canonical "per-entry shape before cross-entry
3713    /// uniqueness" precedence the peer `:children :caixa`
3714    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
3715    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
3716    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
3717    /// ([`crate::AplicacaoSpec::validate_placement`]),
3718    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
3719    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
3720    /// and the within-`:upgrade-from`-entry per-instruction-class
3721    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
3722    /// [`crate::UpgradeError::DuplicateStateChange`],
3723    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
3724    ///
3725    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
3726    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
3727    /// same name in both tables (the dev table's pin overrides the
3728    /// runtime table's pin in test/dev contexts), and caixa's surface
3729    /// mirrors that convention until a deliberate choice retires the
3730    /// override pattern. Only within-list duplicates are structurally
3731    /// incoherent — those are what this gate closes.
3732    pub fn validate_deps(&self) -> Result<(), DepError> {
3733        for &list in crate::dep::DepList::ALL {
3734            let mut seen = std::collections::HashSet::new();
3735            for dep in self.deps_of(list) {
3736                dep.validate()?;
3737                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
3738                    DepError::DuplicateNome {
3739                        nome: dep.nome().to_string(),
3740                        list: list.as_str(),
3741                    }
3742                })?;
3743            }
3744        }
3745        Ok(())
3746    }
3747
3748    /// Reject `:nome` values the K8s apiserver would refuse at admission
3749    /// time. The top-level Caixa identity flows directly into every
3750    /// substrate-side artifact's `metadata.name` axis: the
3751    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
3752    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
3753    /// aggregator keys ComputeUnit derivation off
3754    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
3755    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
3756    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
3757    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
3758    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
3759    /// ([`caixa-mesh::lib::cilium_network_policies`],
3760    /// [`caixa-mesh::lib::gateway_routes`]), and the default
3761    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
3762    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
3763    /// schema enforces the DNS-1123 label rule on admission; a
3764    /// structurally invalid `:nome` (`"MyApp"` — the canonical
3765    /// "I copied the display name verbatim" footgun, `"my_app"` — the
3766    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
3767    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
3768    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
3769    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
3770    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
3771    /// failure surfaced at `kubectl apply` time as a `metadata.name:
3772    /// Invalid value` rejection on whichever derived artifact admitted
3773    /// first, far from the source `caixa.lisp` and without any field
3774    /// naming the offending `:nome`.
3775    ///
3776    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
3777    /// substrate-side predicate the per-axis name gates already share:
3778    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
3779    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
3780    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
3781    /// diagnostic is self-locating (the offending `:nome` is named
3782    /// verbatim) and the author can grep their `caixa.lisp` for
3783    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
3784    /// every per-axis sibling gate already exposes
3785    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
3786    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
3787    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
3788    ///
3789    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
3790    /// derive macro stores the raw String) is gated by the narrower
3791    /// [`ManifestError::NomeEmpty`] arm before the predicate is
3792    /// consulted, mirroring the empty-first cascade every per-axis
3793    /// name gate already uses (e.g. `MembroCaixaEmpty` before
3794    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
3795    pub fn validate_nome(&self) -> Result<(), ManifestError> {
3796        // Routes through the shared
3797        // [`crate::render::require_valid_dns_1123_label`] gate the peer
3798        // name axes each land on so drift between the eight axes'
3799        // accepted DNS-1123-label sets is structurally impossible.
3800        let nome = self.nome();
3801        crate::render::require_valid_dns_1123_label(
3802            nome,
3803            || ManifestError::NomeEmpty,
3804            |reason| ManifestError::NomeInvalid {
3805                nome: nome.to_string(),
3806                reason,
3807            },
3808        )
3809    }
3810
3811    /// Reject `:nome` values whose joint length with the canonical
3812    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
3813    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
3814    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
3815    /// substrate carries materializes the caixa's `:nome` through the
3816    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
3817    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
3818    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
3819    /// `ChartDir.name` + `Chart.yaml::name`
3820    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
3821    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
3822    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
3823    /// `oci://<registry>/lareira-<nome>` chart ref
3824    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
3825    /// admission rule strict-parses against DNS-1123-label, the Helm
3826    /// operator's tracking-secret name is derived from `release_name`
3827    /// and is itself DNS-1123-label-bounded, and the rendered chart's
3828    /// K8s object `metadata.name` axes embed the chart name as a
3829    /// prefix — every one fails admission on a > 63-byte chart name.
3830    ///
3831    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
3832    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
3833    /// `:nome` of 56–63 bytes silently passed validate (the inner
3834    /// DNS-1123 check accepts the bare `:nome`) but produced a
3835    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
3836    /// rejected at admission — far from the source `caixa.lisp`, with
3837    /// no field naming the overflow root cause. The
3838    /// [`lareira_chart_name`] helper's own doc comment
3839    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
3840    /// "the M4 admission webhook will pin the joint-length invariant
3841    /// when it lands". This gate lands the invariant at the
3842    /// manifest-validate layer rather than waiting for the apiserver
3843    /// — the same fail-at-the-source posture every peer per-axis
3844    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
3845    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
3846    /// `:edicao`, etc.) takes.
3847    ///
3848    /// Thin wrapper around
3849    /// [`crate::render::is_lareira_chart_name_shape`] (the
3850    /// substrate-side predicate that composes [`lareira_chart_name`] +
3851    /// [`is_dns_1123_label`] via the lifted
3852    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
3853    /// shared parser-shaped reason into the
3854    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
3855    /// diagnostic is self-locating (the offending `:nome` is named
3856    /// verbatim alongside the rendered chart name and the budget) and
3857    /// the author can shorten in one edit. The gate runs across every
3858    /// `:kind` — `:nome` is the substrate-wide identity axis any
3859    /// future renderer the substrate adds can derive a
3860    /// `lareira-<nome>` artifact from, and uniform enforcement closes
3861    /// the drift footgun where a future kind grows a chart-emitting
3862    /// render path while the validate cascade doesn't catch it.
3863    ///
3864    /// Runs *after* [`Self::validate_nome`] so the narrower
3865    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
3866    /// structurally-malformed `:nome` (empty, uppercase, underscore,
3867    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
3868    /// specific shape error rather than the chart-name-budget error,
3869    /// preserving the legitimate "well-shaped `:nome` that happens to
3870    /// overflow the joint cap" arm for this gate.
3871    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
3872        let nome = self.nome();
3873        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
3874            ManifestError::NomeChartNameBudgetExceeded {
3875                nome: nome.to_string(),
3876                reason,
3877            }
3878        })
3879    }
3880
3881    /// Reject `:versao` values that don't parse as [`semver::Version`].
3882    /// The top-level Caixa version flows directly into every
3883    /// substrate-side artifact that carries a "this is which version of
3884    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
3885    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
3886    /// SemVer-2-strict at `helm template` / `helm install` time per
3887    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
3888    /// `feira publish` Zig-style `v<versao>` git tag
3889    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
3890    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
3891    /// `versao:` value the `lareira-fleet-programs` aggregator carries
3892    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
3893    /// `:latest` tags the substrate's `wasi-service-flake` builds with
3894    /// `skopeo push`, the lacre closure's pinned versions
3895    /// ([`caixa-resolver`] keys `concrete_versao`), and the
3896    /// `:upgrade-from :from` references peers in this exact `versao`
3897    /// shape (`semver::Version`, not `VersionReq`). Each consumer
3898    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
3899    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
3900    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
3901    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
3902    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
3903    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
3904    /// into the version field a peer `:deps :versao` accepts;
3905    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
3906    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
3907    /// derive macro stores the raw String) and the failure surfaced at
3908    /// the *first* downstream consumer that strict-parses it: at
3909    /// `helm install` time as a chart-version rejection, at
3910    /// `feira publish` time as a malformed git tag, at lacre-resolve
3911    /// time as a `semver::Error` not naming the offending caixa, at
3912    /// `feira upgrade --to <versao>` time as an unresolvable
3913    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
3914    /// and without any field naming the offending `:versao`.
3915    ///
3916    /// Thin wrapper around [`semver::Version::parse`] — the same parser
3917    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
3918    /// and [`crate::UpgradeFromEntry::validate`] (the peer
3919    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
3920    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
3921    /// variant, carrying the offending `:versao` verbatim + a
3922    /// parser-shaped reason naming the specific violation, so the
3923    /// diagnostic is self-locating (the author can grep their
3924    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
3925    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
3926    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
3927    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
3928    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
3929    /// now structurally equivalent (every value past validate is
3930    /// round-trippable through [`semver::Version::parse`] without
3931    /// re-checking at the renderer, resolver, or operator hot-upgrade
3932    /// layer), peer with the four `:versao` requirement axes (`:deps`,
3933    /// `:deps-dev`, `:membros`, `:children`) the prior commits
3934    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
3935    ///
3936    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
3937    /// the derive macro stores the raw String) is gated by the
3938    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
3939    /// consulted, mirroring the empty-first cascade every per-axis
3940    /// version gate already uses (e.g. `MembroVersaoEmpty` before
3941    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
3942    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
3943    pub fn validate_versao(&self) -> Result<(), ManifestError> {
3944        let versao = self.versao();
3945        if versao.is_empty() {
3946            return Err(ManifestError::VersaoEmpty);
3947        }
3948        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
3949            versao: versao.to_string(),
3950            reason: e.to_string(),
3951        })?;
3952        Ok(())
3953    }
3954
3955    /// Reject `:restart-window` values the shared
3956    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
3957    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
3958    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
3959    /// `Option<Duration>` routed through the shared codec via `with =
3960    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
3961    /// view-construction path ([`Self::supervisor_view`]) folds the
3962    /// raw string through the same shared codec and soft-swallows the
3963    /// parse error as `None` to keep the view best-effort. Without
3964    /// this gate a malformed `:restart-window` (`"1.5s"` — the
3965    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
3966    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
3967    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
3968    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
3969    /// edge case) silently produced a `SupervisorSpec` with
3970    /// `restart_window: None`, indistinguishable from the canonical
3971    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
3972    /// `MaxIntensity / Period` invariant turns into a never-reset
3973    /// supervisor far from the source `caixa.lisp`, with no field
3974    /// naming the offending `:restart-window`. Lifting the gate to a
3975    /// Caixa-level validator mirrors the trajectory of the peer
3976    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
3977    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
3978    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
3979    /// (line 196: "reject invalid `:restart-window` (non-duration)").
3980    ///
3981    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
3982    /// (the shared codec backing `:supervisor :restart-window` as
3983    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
3984    /// `:politicas :circuit-breaker :window` — all three covered by
3985    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
3986    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
3987    /// variant, carrying the offending raw string + a parser-shaped
3988    /// reason naming the canonical authoring form, so the diagnostic
3989    /// is self-locating (the author can grep their `caixa.lisp` for
3990    /// `:restart-window "<value>"` and fix it in one edit) and
3991    /// uniform with every other manifest-level validate diagnostic.
3992    /// With this gate the four `:restart-window`-shaped surfaces (the
3993    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
3994    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
3995    /// now structurally equivalent — every value past the codec is in
3996    /// one accepted set, by construction.
3997    ///
3998    /// `None` (the canonical "omit the slot to express no reset"
3999    /// shape) is accepted trivially — the gate is a no-op when the
4000    /// author didn't author a window. The empty string is rejected by
4001    /// the shared codec (its digit-only gate refuses an empty
4002    /// magnitude), surfacing the same `RestartWindowMalformed`
4003    /// diagnostic as every other rejected non-canonical shape.
4004    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4005        let Some(s) = self.restart_window() else {
4006            return Ok(());
4007        };
4008        crate::supervisor::duration_codec::parse(s)
4009            .map(|_| ())
4010            .map_err(|reason| ManifestError::RestartWindowMalformed {
4011                restart_window: s.to_string(),
4012                reason,
4013            })
4014    }
4015
4016    /// Reject per-entry values on the three Caixa-level code-surface
4017    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
4018    /// layout checker's `root.join(p)` sandbox would silently subvert.
4019    /// Same three structural footguns the peer
4020    /// [`BehaviorSpec::validate`] (b0c8389) and
4021    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
4022    /// (26da2c7) already close on the M2 `:behavior :on-*` and
4023    /// `:upgrade-from :state-change :script` axes, here lifted onto
4024    /// the three top-level code-path axes through the shared
4025    /// [`is_sandboxed_relative_path`] predicate:
4026    ///
4027    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
4028    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
4029    ///     [`Path::join`] as the base itself — `root.join("")` ==
4030    ///     `root`, so the existence check (`self.exists(&root)`)
4031    ///     trivially passes (the project root exists), and the layout
4032    ///     silently treats the project root as a biblioteca / exe /
4033    ///     servico entry. The `:bibliotecas` loop then hands the root
4034    ///     to `tatara_lisp::read` at `feira build` time as if the root
4035    ///     directory itself were a Lisp source file — a parse error
4036    ///     far from the source `caixa.lisp` with no field naming the
4037    ///     offending entry.
4038    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
4039    ///     [`Path::join`] *replaces* the base when the right-hand side
4040    ///     is absolute, so `root.join("/etc/passwd")` resolves to
4041    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
4042    ///     The existence check then silently consults whatever the
4043    ///     escaped path resolves to — for `:bibliotecas`, the layout
4044    ///     has no `starts_with`-fence (only `:exe` is fenced under
4045    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
4046    ///     `:bibliotecas` entry that happens to resolve on disk
4047    ///     silently passes. For `:exe` / `:servicos` the fence catches
4048    ///     the absolute case downstream as `ExeOutsideDir` /
4049    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
4050    ///     doesn't exist), but with a downstream-shaped diagnostic
4051    ///     that names the resolved escape path rather than the
4052    ///     authoring footgun at the source.
4053    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
4054    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
4055    ///     [`std::path::Component::ParentDir`] anywhere round-trips
4056    ///     through [`Path::join`] as a traversal above the caixa root.
4057    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
4058    ///     *component-aware* (not canonical-path-aware), so
4059    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
4060    ///     is **true** even though the canonical resolution
4061    ///     `{parent of root}/escape.lisp` lives outside the caixa root
4062    ///     — the fence silently lets the parent-escape through, and
4063    ///     the existence check passes if that escape-target happens
4064    ///     to exist. Caught regardless of where the `..` sits
4065    ///     (leading, mid-path, trailing) so the gate matches the peer
4066    ///     predicate's full coverage.
4067    ///
4068    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
4069    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
4070    /// same per-slot diagnostic shape every peer per-axis path-gate
4071    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
4072    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
4073    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
4074    /// order [`Caixa::declared_foreign_code_slots`] uses for its
4075    /// canonical foreign-code-slot diagnostic, so a manifest with
4076    /// multiple malformed slots surfaces the lexicographically-earliest
4077    /// slot's diagnostic deterministically.
4078    ///
4079    /// Lifted to the typed surface as a Caixa-level validator (peer
4080    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
4081    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
4082    /// and wired into [`crate::StandardLayout::verify`] before the
4083    /// existence-check loops so the diagnostic names the offending
4084    /// slot at the source caixa.lisp rather than reporting a
4085    /// downstream `MissingEntry` / `ExeOutsideDir` /
4086    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
4087    /// The fourth typed code-path surface — every author-supplied
4088    /// path on the manifest — is now structurally accept-shaped
4089    /// past validate, peer with `:behavior :on-*` and
4090    /// `:upgrade-from :state-change :script`.
4091    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
4092        /// Per-slot file-type contract for the three Caixa-level
4093        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
4094        /// Each variant names the predicate the per-entry file-type
4095        /// gate consults; [`Self::None`] opts the slot out of any
4096        /// file-type contract. Lifted as a typed local enum so the
4097        /// per-slot dispatch is exhaustive at the `match` — adding a
4098        /// future axis to the typed-substrate `:` slot set (the
4099        /// future `:assets` resource axis the M5 roadmap names, the
4100        /// future `:nix-flake` derivation axis the caixa-flake
4101        /// emitter consults) lands as one variant + one `match` arm,
4102        /// not a coordinated rewrite of every per-slot bool flag.
4103        ///
4104        /// Peer of the typed-substrate per-slot variant disciplines
4105        /// already established on this surface
4106        /// ([`crate::supervisor::RestartStrategy`] +
4107        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
4108        /// supervision-tree axis,
4109        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
4110        /// placement axis, [`crate::aplicacao::WitTarget`] on the
4111        /// `:contratos` payload-target axis): the typed `enum` is
4112        /// the substrate's single source of truth for the per-axis
4113        /// dispatch, and every consumer (the per-arm body here, the
4114        /// future feira-lint per-slot diagnostic renderer, the M4
4115        /// per-axis admission webhook) reaches for the same typed
4116        /// surface rather than re-deriving the partition from inline
4117        /// flag combinations.
4118        enum CodePathFileType {
4119            /// `:exe` — nix-build derivation output, no terminating-
4120            /// extension contract (the canonical `"exe/<name>"`
4121            /// fixtures the layout's `ExeOutsideDir` error message
4122            /// documents carry no extension by convention).
4123            None,
4124            /// `:bibliotecas` — tatara-lisp source files the
4125            /// `feira build` loop reads through `tatara_lisp::read`
4126            /// at parse time. Routes to [`is_lisp_extension`].
4127            LispSource,
4128            /// `:servicos` — ComputeUnit-CR YAML files the
4129            /// caixa-helm / caixa-flux renderers consume through
4130            /// `serde_yaml::from_str`. Routes to
4131            /// [`is_computeunit_yaml_extension`].
4132            ComputeUnitYaml,
4133        }
4134
4135        // The per-slot [`CodePathFileType`] selects which axes carry the
4136        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
4137        // source axis (the `feira build` loop at
4138        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
4139        // `tatara_lisp::read` at parse time) — the lifted
4140        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
4141        // `:exe` is the nix-built executable surface (per the canonical
4142        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
4143        // error message documents and every in-tree
4144        // `caixa_with_code_paths` positive control uses) — its file-type
4145        // contract is "nix-build derivation output", not a typed source
4146        // file, so [`CodePathFileType::None`] opts the slot out of any
4147        // file-type gate. `:servicos` is the `.computeunit.yaml`
4148        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
4149        // renderers consume each entry through `serde_yaml::from_str` as
4150        // a typed `ComputeUnit` CR) — the lifted
4151        // [`is_computeunit_yaml_extension`] predicate gates the compound
4152        // `.computeunit.yaml` suffix. All three axes are surfaced through
4153        // the same iteration so the sandbox-shape + duplicate gates
4154        // apply uniformly; the typed file-type dispatch fires per-slot
4155        // exactly where the downstream consumer's accepted set demands
4156        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
4157        // compounding lift on the peer 64772a9 `:bibliotecas`
4158        // `.lisp`-gate trajectory — the second of the three code-path
4159        // axes to land on a typed compound-suffix gate, with the same
4160        // self-locating per-slot diagnostic shape every peer per-axis
4161        // file-type lift uses (`*NonLispExtension { slot, path }` /
4162        // `*NonComputeUnitYamlExtension { slot, path }`).
4163        for (slot, list, file_type) in [
4164            (
4165                ":bibliotecas",
4166                &self.bibliotecas,
4167                CodePathFileType::LispSource,
4168            ),
4169            (":exe", &self.exe, CodePathFileType::None),
4170            (
4171                ":servicos",
4172                &self.servicos,
4173                CodePathFileType::ComputeUnitYaml,
4174            ),
4175        ] {
4176            // Per-slot set-not-multiset gate on the typed code-path axis.
4177            // Every peer Vec-shaped author-supplied list past validate is
4178            // a set, not a multiset: `:membros :caixa`
4179            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
4180            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
4181            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
4182            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
4183            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
4184            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
4185            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
4186            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
4187            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
4188            // the three code-path lists are the last Vec-shaped author-
4189            // supplied slots on the typed Caixa surface still admitting a
4190            // duplicate entry silently. Scope is per-list (`:bibliotecas`
4191            // duplicates are flagged within `:bibliotecas`, not across
4192            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
4193            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
4194            // legitimate dev-vs-runtime shape on the dep axis, fenced
4195            // separately by [`crate::dep::validate_no_self_dep`]). On the
4196            // code-path axis a cross-slot collision is structurally
4197            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
4198            // fence — `:exe` and `:servicos` entries are confined to their
4199            // own directory trees, so the only way a string could appear
4200            // on two code-path lists is the (rare, structurally invalid)
4201            // case where `:bibliotecas` carries an `"exe/<x>"` or
4202            // `"servicos/<x>.yaml"`-shaped path.
4203            //
4204            // Without the gate three authoring footguns silently passed:
4205            //
4206            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
4207            //     canonical copy-paste-the-wrong-file footgun. `feira
4208            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
4209            //     list and re-parses the same file twice, wasting work
4210            //     and silently masking the author's intent to declare a
4211            //     *second* biblioteca.
4212            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
4213            //     Binario surface. The future `caixa-flake` `nix flake`
4214            //     emitter that materializes each `:exe` entry as a flake
4215            //     `packages.<exe-name>` derivation would collide on the
4216            //     duplicate package name and surface a flake-eval error
4217            //     far from the source `caixa.lisp`.
4218            //   - `:servicos ("servicos/x.computeunit.yaml"
4219            //     "servicos/x.computeunit.yaml")` — the same footgun on
4220            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
4221            //     renderers already refuse `:servicos.len() != 1` with
4222            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
4223            //     that diagnostic surfaces "too many servicos" without
4224            //     naming "duplicate entry" — the typed self-locating
4225            //     "which entry is the duplicate" framing only lands at
4226            //     this gate.
4227            //
4228            // Same `seen.insert(entry.as_str())` shape every peer per-list
4229            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
4230            // 86c769b, `:deps` 359fba5) and the same "structural shape
4231            // checks fire before the duplicate check on the same entry"
4232            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
4233            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
4234            // empty entry first, not the duplicate on the later pair).
4235            let mut seen = std::collections::HashSet::new();
4236            for entry in list {
4237                let path = Path::new(entry);
4238                match is_sandboxed_relative_path(path) {
4239                    Ok(()) => {}
4240                    Err(PathShapeViolation::Empty) => {
4241                        return Err(ManifestError::CodePathEmpty { slot });
4242                    }
4243                    Err(PathShapeViolation::Absolute) => {
4244                        return Err(ManifestError::CodePathAbsolute {
4245                            slot,
4246                            path: path.to_path_buf(),
4247                        });
4248                    }
4249                    Err(PathShapeViolation::ParentEscape) => {
4250                        return Err(ManifestError::CodePathParentEscape {
4251                            slot,
4252                            path: path.to_path_buf(),
4253                        });
4254                    }
4255                }
4256                // The per-slot file-type gate dispatched through the
4257                // typed [`CodePathFileType`] selector above. Each variant
4258                // routes to the lifted predicate the downstream consumer
4259                // demands:
4260                //
4261                //   - [`LispSource`] → [`is_lisp_extension`] for
4262                //     `:bibliotecas` (the `feira build` loop's
4263                //     `tatara_lisp::read` consumer);
4264                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
4265                //     for `:servicos` (the caixa-helm / caixa-flux
4266                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
4267                //     accepted set);
4268                //   - [`None`] for `:exe` — the nix-build derivation-
4269                //     output axis has no terminating-extension contract.
4270                //
4271                // Fires after the sandbox-shape arms so a path that is
4272                // *both* sandbox-escaping and wrong-extension surfaces
4273                // the more fundamental sandbox-shape diagnostic first
4274                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
4275                // `ParentEscape` → `NonLispExtension` arm-ordering on
4276                // `:behavior :on-*` c97815a, and `EmptyScript` →
4277                // `AbsoluteScript` → `ParentEscapeScript` →
4278                // `NonLispExtensionScript` on
4279                // `:upgrade-from :state-change :script` 33cc830), and
4280                // before the duplicate gate so the narrower per-entry
4281                // file-type shape dominates the cross-entry uniqueness
4282                // diagnostic (a
4283                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
4284                // `:servicos` surfaces
4285                // `CodePathNonComputeUnitYamlExtension` on the first
4286                // entry rather than `CodePathDuplicate` on the pair —
4287                // peer with the 64772a9 `:bibliotecas`
4288                // `("lib/x.txt" "lib/x.txt")` ordering).
4289                match file_type {
4290                    CodePathFileType::None => {}
4291                    CodePathFileType::LispSource => {
4292                        if !is_lisp_extension(path) {
4293                            return Err(ManifestError::CodePathNonLispExtension {
4294                                slot,
4295                                path: path.to_path_buf(),
4296                            });
4297                        }
4298                    }
4299                    CodePathFileType::ComputeUnitYaml => {
4300                        if !is_computeunit_yaml_extension(path) {
4301                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
4302                                slot,
4303                                path: path.to_path_buf(),
4304                            });
4305                        }
4306                    }
4307                }
4308                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
4309                    ManifestError::CodePathDuplicate {
4310                        slot,
4311                        path: path.to_path_buf(),
4312                    }
4313                })?;
4314            }
4315        }
4316        Ok(())
4317    }
4318
4319    /// Reject `:etiquetas` lists with an empty entry or with two entries
4320    /// agreeing on the same string. `:etiquetas` is the universal
4321    /// registry-search-tag axis on [`Caixa`] (every kind carries the
4322    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4323    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
4324    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
4325    /// a [`std::collections::BTreeSet`] alongside the four substrate-
4326    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
4327    /// Two authoring footguns silently passed validate without this gate:
4328    ///
4329    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
4330    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
4331    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
4332    ///     `chart.metadata.keywords` admits the value without a strict
4333    ///     parser-side gate, but the empty keyword has no operational
4334    ///     meaning — it indexes nothing in the future caixa-registry
4335    ///     search axis and clutters the rendered chart with a no-op tag.
4336    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
4337    ///     copy-paste-the-wrong-tag footgun) silently passed validate
4338    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
4339    ///     at chart render — a "second wins / one silently disappears"
4340    ///     shape divergent from every peer typed-graph set gate
4341    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
4342    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
4343    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4344    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4345    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4346    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
4347    ///     on `:upgrade-from`, the per-instruction-class singularity
4348    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
4349    ///     [`crate::UpgradeError::DuplicateStateChange`] /
4350    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
4351    ///     discipline is uniform: every Vec-shaped author-supplied list
4352    ///     past validate is set-not-multiset, by construction.
4353    ///
4354    /// Past the empty arm the gate enforces the chart-keyword shape
4355    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
4356    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
4357    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
4358    /// continuation. Closes the canonical paste-from-doc footguns the
4359    /// bare empty + duplicate arms left open: paste-from-aligned-doc
4360    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
4361    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
4362    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
4363    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
4364    /// — the author meant three separate list entries), path-separator
4365    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
4366    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
4367    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
4368    /// control bytes that would silently land as malformed search tags
4369    /// in the rendered Chart.yaml `keywords:` array and break the
4370    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
4371    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
4372    /// established on the sibling universal-axis `Vec<String>` surface
4373    /// — the second universal-axis Vec<String> surface to land the
4374    /// empty-first-then-shape-then-duplicate per-entry cascade.
4375    ///
4376    /// Same empty-first cascade discipline every peer per-axis gate
4377    /// uses: the per-entry empty arm fires before the per-entry shape
4378    /// arm fires before the cross-entry duplicate arm, so an
4379    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
4380    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
4381    /// has no value" defect) before either the shape or the duplicate
4382    /// diagnostic. Walks the list in declaration order so the
4383    /// first-collision diagnostic surfaces the lexicographically-
4384    /// earliest offending position, peer with every other duplicate
4385    /// gate on this surface.
4386    ///
4387    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
4388    /// caixa-build gate alongside the peer universal gates
4389    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4390    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
4391    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
4392    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4393    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4394    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4395    /// slot sets. The future caixa-registry search axis can reach for
4396    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
4397    /// chart-keyword-shaped string without re-deriving the precondition.
4398    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
4399        let mut seen = std::collections::HashSet::new();
4400        for etiqueta in self.etiquetas() {
4401            if etiqueta.is_empty() {
4402                return Err(ManifestError::EtiquetaEmpty);
4403            }
4404            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
4405                ManifestError::EtiquetaInvalid {
4406                    etiqueta: etiqueta.clone(),
4407                    reason,
4408                }
4409            })?;
4410            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
4411                ManifestError::EtiquetaDuplicate {
4412                    etiqueta: etiqueta.clone(),
4413                }
4414            })?;
4415        }
4416        Ok(())
4417    }
4418
4419    /// Reject `:autores` lists with an empty entry or with two entries
4420    /// agreeing on the same string. `:autores` is the universal
4421    /// maintainer-axis on [`Caixa`] (every kind carries the
4422    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4423    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
4424    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
4425    /// to a `Maintainer { name, email: None }` without dedup). Two
4426    /// authoring footguns silently passed validate without this gate:
4427    ///
4428    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
4429    ///     blank-doc footgun) rendered as
4430    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
4431    ///     empty maintainer name has no operational meaning — it
4432    ///     identifies no one in the substrate's authorship index and
4433    ///     clutters the rendered chart with a no-op maintainer.
4434    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
4435    ///     the copy-paste-the-wrong-author footgun) silently passed
4436    ///     validate and rendered as two identical maintainer entries.
4437    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
4438    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
4439    ///     rendered `keywords:` array at chart-render time), the
4440    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
4441    ///     entries stack verbatim in the chart, divergent from every
4442    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
4443    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
4444    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4445    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4446    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4447    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
4448    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
4449    ///     `:etiquetas`).
4450    ///
4451    /// Past the empty arm the gate enforces the chart-maintainer-name
4452    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
4453    /// the structural single-line printable-UTF-8 floor every realistic
4454    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
4455    /// or trailing whitespace, no ASCII control characters anywhere,
4456    /// Unicode bytes accepted. Closes the canonical paste-from-doc
4457    /// footguns the bare empty + duplicate arms left open:
4458    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
4459    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
4460    /// pasted a multi-line block of author records into one `:autores`
4461    /// entry instead of splitting into one entry per author),
4462    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
4463    /// and the paste-from-binary-blob control bytes that would silently
4464    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
4465    /// `maintainers:` array. Mirrors the shape-predicate cascade
4466    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4467    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
4468    /// establish past their own empty arms on the sibling universal-axis
4469    /// `Option<String>` surfaces — the first universal-axis Vec<String>
4470    /// surface to land the empty-first-then-shape-then-duplicate per-entry
4471    /// cascade.
4472    ///
4473    /// Same empty-first cascade discipline every peer per-axis gate
4474    /// uses: the per-entry empty arm fires before the per-entry shape
4475    /// arm before the cross-entry duplicate arm. Walks the list in
4476    /// declaration order so the first-collision diagnostic surfaces the
4477    /// lexicographically-earliest offending position, peer with every
4478    /// other duplicate gate on this surface.
4479    ///
4480    /// Universal-axis (every kind carries `:autores`), so wired at the
4481    /// caixa-build gate alongside the peer universal gates
4482    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4483    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4484    /// [`Self::validate_code_paths`] — before the kind-coherence gates
4485    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4486    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4487    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4488    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4489    /// slot sets.
4490    pub fn validate_autores(&self) -> Result<(), ManifestError> {
4491        let mut seen = std::collections::HashSet::new();
4492        for autor in self.autores() {
4493            if autor.is_empty() {
4494                return Err(ManifestError::AutorEmpty);
4495            }
4496            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
4497                ManifestError::AutorInvalid {
4498                    autor: autor.clone(),
4499                    reason,
4500                }
4501            })?;
4502            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
4503                ManifestError::AutorDuplicate {
4504                    autor: autor.clone(),
4505                }
4506            })?;
4507        }
4508        Ok(())
4509    }
4510
4511    /// Reject `:repositorio` values whose shape the shared
4512    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
4513    /// `repositorio: Option<String>` slot on [`Caixa`] is the
4514    /// universal git-shaped homepage axis every kind carries — the
4515    /// substrate routes the same string through two load-bearing
4516    /// consumers:
4517    ///
4518    ///   - [`caixa-helm`] folds it verbatim into the rendered
4519    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
4520    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
4521    ///     the chart `README.md` `repo = …` interpolation
4522    ///     (`caixa-helm/src/lib.rs:359`).
4523    ///   - [`caixa-flux`] folds it verbatim into the standalone
4524    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
4525    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
4526    ///     `GitRepository.spec.url` the cluster's source-controller
4527    ///     polls — the load-bearing deploy-time axis.
4528    ///
4529    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4530    /// substitute a placeholder when the slot is absent (`None` → the
4531    /// fallback fires); a `Some("")` *skips the fallback* and silently
4532    /// passes the empty string through to `Chart.yaml home: ""` /
4533    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
4534    /// controller both reject the empty URL far from the source
4535    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
4536    /// Similarly a malformed `:repositorio` (whitespace, control char,
4537    /// missing `:` separator, leading `-`) silently lands in the
4538    /// rendered artifacts and breaks at `git clone` / `helm template`
4539    /// / `flux reconcile` time.
4540    ///
4541    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
4542    /// same shared predicate the peer [`crate::DepSource::validate`]
4543    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
4544    /// gate the two `git URL`-shaped surfaces on the typed Caixa
4545    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
4546    /// structurally equivalent: every value past validate is
4547    /// guaranteed-acceptable by the predicate's union of constraints
4548    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
4549    /// control chars, ASCII only, no leading `:`, contains a `:`
4550    /// separator). The predicate accepts every documented authoring
4551    /// shape — `github:org/repo` shorthand, `https://host/path`,
4552    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
4553    /// scp-style SSH, `file:///path` — and refuses the canonical
4554    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
4555    /// injection footguns at validate time. Maps the predicate's
4556    /// `String` reason verbatim into the
4557    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
4558    /// offending value + parser-shaped reason so the diagnostic is
4559    /// self-locating (the author can grep their `caixa.lisp` for
4560    /// `:repositorio "<value>"` and fix it in one edit).
4561    ///
4562    /// `None` (the canonical "omit the slot to express no published
4563    /// homepage" shape) is accepted trivially — the gate is a no-op
4564    /// when the author didn't declare a value. `Some("")` is gated by
4565    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
4566    /// shape predicate is consulted, mirroring the empty-first cascade
4567    /// every peer per-axis identity gate uses
4568    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
4569    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
4570    /// [`crate::DepError::FonteRepoEmpty`] →
4571    /// [`crate::DepError::FonteRepoInvalid`]).
4572    ///
4573    /// Universal-axis (every kind carries `:repositorio`), so wired at
4574    /// the caixa-build gate alongside the peer universal gates
4575    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4576    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4577    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
4578    /// before the kind-coherence gates
4579    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4580    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4581    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4582    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4583    /// specific slot sets.
4584    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
4585        let Some(s) = self.repositorio() else {
4586            return Ok(());
4587        };
4588        if s.is_empty() {
4589            return Err(ManifestError::RepositorioEmpty);
4590        }
4591        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
4592            repositorio: s.to_string(),
4593            reason,
4594        })
4595    }
4596
4597    /// Reject `:descricao` values that are the empty string. The flat
4598    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
4599    /// free-form-prose homepage axis every kind carries — the
4600    /// substrate routes the same string through two load-bearing
4601    /// consumers in the [`caixa-helm`] renderer:
4602    ///
4603    ///   - `build_chart_yaml` folds it verbatim into the rendered
4604    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
4605    ///     field (`caixa-helm/src/lib.rs:232-235`).
4606    ///   - `build_readme` folds it verbatim into the rendered chart
4607    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
4608    ///
4609    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4610    /// substitute a `caixa.nome`-derived placeholder when the slot is
4611    /// absent (`None` → the fallback fires); a `Some("")` *skips the
4612    /// fallback* and silently passes the empty string through to
4613    /// `Chart.yaml description: ""` / a blank chart `README.md`
4614    /// header. Helm's chart spec requires a non-empty `description:`
4615    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
4616    /// `WARNING [chart.metadata.description]: description is required`),
4617    /// so the empty `Some("")` silently lands in the rendered
4618    /// artifacts and breaks at `helm lint` / `helm install` time far
4619    /// from the source `caixa.lisp`, with no field naming the
4620    /// offending `:descricao`.
4621    ///
4622    /// `None` (the canonical "omit the slot to defer to the renderer's
4623    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
4624    /// the gate is a no-op when the author didn't declare a value.
4625    /// `Some("")` is gated by the narrower
4626    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
4627    /// shape every peer per-axis empty gate uses
4628    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4629    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4630    /// [`ManifestError::RepositorioEmpty`]).
4631    ///
4632    /// Universal-axis (every kind carries `:descricao`), so wired at
4633    /// the caixa-build gate alongside the peer universal gates
4634    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4635    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4636    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4637    /// [`Self::validate_code_paths`] — before the kind-coherence
4638    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4639    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4640    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4641    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4642    /// specific slot sets.
4643    ///
4644    /// Past the empty arm the gate enforces the chart-description
4645    /// shape predicate via [`crate::render::is_chart_description_shape`]:
4646    /// the structural single-line UTF-8 floor every realistic chart
4647    /// description in the wild matches — 1..=512 bytes, no leading
4648    /// or trailing whitespace, no ASCII control characters anywhere
4649    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
4650    /// carriage return, and every other control byte), Unicode
4651    /// continuation bytes accepted (the canonical fixtures carry
4652    /// `→` and `—`). Closes the canonical paste-from-doc footguns
4653    /// the bare empty-arm gate left open: paste-from-aligned-doc
4654    /// leading / trailing whitespace (`" Checkout flow."`,
4655    /// `"Checkout flow. "`), paste-from-multiline-doc newline
4656    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
4657    /// (`"Checkout\rflow."`), tab-from-aligned-doc
4658    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
4659    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
4660    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
4661    /// [`Self::validate_edicao`] establish past their own empty arms
4662    /// on the sibling universal-axis `Option<String>` Caixa-level
4663    /// value-shape surfaces.
4664    ///
4665    /// The empty-first cascade discipline mirrors every peer per-axis
4666    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
4667    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
4668    /// diagnostic surfaces on `Some("")` rather than the broader
4669    /// shape-predicate diagnostic — peer with how
4670    /// [`ManifestError::LicencaEmpty`] runs before
4671    /// [`ManifestError::LicencaInvalid`],
4672    /// [`ManifestError::EdicaoEmpty`] runs before
4673    /// [`ManifestError::EdicaoInvalid`],
4674    /// [`ManifestError::RepositorioEmpty`] runs before
4675    /// [`ManifestError::RepositorioInvalid`].
4676    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
4677        let Some(s) = self.descricao() else {
4678            return Ok(());
4679        };
4680        if s.is_empty() {
4681            return Err(ManifestError::DescricaoEmpty);
4682        }
4683        crate::render::is_chart_description_shape(s).map_err(|reason| {
4684            ManifestError::DescricaoInvalid {
4685                descricao: s.to_string(),
4686                reason,
4687            }
4688        })?;
4689        Ok(())
4690    }
4691
4692    /// Reject `:licenca` values that are the empty string. The flat
4693    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
4694    /// SPDX-shaped license-expression axis every kind carries — the
4695    /// substrate routes the same string through the [`caixa-helm`]
4696    /// renderer's `build_readme` which folds it verbatim into the
4697    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
4698    /// section (`caixa-helm/src/lib.rs:361`) via
4699    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
4700    /// fallback only fires on `None`; a `Some("")` *skips the
4701    /// fallback* and silently passes the empty string through to a
4702    /// chart `README.md` whose `License` section renders as the bare
4703    /// trailing period (`.\n`) — peer footgun with the
4704    /// `Some("")`-skips-`unwrap_or_else` shape the
4705    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
4706    /// gates close on the sibling free-form-prose and git-URL axes.
4707    ///
4708    /// `None` (the canonical "omit the slot to defer to the
4709    /// renderer's `MIT` fallback" shape every existing fixture
4710    /// carries) is accepted trivially — the gate is a no-op when the
4711    /// author didn't declare a value. `Some("")` is gated by the
4712    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
4713    /// empty-arm shape every peer per-axis empty gate uses
4714    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4715    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4716    /// [`ManifestError::RepositorioEmpty`],
4717    /// [`ManifestError::DescricaoEmpty`]).
4718    ///
4719    /// Universal-axis (every kind carries `:licenca`), so wired at
4720    /// the caixa-build gate alongside the peer universal gates
4721    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4722    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4723    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4724    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
4725    /// — before the kind-coherence gates
4726    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4727    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4728    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4729    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4730    /// specific slot sets.
4731    ///
4732    /// Past the empty arm the gate enforces the SPDX-expression shape
4733    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
4734    /// structural alphabet floor every realistic SPDX expression in
4735    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
4736    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
4737    /// single ASCII space (token separator). Closes the canonical
4738    /// paste-from-doc footguns the bare empty-arm gate left open:
4739    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
4740    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
4741    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
4742    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
4743    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
4744    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
4745    /// Apache-2.0"`), and semicolon-list-separator confusion
4746    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
4747    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
4748    /// establish past their own empty arms.
4749    ///
4750    /// The empty-first cascade discipline mirrors every peer per-axis
4751    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
4752    /// [`ManifestError::LicencaInvalid`], so the narrower empty
4753    /// diagnostic surfaces on `Some("")` rather than the broader
4754    /// shape-predicate diagnostic — peer with how
4755    /// [`ManifestError::EdicaoEmpty`] runs before
4756    /// [`ManifestError::EdicaoInvalid`],
4757    /// [`ManifestError::RepositorioEmpty`] runs before
4758    /// [`ManifestError::RepositorioInvalid`].
4759    ///
4760    /// A future tightening on this axis can extend the alphabet
4761    /// floor into a full SPDX expression parser + license-id
4762    /// allowlist (rejecting alphabet-valid values that don't name a
4763    /// real SPDX license identifier — e.g., `"NotAReal"` is
4764    /// alphabet-valid but no `NotAReal` license-id exists). That
4765    /// parser only becomes meaningful past a real SPDX-spec
4766    /// dependency; this gate establishes the structural floor by
4767    /// refusing every non-SPDX-alphabet value at validate time.
4768    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
4769        let Some(s) = self.licenca() else {
4770            return Ok(());
4771        };
4772        if s.is_empty() {
4773            return Err(ManifestError::LicencaEmpty);
4774        }
4775        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
4776            ManifestError::LicencaInvalid {
4777                licenca: s.to_string(),
4778                reason,
4779            }
4780        })?;
4781        Ok(())
4782    }
4783
4784    /// Reject `:edicao` values that are the empty string. The flat
4785    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
4786    /// language-edition axis every kind carries — it determines the
4787    /// tatara-lisp macro surface + compatibility flags the substrate
4788    /// applies when building a caixa, and lands verbatim in the
4789    /// `Caixa::template` author-time scaffold (the canonical
4790    /// `:edicao "2026"` line every `feira init` emits via
4791    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
4792    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
4793    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
4794    /// `caixa-core/src/render.rs:2510`) via
4795    /// `edicao: Some("2026".into())`.
4796    ///
4797    /// `None` (the canonical "omit the slot to defer to the
4798    /// substrate's default edition" shape every existing
4799    /// [`caixa-resolver`] integration test fixture carries via
4800    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
4801    /// is accepted trivially — the gate is a no-op when the author
4802    /// didn't declare a value. `Some("")` is gated by the narrower
4803    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
4804    /// shape every peer per-axis empty gate uses
4805    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4806    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4807    /// [`ManifestError::RepositorioEmpty`],
4808    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
4809    ///
4810    /// Universal-axis (every kind carries `:edicao`), so wired at
4811    /// the caixa-build gate alongside the peer universal gates
4812    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4813    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4814    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4815    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4816    /// [`Self::validate_code_paths`] — before the kind-coherence
4817    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4818    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4819    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4820    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4821    /// specific slot sets.
4822    ///
4823    /// Past the empty arm the gate enforces the canonical year-shape
4824    /// predicate: every documented tatara-lisp edition is a 4-digit
4825    /// ASCII decimal year (`"2026"` is the only edition currently
4826    /// minted; future-introduced siblings will follow the same
4827    /// shape, peer with Cargo's `[package] edition` grammar which
4828    /// every value Cargo has ever accepted matches — `"2015"`,
4829    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
4830    /// 4 ASCII decimal bytes is rejected with the narrower
4831    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
4832    /// shape-predicate cascade [`Self::validate_repositorio`]
4833    /// establishes past its own empty arm
4834    /// ([`ManifestError::RepositorioEmpty`] →
4835    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
4836    /// paste-from-doc footguns the bare empty-arm gate left open:
4837    ///
4838    ///   - leading / trailing whitespace from a paste-from-doc
4839    ///     (`"2026 "`, `" 2026"`)
4840    ///   - control characters / CRLF from a paste-from-multiline-doc
4841    ///     (`"2026\n"`)
4842    ///   - non-ASCII look-alikes from a fullwidth keyboard
4843    ///     (`"2026"`) which would silently land as a non-ASCII
4844    ///     string in the rendered caixa.lisp
4845    ///   - free-form non-year values (`"x"`, `"latest"`,
4846    ///     `"nightly"`) that have no operational meaning on the
4847    ///     substrate's build-time edition selector
4848    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
4849    ///     `"r2026"`) — common version-tag idioms that don't apply
4850    ///     to the year-shaped edition axis
4851    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
4852    ///     edition is a year, not a fractional version
4853    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
4854    ///     `"00026"`) that don't name a year
4855    ///
4856    /// `None` (the canonical "omit the slot to defer to the
4857    /// substrate's default edition" shape every existing
4858    /// [`caixa-resolver`] integration test fixture carries via
4859    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
4860    /// is accepted trivially — the gate is a no-op when the author
4861    /// didn't declare a value. The empty-first cascade discipline
4862    /// mirrors every peer per-axis identity gate:
4863    /// [`ManifestError::EdicaoEmpty`] runs before
4864    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
4865    /// diagnostic surfaces on `Some("")` rather than the broader
4866    /// shape-predicate diagnostic — peer with how
4867    /// [`ManifestError::NomeEmpty`] runs before
4868    /// [`ManifestError::NomeInvalid`],
4869    /// [`ManifestError::VersaoEmpty`] runs before
4870    /// [`ManifestError::VersaoInvalid`],
4871    /// [`ManifestError::RepositorioEmpty`] runs before
4872    /// [`ManifestError::RepositorioInvalid`].
4873    ///
4874    /// A future tightening on this axis can extend the shape
4875    /// predicate into a known-edition allowlist (rejecting
4876    /// year-shaped values that don't name a tatara-lisp edition
4877    /// the substrate actually understands — e.g., `"1999"` is
4878    /// year-shaped but no `1999` edition exists). That allowlist
4879    /// only becomes meaningful past the introduction of a sibling
4880    /// edition to `"2026"`; this gate establishes the structural
4881    /// floor by refusing every non-year-shaped value at validate
4882    /// time.
4883    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
4884        let Some(s) = self.edicao() else {
4885            return Ok(());
4886        };
4887        if s.is_empty() {
4888            return Err(ManifestError::EdicaoEmpty);
4889        }
4890        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
4891            return Err(ManifestError::EdicaoInvalid {
4892                edicao: s.to_string(),
4893                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
4894            });
4895        }
4896        Ok(())
4897    }
4898
4899    /// Compose the supervisor-related flat slots into a single
4900    /// [`SupervisorSpec`] for validation. Returns `None` when the
4901    /// caixa isn't a `:kind Supervisor`.
4902    ///
4903    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
4904    /// simple (one form, no nested `:supervisor (…)` block); this view
4905    /// is the "typed shape" the operator + supervisor reconciler
4906    /// consume.
4907    #[must_use]
4908    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
4909        if !self.kind().is_supervisor() {
4910            return None;
4911        }
4912        // Fold through the shared `supervisor::duration_codec::parse`
4913        // — the same parser the serde-routed `with = "duration_codec"`
4914        // on `SupervisorSpec::restart_window`, the `:politicas
4915        // :timeout` codec, and the `:politicas :circuit-breaker
4916        // :window` codec all consume. The prior inline f64-shaped
4917        // duplicate (`parse_window_inline`) admitted every magnitude
4918        // the integer-magnitude gate (1c55a2a) rejects on the three
4919        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
4920        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
4921        // `None` (i.e. "no reset"), divergent from the shared codec's
4922        // integer-magnitude discipline by construction. The fold
4923        // closes the divergence: every value the typed
4924        // `SupervisorSpec` carries past `supervisor_view` is in the
4925        // shared codec's accepted set. The `.ok()` here preserves the
4926        // existing soft-swallow shape on this view-construction path;
4927        // the new [`Caixa::validate_restart_window`] (sibling of
4928        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
4929        // the offending raw string at build time so authoring tools
4930        // (`feira lint`, the future layout-side wire-up) surface a
4931        // self-locating diagnostic instead of a silently dropped
4932        // window.
4933        let restart_window = self
4934            .restart_window()
4935            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
4936        Some(SupervisorSpec {
4937            estrategia: self.estrategia().unwrap_or_default(),
4938            max_restarts: self.max_restarts().unwrap_or(5),
4939            restart_window,
4940            children: self.children().to_vec(),
4941        })
4942    }
4943
4944    /// A minimal starter manifest emitted by `feira init`.
4945    #[must_use]
4946    pub fn template(nome: &str) -> String {
4947        format!(
4948            "(defcaixa\n  \
4949               :nome        {nome:?}\n  \
4950               :versao      \"0.1.0\"\n  \
4951               :kind        Biblioteca\n  \
4952               :edicao      \"2026\"\n  \
4953               :descricao   \"FIXME — describe this caixa\"\n  \
4954               :autores     ()\n  \
4955               :etiquetas   ()\n  \
4956               :deps        ()\n  \
4957               :deps-dev    ()\n  \
4958               :bibliotecas (\"lib/{nome}.lisp\"))\n"
4959        )
4960    }
4961
4962    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
4963    /// back after mutation (e.g. `feira add`).
4964    ///
4965    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
4966    /// The derive-macro `compile_from_sexp` path is the inverse, so any
4967    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
4968    #[must_use]
4969    pub fn to_lisp(&self) -> String {
4970        let json = serde_json::to_value(self).expect("Caixa serialize");
4971        let sexp = tatara_lisp::domain::json_to_sexp(&json);
4972        let tatara_lisp::Sexp::List(items) = sexp else {
4973            return format!("(defcaixa {sexp})\n");
4974        };
4975        let mut out = String::from("(defcaixa");
4976        let mut i = 0;
4977        while i + 1 < items.len() {
4978            out.push_str("\n  ");
4979            out.push_str(&items[i].to_string());
4980            out.push(' ');
4981            out.push_str(&items[i + 1].to_string());
4982            i += 2;
4983        }
4984        out.push_str(")\n");
4985        out
4986    }
4987}
4988
4989/// Errors raised by top-level [`Caixa`] validators that don't fit
4990/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
4991/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
4992/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
4993/// through every substrate-side artifact's `metadata.name` /
4994/// version derivation.
4995///
4996/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
4997/// doc-comment anticipates) can hold one of each per-axis error
4998/// family without reshaping individual diagnostics; this enum is
4999/// the first such per-Caixa-identity family.
5000#[derive(Debug, Error, PartialEq, Eq)]
5001pub enum ManifestError {
5002    #[error(
5003        ":nome is empty (every caixa must name itself; the value flows \
5004         into every K8s artifact's `metadata.name` derivation and into \
5005         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
5006    )]
5007    NomeEmpty,
5008    #[error(
5009        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
5010         apiserver enforces this rule on every `metadata.name` the \
5011         caixa's substrate-side renderers derive from `:nome` — the \
5012         `lareira-<nome>` Helm chart name, the programs.yaml entry \
5013         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
5014         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
5015         name; use a lowercase alphanumeric + hyphen identifier like \
5016         `\"checkout\"` or `\"cart-v2\"`)"
5017    )]
5018    NomeInvalid { nome: String, reason: String },
5019    #[error(
5020        ":nome {nome:?} overflows the joint-length budget on the canonical \
5021         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
5022         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
5023         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
5024         `chart:` slot, `caixa-tatara`'s `release_name` + \
5025         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
5026         joint name through the canonical `lareira_chart_name` helper, and \
5027         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
5028         DNS-1123 label cap on every chart-name-derived `metadata.name` \
5029         reject any joint name exceeding 63 bytes; the narrower \
5030         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
5031         arm gates the chart-name budget downstream renderers inherit)"
5032    )]
5033    NomeChartNameBudgetExceeded { nome: String, reason: String },
5034    #[error(
5035        ":versao is empty (every caixa must pin its own version; the value flows \
5036         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
5037         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
5038         `:latest` tags, the lacre closure's `concrete_versao`, and the \
5039         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
5040    )]
5041    VersaoEmpty,
5042    #[error(
5043        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
5044         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
5045         with optional `-prerelease` and `+build` — across every artifact derived \
5046         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
5047         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
5048         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
5049         and the `:upgrade-from :from` peers that match against this exact shape; \
5050         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
5051         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
5052         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
5053    )]
5054    VersaoInvalid { versao: String, reason: String },
5055    #[error(
5056        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
5057         substrate consumes this string through the shared \
5058         `supervisor::duration_codec` — the same parser routed via `with = \
5059         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
5060         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
5061         the canonical authoring form is `<integer><unit>` where the unit is one \
5062         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
5063         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
5064         Without this gate a malformed `:restart-window` silently produced a \
5065         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
5066         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
5067         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
5068         layer with the offending value named verbatim. Omit the slot entirely to \
5069         express \"no reset\"; carry a positive integer duration to express the \
5070         sliding window)"
5071    )]
5072    RestartWindowMalformed {
5073        restart_window: String,
5074        reason: String,
5075    },
5076    #[error(
5077        "{slot} entry is an empty path string — every {slot} entry must name \
5078         a file relative to the caixa root; omit the entry to omit the file \
5079         (the layout checker's `root.join(\"\")` resolves to the caixa root \
5080         itself, so an empty entry silently aliases the project root as a \
5081         declared {slot} file, then fails downstream at parse / existence \
5082         time with a diagnostic that names the root rather than the offending \
5083         entry)"
5084    )]
5085    CodePathEmpty { slot: &'static str },
5086    #[error(
5087        "{slot} entry {} is an absolute path — entries must be relative to \
5088         the caixa root, since `Path::join` replaces the base with an absolute \
5089         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
5090         outside the caixa root sandbox; rewrite the entry as a relative path \
5091         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
5092         `\"servicos/<name>.computeunit.yaml\"`)",
5093        path.display()
5094    )]
5095    CodePathAbsolute { slot: &'static str, path: PathBuf },
5096    #[error(
5097        "{slot} entry {} contains a `..` component — entries must not traverse \
5098         above the caixa root (the layout's `starts_with(<dir>)` fence on \
5099         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
5100         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
5101         has no such fence, so a leading `..` escapes unconditionally if the \
5102         resolved target happens to exist)",
5103        path.display()
5104    )]
5105    CodePathParentEscape { slot: &'static str, path: PathBuf },
5106    #[error(
5107        "{slot} entry {} does not terminate in the `.lisp` extension — every \
5108         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
5109         loop reads through `tatara_lisp::read` at parse time, so any other \
5110         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
5111         structurally a parser error far from the source caixa.lisp, with \
5112         no field naming the offending `:bibliotecas` entry. Pin a relative \
5113         path under the caixa root whose terminating extension is \
5114         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
5115         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
5116         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
5117         (33cc830) axes already carry through the same lifted \
5118         `is_lisp_extension` predicate",
5119        path.display()
5120    )]
5121    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
5122    #[error(
5123        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
5124         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
5125         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
5126         through `serde_yaml::from_str` at chart / FluxCD bundle render \
5127         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
5128         off-by-one-segment `.computeunit-yaml`, the editor-backup \
5129         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
5130         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
5131         source caixa.lisp, with no field naming the offending `:servicos` \
5132         entry. Pin a relative path under the caixa root whose terminating \
5133         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
5134         `\"servicos/<name>.computeunit.yaml\"`, \
5135         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
5136         contract the sibling `:bibliotecas` axis (64772a9) already carries \
5137         on the tatara-lisp-source axis through the peer lifted \
5138         `is_lisp_extension` predicate, here on the compound-suffix axis \
5139         `Path::extension` can't express on its own through the lifted \
5140         `is_computeunit_yaml_extension` predicate",
5141        path.display()
5142    )]
5143    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
5144    #[error(
5145        "{slot} entry {} appears more than once (the code-path list is \
5146         a set, not a multiset; every peer Vec-shaped author-supplied \
5147         list past validate is set-not-multiset — `:membros :caixa`, \
5148         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5149         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
5150         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
5151         code-path lists are the last Vec-shaped author-supplied slots on \
5152         the typed Caixa surface still admitting a duplicate entry. \
5153         `:bibliotecas` duplicates re-parse the same file at \
5154         `feira build` time and silently mask the author's intent to \
5155         declare a *second* biblioteca; `:exe` duplicates collide on the \
5156         flake `packages.<name>` derivation key at the future \
5157         `caixa-flake` materializer; `:servicos` duplicates surface as the \
5158         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
5159         rejection far from the source `caixa.lisp`. Drop the duplicate \
5160         or rename it to the actual second file intended)",
5161        path.display()
5162    )]
5163    CodePathDuplicate { slot: &'static str, path: PathBuf },
5164    #[error(
5165        ":etiquetas entry is empty (every tag must carry a non-empty \
5166         registry-search identifier; the empty entry has no operational \
5167         meaning — it indexes nothing in the future caixa-registry search \
5168         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
5169         with a no-op tag; omit the entry to express \"no tag on this \
5170         position\")"
5171    )]
5172    EtiquetaEmpty,
5173    #[error(
5174        ":etiquetas entry {etiqueta:?} appears more than once (the \
5175         registry-search tag set is a set, not a multiset; duplicate \
5176         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
5177         at chart render — a \"second wins / one silently disappears\" \
5178         shape divergent from every peer typed-graph set gate \
5179         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
5180         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
5181         duplicate or rename it to the actual tag intended)"
5182    )]
5183    EtiquetaDuplicate { etiqueta: String },
5184    #[error(
5185        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
5186         {reason} (the substrate consumes this string through the shared \
5187         `crate::render::is_chart_keyword_shape` predicate — the same \
5188         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
5189         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
5190         continuation. The canonical authoring shapes are short kebab-case \
5191         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
5192         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
5193         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
5194         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
5195         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
5196         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
5197         `\"mesh,http,grpc\"` — the author meant to author three separate \
5198         list entries; path-separator confusion `\"caixa/servico\"`; \
5199         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
5200         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
5201         `\"café\"` — every legitimate search tag is strict ASCII; \
5202         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
5203         passed `from_lisp` + `validate_etiquetas` + \
5204         `StandardLayout::verify` and landed in the rendered \
5205         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
5206         malformed search tag — Artifact Hub's keyword index + the future \
5207         caixa-registry's keyword index would either silently drop the \
5208         tag or fail to index it far from the source caixa.lisp; the gate \
5209         moves the diagnostic to the manifest layer with the offending \
5210         value named verbatim)"
5211    )]
5212    EtiquetaInvalid { etiqueta: String, reason: String },
5213    #[error(
5214        ":autores entry is empty (every maintainer must carry a non-empty \
5215         identifier; the empty entry has no operational meaning — it \
5216         identifies no one in the substrate's authorship index and renders \
5217         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
5218         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
5219         omit the entry to express \"no maintainer on this position\")"
5220    )]
5221    AutorEmpty,
5222    #[error(
5223        ":autores entry {autor:?} appears more than once (the maintainer \
5224         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
5225         `maintainers:` rendering does *no* dedup — duplicate entries \
5226         stack verbatim in `Chart.yaml` as two identical \
5227         `Maintainer {{ name, email: None }}` records, divergent from every \
5228         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
5229         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5230         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
5231         rename it to the actual author intended)"
5232    )]
5233    AutorDuplicate { autor: String },
5234    #[error(
5235        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
5236         {reason} (the substrate consumes this string through the shared \
5237         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
5238         single-line-UTF-8 floor every realistic chart maintainer name carries: \
5239         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
5240         characters anywhere, Unicode bytes accepted. The canonical authoring \
5241         shapes are short single-line identifiers like `\"pleme-io\"`, \
5242         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
5243         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
5244         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
5245         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
5246         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
5247         records into one entry instead of splitting into one entry per author; \
5248         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
5249         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
5250         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5251         `validate_autores` + `StandardLayout::verify` and landed in the \
5252         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
5253         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
5254         round-trip — every chart-aware UI (`helm list`, `helm search`, \
5255         Artifact Hub maintainer index) would render the maintainer name in a \
5256         single-line column far from the source caixa.lisp; the gate moves the \
5257         diagnostic to the manifest layer with the offending value named \
5258         verbatim)"
5259    )]
5260    AutorInvalid { autor: String, reason: String },
5261    #[error(
5262        ":repositorio is the empty string (every published caixa names its \
5263         git source via a non-empty `:repositorio` locator — the value \
5264         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
5265         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
5266         `GitRepository.spec.url` via `caixa-flux`'s \
5267         `ClusterBundleOpts::for_caixa`; both consumers' \
5268         `Option::unwrap_or_else` fallbacks only fire when the slot is \
5269         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
5270         `url: \"\"` in the rendered artifacts and breaks at `helm \
5271         template` / FluxCD source-controller reconcile time far from the \
5272         source caixa.lisp; omit the slot entirely to defer to the \
5273         renderer's `https://github.com/pleme-io/<nome>` / \
5274         `caixa.nome`-derived fallback, or carry a canonical authoring \
5275         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
5276         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
5277         `\"file:///path\"`)"
5278    )]
5279    RepositorioEmpty,
5280    #[error(
5281        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
5282         (the substrate consumes this string through the shared \
5283         `crate::render::is_git_repo_url` predicate — the same parser the \
5284         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
5285         value through via `DepSource::validate`; the canonical authoring \
5286         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
5287         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
5288         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
5289         scp-style SSH form. Without this gate a malformed `:repositorio` \
5290         (whitespace from a paste-from-doc; control characters / CRLF \
5291         from a paste-from-multiline-doc; a leading `-` from a \
5292         CLI-argument-injection footgun; a missing `:` separator from a \
5293         bare `org/repo` shape git treats as a relative filesystem path) \
5294         silently landed in the rendered `Chart.yaml home:` and the \
5295         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
5296         FluxCD reconcile time far from the source caixa.lisp; the gate \
5297         moves the diagnostic to the manifest layer with the offending \
5298         value named verbatim)"
5299    )]
5300    RepositorioInvalid { repositorio: String, reason: String },
5301    #[error(
5302        ":descricao is the empty string (every published caixa names \
5303         its purpose via a non-empty `:descricao` summary — the value \
5304         flows verbatim into the rendered `lareira-<nome>` Helm \
5305         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
5306         `build_chart_yaml` and into the chart `README.md` header via \
5307         `build_readme`; both consumers' `Option::unwrap_or_else` \
5308         `caixa.nome`-derived fallbacks only fire when the slot is \
5309         `None`, so an empty `Some(\"\")` silently lands as \
5310         `description: \"\"` / a blank `README.md` header in the \
5311         rendered artifacts and breaks at `helm lint` time \
5312         (`WARNING [chart.metadata.description]: description is \
5313         required` on `apiVersion: v2` charts) far from the source \
5314         caixa.lisp; omit the slot entirely to defer to the \
5315         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
5316         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
5317         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
5318         Servico.\"`)"
5319    )]
5320    DescricaoEmpty,
5321    #[error(
5322        ":descricao {descricao:?} is not a valid chart-description shape: \
5323         {reason} (the substrate consumes this string through the shared \
5324         `crate::render::is_chart_description_shape` predicate — the same \
5325         single-line-UTF-8 floor every realistic chart description carries: \
5326         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
5327         characters anywhere, Unicode prose bytes accepted. The canonical \
5328         authoring shapes are short single-line summaries like `\"Canonical \
5329         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
5330         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
5331         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
5332         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
5333         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
5334         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
5335         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
5336         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5337         `validate_descricao` + `StandardLayout::verify` and landed in the \
5338         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
5339         field + `README.md` header paragraph as a YAML-illegal multi-line \
5340         scalar or a silently-trimmed whitespace round-trip — every \
5341         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
5342         render the description in a single-line column far from the source \
5343         caixa.lisp; the gate moves the diagnostic to the manifest layer \
5344         with the offending value named verbatim)"
5345    )]
5346    DescricaoInvalid { descricao: String, reason: String },
5347    #[error(
5348        ":licenca is the empty string (every published caixa names \
5349         its license via a non-empty `:licenca` SPDX expression — the \
5350         value flows verbatim into the rendered `lareira-<nome>` Helm \
5351         chart's `README.md` `## License` section via `caixa-helm`'s \
5352         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
5353         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
5354         only fires when the slot is `None`, so an empty `Some(\"\")` \
5355         silently lands as a bare trailing period in the rendered \
5356         chart `README.md` `License` section far from the source \
5357         caixa.lisp; omit the slot entirely to defer to the \
5358         renderer's `MIT` fallback, or carry a canonical SPDX \
5359         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
5360         `\"Apache-2.0 OR MIT\"`)"
5361    )]
5362    LicencaEmpty,
5363    #[error(
5364        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
5365         (the substrate consumes this string through the shared \
5366         `crate::render::is_spdx_expression_shape` predicate — the same \
5367         alphabet-floor parser every peer per-axis value-shape gate routes \
5368         its value through; the canonical authoring shapes are single \
5369         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
5370         compound expressions like `\"Apache-2.0 OR MIT\"`, \
5371         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
5372         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
5373         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
5374         like `\"LicenseRef-MyLicense\"` / \
5375         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
5376         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
5377         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
5378         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
5379         a smart-quote paste; underscore-instead-of-hyphen typo \
5380         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
5381         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
5382         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
5383         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
5384         `README.md` `## License` section + a future SPDX-aware \
5385         `Chart.yaml license:` emitter would refuse the value at \
5386         `helm lint` time far from the source caixa.lisp; the gate moves \
5387         the diagnostic to the manifest layer with the offending value \
5388         named verbatim)"
5389    )]
5390    LicencaInvalid { licenca: String, reason: String },
5391    #[error(
5392        ":edicao is the empty string (every published caixa names \
5393         its language edition via a non-empty `:edicao` value — the \
5394         edition determines the tatara-lisp macro surface + \
5395         compatibility flags the substrate applies when building \
5396         the caixa; the canonical `Caixa::template` scaffold every \
5397         `feira init` emits carries `:edicao \"2026\"` verbatim and \
5398         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
5399         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
5400         construction, so an empty `Some(\"\")` silently lands as a \
5401         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
5402         a future renderer-side consumer that folds it through \
5403         `Option::unwrap_or_else` will skip the fallback and pass the \
5404         empty edition through to the substrate's build-time edition \
5405         selector far from the source caixa.lisp; omit the slot \
5406         entirely to defer to the substrate's default edition, or \
5407         carry a canonical edition like `\"2026\"`)"
5408    )]
5409    EdicaoEmpty,
5410    #[error(
5411        ":edicao {edicao:?} is not a valid edition: {reason} (every \
5412         documented tatara-lisp edition is a 4-digit ASCII decimal \
5413         year — `\"2026\"` is the only edition currently minted; \
5414         future-introduced siblings will follow the same shape, peer \
5415         with Cargo's `[package] edition` grammar which every value \
5416         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
5417         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
5418         paste-from-doc footguns silently passed: a trailing space \
5419         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
5420         from a paste-from-multiline-doc, a fullwidth-keyboard \
5421         look-alike (`\"2026\"`), a free-form non-year value \
5422         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
5423         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
5424         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
5425         wrong-length numeric value (`\"26\"`, `\"202\"`, \
5426         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
5427         rendered caixa.lisp and broke at the substrate's \
5428         build-time edition selector far from the source caixa.lisp; \
5429         omit the slot entirely to defer to the substrate's default \
5430         edition, or carry a canonical 4-digit ASCII decimal year \
5431         like `\"2026\"`)"
5432    )]
5433    EdicaoInvalid { edicao: String, reason: String },
5434}
5435
5436#[cfg(test)]
5437mod tests {
5438    use super::*;
5439
5440    #[test]
5441    fn template_round_trips() {
5442        let src = Caixa::template("demo");
5443        let c = Caixa::from_lisp(&src).expect("template must parse");
5444        assert_eq!(c.nome, "demo");
5445        assert_eq!(c.versao, "0.1.0");
5446        assert_eq!(c.kind, CaixaKind::Biblioteca);
5447        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
5448        assert!(c.deps.is_empty());
5449        assert!(c.deps_dev.is_empty());
5450    }
5451
5452    #[test]
5453    fn register_populates_registry() {
5454        Caixa::register().expect("first register call in this test process must succeed");
5455        let kws = tatara_lisp::domain::registered_keywords();
5456        assert!(kws.contains(&"defcaixa"));
5457    }
5458
5459    #[test]
5460    fn to_lisp_round_trips() {
5461        let src = Caixa::template("demo");
5462        let c1 = Caixa::from_lisp(&src).unwrap();
5463        let emitted = c1.to_lisp();
5464        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
5465        assert_eq!(c1, c2);
5466    }
5467
5468    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
5469    //
5470    // The compounding pin: the variant stores only the typed
5471    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
5472    // (canonical keyword, description, consumer) routes through the enum's
5473    // own accessors at Display time. Prior to that closure the variant
5474    // carried each accessor's return value as a stored `&'static str`
5475    // snapshot alongside `dialeto`; a caller could construct the variant
5476    // with a snapshot that drifted from what `dialeto`'s accessors would
5477    // return, and every downstream user-facing projection would silently
5478    // disagree with the classification. Storing only the axis makes the
5479    // drift structurally impossible.
5480
5481    #[test]
5482    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
5483        // Single-field construction is the whole compounding shape — a
5484        // future re-introduction of a snapshot field (a `palavra_canonica:
5485        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
5486        // would re-open the drift surface and this construction would fail
5487        // to compile with "missing field" until every snapshot was seeded
5488        // at the call site again. The compile-time guarantee is the
5489        // invariant; the assertion below only witnesses that the
5490        // construction is well-formed after the closure.
5491        let err = LeituraError::DialetoEstrangeiro {
5492            dialeto: crate::dialeto::CaixaDialeto::Molde,
5493        };
5494        assert!(matches!(
5495            err,
5496            LeituraError::DialetoEstrangeiro {
5497                dialeto: crate::dialeto::CaixaDialeto::Molde,
5498            }
5499        ));
5500    }
5501
5502    #[test]
5503    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
5504        // For every foreign-dialect classification the variant surfaces —
5505        // [`crate::dialeto::CaixaDialeto::Molde`] and
5506        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
5507        // variants [`Caixa::from_lisp`] raises this error for — the
5508        // rendered [`std::fmt::Display`] byte-string must interpolate each
5509        // typed accessor's return verbatim. A future re-introduction of a
5510        // stored `&'static str` snapshot alongside `dialeto` that Display
5511        // read instead of the accessor would fail this pin as soon as the
5512        // two disagreed; a future accessor rebrand (a per-dialect
5513        // consumer rename, a canonical-keyword shift once the substrate
5514        // migration named in [`crate::dialeto`] completes) reaches every
5515        // consumer through one typed dispatch and this pin verifies the
5516        // display path is one of them.
5517        for d in [
5518            crate::dialeto::CaixaDialeto::Molde,
5519            crate::dialeto::CaixaDialeto::MoldePosicional,
5520        ] {
5521            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
5522            assert!(
5523                rendered.contains(d.palavra_canonica()),
5524                "Display must interpolate `dialeto.palavra_canonica()` \
5525                 verbatim — a stored snapshot would silently drift from \
5526                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
5527            );
5528            assert!(
5529                rendered.contains(d.descricao()),
5530                "Display must interpolate `dialeto.descricao()` verbatim. \
5531                 dialect: {d}, rendered: {rendered:?}"
5532            );
5533            assert!(
5534                rendered.contains(d.consumidor()),
5535                "Display must interpolate `dialeto.consumidor()` verbatim. \
5536                 dialect: {d}, rendered: {rendered:?}"
5537            );
5538        }
5539    }
5540
5541    #[test]
5542    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
5543        // The end-to-end pin the compounding closure defends: a
5544        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
5545        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
5546        // rendered Display byte-string names the Molde accessors'
5547        // returns verbatim. Any future path that constructed the variant
5548        // with a mismatched snapshot (a stored `palavra_canonica:
5549        // "defcaixa"` on a `Molde` classification) would land Display
5550        // pointing at `defcaixa` while the typed axis said `Molde` — the
5551        // exact drift the closure removes.
5552        let src = r#"
5553          (defcaixa
5554            :name "x"
5555            :kind :Biblioteca
5556            :ecosystem :rust-single-crate
5557            :package {:name "x" :version "0.1.0"})
5558        "#;
5559        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
5560        match err {
5561            LeituraError::DialetoEstrangeiro { dialeto } => {
5562                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
5563                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5564                assert!(rendered.contains(dialeto.palavra_canonica()));
5565                assert!(rendered.contains(dialeto.consumidor()));
5566                assert!(rendered.contains(dialeto.descricao()));
5567            }
5568            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
5569        }
5570    }
5571
5572    #[test]
5573    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
5574        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
5575        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
5576        // positional-arity `defmolde` form written under a `(defcaixa …)`
5577        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
5578        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
5579        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
5580        // so no test exercised the positional-arity path through
5581        // `Caixa::from_lisp` specifically; the sibling
5582        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
5583        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
5584        // two arms route through the lifted
5585        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
5586        // typed predicate — the same predicate the pre-lift `foreign =>`
5587        // wildcard resolved to today — and this pin makes the
5588        // positional-arity arm's byte-shape at the gate explicit rather
5589        // than implied by wildcard-absorption. A future regression that
5590        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
5591        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
5592        // from the two-arity closure) would fail this pin at caixa-core
5593        // test time rather than surfacing far from the change as a
5594        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
5595        // …)` silently parsing past the derive.
5596        let src = r#"
5597          (defcaixa todoku-go
5598            :kind :Biblioteca
5599            :ecosystem :go
5600            :package {:name "todoku-go" :version "0.3.0"})
5601        "#;
5602        let err =
5603            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
5604        match err {
5605            LeituraError::DialetoEstrangeiro { dialeto } => {
5606                assert_eq!(
5607                    dialeto,
5608                    crate::dialeto::CaixaDialeto::MoldePosicional,
5609                    "DialetoEstrangeiro must carry the MoldePosicional \
5610                     variant verbatim — the positional-arity `defmolde` \
5611                     form under a `(defcaixa …)` head is the \
5612                     `MoldePosicional` arm's canonical byte-shape"
5613                );
5614                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5615                assert!(
5616                    rendered.contains(dialeto.palavra_canonica()),
5617                    "Display must interpolate `dialeto.palavra_canonica()` \
5618                     verbatim on the MoldePosicional arm; rendered: \
5619                     {rendered:?}"
5620                );
5621                assert!(
5622                    rendered.contains(dialeto.consumidor()),
5623                    "Display must interpolate `dialeto.consumidor()` \
5624                     verbatim on the MoldePosicional arm; rendered: \
5625                     {rendered:?}"
5626                );
5627                assert!(
5628                    rendered.contains(dialeto.descricao()),
5629                    "Display must interpolate `dialeto.descricao()` \
5630                     verbatim on the MoldePosicional arm; rendered: \
5631                     {rendered:?}"
5632                );
5633            }
5634            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
5635        }
5636    }
5637
5638    #[test]
5639    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
5640        // Load-bearing byte-parity pin: for every arm in
5641        // [`crate::dialeto::CaixaDialeto::ALL`], the
5642        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
5643        // partition must agree with the lifted
5644        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
5645        // typed predicate — i.e. from_lisp raises
5646        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
5647        // `d.is_molde_family()` returns `true`, and does NOT raise
5648        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
5649        // predicate returns `false` (the arm's source falls through to
5650        // the derive — parses cleanly on
5651        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
5652        // [`LeituraError::Leitura`] on
5653        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
5654        //
5655        // Pre-lift the gate hand-rolled a three-arm match
5656        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
5657        // whose `foreign =>` wildcard expressed no compile-time link
5658        // back to the substrate primitive's arm-family; a future fifth
5659        // dialect the [`crate::dialeto`] module doc's "third dialect"
5660        // hazard actualises would fall silently onto the wildcard
5661        // regardless of whether it belonged to the `defmolde` family or
5662        // to a distinct `defcaixa`-family. Post-lift the partition
5663        // resolves through
5664        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
5665        // typed dispatch, and this pin refuses any future regression
5666        // that silently split the from_lisp partition from the typed
5667        // predicate — the two paths now migrate as one on any future
5668        // arm addition.
5669        //
5670        // Sibling in shape to the peer
5671        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
5672        // (e9d2315) that pins the same byte-parity between
5673        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
5674        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
5675        // `== "defmolde"` classifier — extends the discipline from the
5676        // two paths within the [`crate::dialeto`] primitive onto the
5677        // third external consumer of the `defmolde`-family partition
5678        // (the [`Caixa::from_lisp`] gate that raises
5679        // [`LeituraError::DialetoEstrangeiro`]).
5680        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
5681            (
5682                crate::dialeto::CaixaDialeto::Pacote,
5683                r#"
5684                  (defcaixa
5685                    :nome   "checkout"
5686                    :versao "0.1.0"
5687                    :kind   Biblioteca
5688                    :edicao "2026"
5689                    :descricao "canonical Pacote source"
5690                    :autores ()
5691                    :etiquetas ()
5692                    :deps ()
5693                    :deps-dev ()
5694                    :bibliotecas ("lib/checkout.lisp"))
5695                "#,
5696            ),
5697            (
5698                crate::dialeto::CaixaDialeto::Molde,
5699                r#"
5700                  (defcaixa
5701                    :name "base64"
5702                    :kind :Biblioteca
5703                    :ecosystem :rust-single-crate
5704                    :package {:name "base64" :version "0.22.1"}
5705                    :workflows [:auto-release])
5706                "#,
5707            ),
5708            (
5709                crate::dialeto::CaixaDialeto::MoldePosicional,
5710                r#"
5711                  (defcaixa todoku-go
5712                    :kind :Biblioteca
5713                    :ecosystem :go
5714                    :package {:name "todoku-go" :version "0.3.0"})
5715                "#,
5716            ),
5717            (
5718                crate::dialeto::CaixaDialeto::Desconhecido,
5719                r#"(defcaixa :licenca "MIT")"#,
5720            ),
5721        ];
5722
5723        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
5724        // must appear in the fixture table so the pin's arm-set stays
5725        // synchronised with the enum's arm-set. Fails at test time if a
5726        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
5727        // (with a corresponding `is_molde_family` return) forgot to
5728        // extend this fixture table with a canonical source for the new
5729        // arm — the pin cannot cover an arm it has no source for.
5730        for &expected in crate::dialeto::CaixaDialeto::ALL {
5731            assert!(
5732                fixtures.iter().any(|(d, _)| *d == expected),
5733                "fixture table must carry a canonical source for every \
5734                 CaixaDialeto arm; missing: {expected:?}"
5735            );
5736        }
5737
5738        for &(expected_dialect, src) in fixtures {
5739            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
5740                panic!(
5741                    "fixture source for {expected_dialect:?} must classify \
5742                     cleanly, got err: {err:?}"
5743                )
5744            });
5745            assert_eq!(
5746                classified, expected_dialect,
5747                "fixture source for {expected_dialect:?} must classify as \
5748                 {expected_dialect:?} (drift here defeats the byte-parity \
5749                 pin below — a source labelled for one arm but classifying \
5750                 as another would silently satisfy or violate the pin for \
5751                 the wrong reason)"
5752            );
5753
5754            let outcome = Caixa::from_lisp(src);
5755            match (expected_dialect.is_molde_family(), &outcome) {
5756                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
5757                    assert_eq!(
5758                        *dialeto, expected_dialect,
5759                        "DialetoEstrangeiro must carry the same typed arm \
5760                         the classifier returned — a drift here would let \
5761                         from_lisp raise the error while pointing at the \
5762                         wrong dialect (e.g. rejecting a \
5763                         MoldePosicional source as Molde). arm: \
5764                         {expected_dialect:?}"
5765                    );
5766                }
5767                (true, other) => panic!(
5768                    "arm {expected_dialect:?} has is_molde_family() = true \
5769                     so from_lisp must raise DialetoEstrangeiro carrying \
5770                     {expected_dialect:?}; got: {other:?}"
5771                ),
5772                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
5773                    "arm {expected_dialect:?} has is_molde_family() = false \
5774                     so from_lisp must NOT raise DialetoEstrangeiro; got \
5775                     one carrying: {dialeto:?}. This means the typed \
5776                     predicate and the from_lisp partition disagree on \
5777                     this arm — exactly the drift this pin refuses."
5778                ),
5779                (false, _) => {
5780                    // A non-molde arm's source falls through to the
5781                    // derive: Pacote sources parse to Ok(_); Desconhecido
5782                    // sources surface as LeituraError::Leitura from the
5783                    // derive's own unknown-keyword rejection. Either
5784                    // shape is acceptable here — the pin's promise is
5785                    // narrower: "no DialetoEstrangeiro on
5786                    // is_molde_family() == false".
5787                }
5788            }
5789        }
5790    }
5791
5792    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
5793
5794    #[test]
5795    fn limits_round_trip_via_json() {
5796        use crate::LimitsSpec;
5797        use std::time::Duration;
5798        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5799        c.limits = Some(LimitsSpec {
5800            memory: Some(64 * 1024 * 1024),
5801            fuel: Some(1_000_000),
5802            wall_clock: Some(Duration::from_secs(30)),
5803            cpu: Some(500),
5804        });
5805        let json = serde_json::to_string(&c).unwrap();
5806        assert!(json.contains("\"limits\""));
5807        assert!(json.contains("\"64MiB\""));
5808        assert!(json.contains("\"30s\""));
5809        assert!(json.contains("\"500m\""));
5810        let back: Caixa = serde_json::from_str(&json).unwrap();
5811        assert_eq!(c.limits, back.limits);
5812    }
5813
5814    #[test]
5815    fn behavior_round_trip_via_json() {
5816        use crate::BehaviorSpec;
5817        use std::path::PathBuf;
5818        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5819        c.behavior = Some(BehaviorSpec {
5820            on_init: Some(PathBuf::from("lib/init.lisp")),
5821            on_call: Some(PathBuf::from("lib/handlers.lisp")),
5822            ..Default::default()
5823        });
5824        let json = serde_json::to_string(&c).unwrap();
5825        let back: Caixa = serde_json::from_str(&json).unwrap();
5826        assert_eq!(c.behavior, back.behavior);
5827    }
5828
5829    #[test]
5830    fn upgrade_from_round_trip_via_json() {
5831        use crate::{UpgradeFromEntry, UpgradeInstruction};
5832        use std::path::PathBuf;
5833        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5834        c.upgrade_from = vec![UpgradeFromEntry {
5835            from: "0.1.0".into(),
5836            instructions: vec![
5837                UpgradeInstruction::LoadModule {
5838                    module: "demo".into(),
5839                },
5840                UpgradeInstruction::StateChange {
5841                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
5842                },
5843                UpgradeInstruction::SoftPurge {
5844                    module: "demo-old".into(),
5845                },
5846            ],
5847        }];
5848        let json = serde_json::to_string(&c).unwrap();
5849        let back: Caixa = serde_json::from_str(&json).unwrap();
5850        assert_eq!(c.upgrade_from, back.upgrade_from);
5851    }
5852
5853    #[test]
5854    fn supervisor_view_returns_typed_shape() {
5855        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
5856        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
5857        c.kind = CaixaKind::Supervisor;
5858        c.bibliotecas.clear();
5859        c.estrategia = Some(RestartStrategy::OneForOne);
5860        c.max_restarts = Some(5);
5861        c.restart_window = Some("60s".into());
5862        c.children = vec![ChildSpec {
5863            caixa: "worker".into(),
5864            versao: "^0.1".into(),
5865            restart: RestartPolicy::Permanent,
5866        }];
5867        let view = c.supervisor_view().expect("Supervisor kind has a view");
5868        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
5869        assert_eq!(view.max_restarts, 5);
5870        assert_eq!(
5871            view.restart_window,
5872            Some(std::time::Duration::from_secs(60))
5873        );
5874        assert_eq!(view.children.len(), 1);
5875        view.validate().unwrap();
5876    }
5877
5878    #[test]
5879    fn supervisor_view_none_for_non_supervisor_kinds() {
5880        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5881        assert!(c.supervisor_view().is_none());
5882    }
5883
5884    #[test]
5885    fn declared_mesh_slots_empty_for_bare_caixa() {
5886        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5887        assert!(c.declared_mesh_slots().is_empty());
5888    }
5889
5890    #[test]
5891    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
5892        use crate::{Entrada, Membro};
5893        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5894        // Set a non-adjacent pair (:membros + :entrada) to pin that the
5895        // canonical declaration order is preserved regardless of which
5896        // subset is populated.
5897        c.membros = vec![Membro {
5898            caixa: "a".into(),
5899            versao: "^0.1".into(),
5900        }];
5901        c.entrada = Some(Entrada {
5902            host: "x.example.com".into(),
5903            para: "a".into(),
5904            paths: vec![],
5905            port: 8080,
5906        });
5907        assert_eq!(
5908            c.declared_mesh_slots(),
5909            vec![
5910                crate::render::M3_AUTHOR_KEY_MEMBROS,
5911                crate::render::M3_AUTHOR_KEY_ENTRADA,
5912            ]
5913        );
5914    }
5915
5916    #[test]
5917    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
5918        // Scalar-value pin: the five author-facing kebab-case labels the
5919        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
5920        // mesh slot axis, one arm per typed slot. Mirrors the peer
5921        // scalar-value pin the sibling
5922        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
5923        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
5924        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
5925        // carry (f49c8b0), so both altitudes of the typed-slot algebra
5926        // (per-Servico M2 + per-Aplicacao M3) share the same
5927        // "one canonical byte-string per arm" discipline. A future
5928        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
5929        // `:politicas` → `:policies`, `:placement` → `:distribution`,
5930        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
5931        // and every consumer that reaches for the label picks it up at
5932        // build time rather than at runtime as a downstream mismatch.
5933        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
5934        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
5935        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
5936        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
5937        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
5938    }
5939
5940    #[test]
5941    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
5942        // Production-through-const pin: the five per-arm labels the
5943        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
5944        // `Vec` route through the lifted
5945        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
5946        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
5947        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
5948        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
5949        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
5950        // declaration order. A future re-order or drift at the tagger
5951        // (a rename that reaches the tagger but not the const, or vice
5952        // versa) surfaces here at build time rather than at runtime as
5953        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
5954        // `slots: <stale-kebab-case>` diagnostic far from the rename's
5955        // commit. Mirror of the peer
5956        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
5957        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
5958        // axis.
5959        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
5960        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5961        c.membros = vec![Membro {
5962            caixa: "a".into(),
5963            versao: "^0.1".into(),
5964        }];
5965        c.contratos = vec![WitContract {
5966            de: "a".into(),
5967            para: "a".into(),
5968            wit: "wasi:http/proxy".into(),
5969            endpoint: Some("/x".into()),
5970            subject: None,
5971            slot: None,
5972        }];
5973        c.politicas = Some(MeshPolicy::default());
5974        c.placement = Some(Placement {
5975            estrategia: PlacementStrategy::Replicated,
5976            clusters: vec!["rio".into()],
5977            affinity: None,
5978            shard_key: None,
5979        });
5980        c.entrada = Some(Entrada {
5981            host: "x.example.com".into(),
5982            para: "a".into(),
5983            paths: vec![],
5984            port: 8080,
5985        });
5986        assert_eq!(
5987            c.declared_mesh_slots(),
5988            vec![
5989                crate::render::M3_AUTHOR_KEY_MEMBROS,
5990                crate::render::M3_AUTHOR_KEY_CONTRATOS,
5991                crate::render::M3_AUTHOR_KEY_POLITICAS,
5992                crate::render::M3_AUTHOR_KEY_PLACEMENT,
5993                crate::render::M3_AUTHOR_KEY_ENTRADA,
5994            ]
5995        );
5996    }
5997
5998    #[test]
5999    fn declared_supervisor_slots_empty_for_bare_caixa() {
6000        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6001        assert!(c.declared_supervisor_slots().is_empty());
6002    }
6003
6004    #[test]
6005    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
6006        use crate::RestartStrategy;
6007        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6008        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
6009        // that the canonical declaration order is preserved regardless
6010        // of which subset is populated.
6011        c.estrategia = Some(RestartStrategy::OneForOne);
6012        c.restart_window = Some("60s".into());
6013        assert_eq!(
6014            c.declared_supervisor_slots(),
6015            vec![
6016                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6017                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6018            ]
6019        );
6020    }
6021
6022    #[test]
6023    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6024        // Scalar-value pin: the four author-facing kebab-case labels the
6025        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
6026        // supervision-tree slot axis, one arm per typed slot. Mirrors the
6027        // peer scalar-value pins the sibling
6028        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
6029        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
6030        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
6031        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
6032        // top-level M3 slot consts carry, so all three kind-scoped
6033        // typed-slot-family author-facing-label axes route through one
6034        // canonical per-arm declaration. A future rebrand
6035        // (`:estrategia` → `:strategy` for English uniformity,
6036        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
6037        // `MaxIntensity` name, `:restart-window` → `:period` matching
6038        // OTP's `Period` name, `:children` → `:workers` matching Elixir
6039        // idiom) lands as an edit to exactly one const, and every
6040        // consumer that reaches for the label picks it up at build time
6041        // rather than at runtime as a downstream mismatch.
6042        assert_eq!(
6043            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6044            ":estrategia"
6045        );
6046        assert_eq!(
6047            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6048            ":max-restarts"
6049        );
6050        assert_eq!(
6051            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6052            ":restart-window"
6053        );
6054        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
6055    }
6056
6057    #[test]
6058    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
6059        // Production-through-const pin: the four per-arm labels the
6060        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
6061        // return `Vec` route through the lifted
6062        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
6063        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
6064        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
6065        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
6066        // canonical declaration order. A future re-order or drift at the
6067        // tagger (a rename that reaches the tagger but not the const, or
6068        // vice versa) surfaces here at build time rather than at runtime
6069        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
6070        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6071        // commit. Mirror of the peer
6072        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6073        // (f49c8b0) and
6074        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
6075        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
6076        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
6077        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6078        c.estrategia = Some(RestartStrategy::OneForOne);
6079        c.max_restarts = Some(5);
6080        c.restart_window = Some("60s".into());
6081        c.children = vec![ChildSpec {
6082            caixa: "worker".into(),
6083            versao: "^0.1".into(),
6084            restart: RestartPolicy::Permanent,
6085        }];
6086        assert_eq!(
6087            c.declared_supervisor_slots(),
6088            vec![
6089                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6090                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6091                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6092                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
6093            ]
6094        );
6095    }
6096
6097    #[test]
6098    fn declared_servico_slots_empty_for_bare_caixa() {
6099        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6100        assert!(c.declared_servico_slots().is_empty());
6101    }
6102
6103    #[test]
6104    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
6105        use crate::{UpgradeFromEntry, UpgradeInstruction};
6106        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6107        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
6108        // the canonical declaration order is preserved regardless of
6109        // which subset is populated.
6110        c.limits = Some(crate::LimitsSpec {
6111            fuel: Some(1_000_000),
6112            ..Default::default()
6113        });
6114        c.upgrade_from = vec![UpgradeFromEntry {
6115            from: "0.1.0".into(),
6116            instructions: vec![UpgradeInstruction::Restart],
6117        }];
6118        assert_eq!(
6119            c.declared_servico_slots(),
6120            vec![
6121                crate::render::M2_AUTHOR_KEY_LIMITS,
6122                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6123            ]
6124        );
6125    }
6126
6127    #[test]
6128    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6129        // Scalar-value pin: the three author-facing kebab-case labels
6130        // the `(defcaixa … :<slot> (…))` surface admits on the M2
6131        // top-level slot axis, one arm per typed slot. Mirrors the peer
6132        // scalar-value pin the sibling renderer-side
6133        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
6134        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
6135        // consts carry, so both halves of the M2 top-level slot dual
6136        // axis (author-facing kebab-case label + renderer-side
6137        // camelCase overlay-container wire key) route through one
6138        // canonical per-arm declaration. A future rebrand
6139        // (`:limits` → `:sandbox` matching Lunatic per-process
6140        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
6141        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
6142        // matching Erlang's verbatim appup name) lands as an edit to
6143        // exactly one const, and every consumer that reaches for the
6144        // label picks it up at build time rather than at runtime as a
6145        // downstream mismatch.
6146        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
6147        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
6148        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
6149    }
6150
6151    #[test]
6152    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
6153        // Production-through-const pin: the three per-arm labels the
6154        // [`Caixa::declared_servico_slots`] tagger pushes onto its
6155        // return `Vec` route through the lifted
6156        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
6157        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
6158        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
6159        // declaration order. A future re-order or drift at the tagger
6160        // (a rename that reaches the tagger but not the const, or vice
6161        // versa) surfaces here at build time rather than at runtime as
6162        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
6163        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6164        // commit. Mirror of the peer
6165        // [`crate::behavior::BehaviorSpec::declared_slots`] production
6166        // tagger pin (889dc18) on the sibling per-callback axis.
6167        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
6168        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6169        c.limits = Some(crate::LimitsSpec {
6170            fuel: Some(1_000_000),
6171            ..Default::default()
6172        });
6173        c.behavior = Some(BehaviorSpec {
6174            on_init: Some(PathBuf::from("lib/init.lisp")),
6175            ..Default::default()
6176        });
6177        c.upgrade_from = vec![UpgradeFromEntry {
6178            from: "0.1.0".into(),
6179            instructions: vec![UpgradeInstruction::Restart],
6180        }];
6181        assert_eq!(
6182            c.declared_servico_slots(),
6183            vec![
6184                crate::render::M2_AUTHOR_KEY_LIMITS,
6185                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
6186                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6187            ]
6188        );
6189    }
6190
6191    #[test]
6192    fn existing_manifests_unaffected_by_new_optional_slots() {
6193        // Regression test: a caixa.lisp authored before M2 typed slots
6194        // should still parse + serialize cleanly. The bare `defcaixa`
6195        // emitted by `Caixa::template` has none of the new fields.
6196        let src = Caixa::template("legacy");
6197        let c = Caixa::from_lisp(&src).unwrap();
6198        assert!(c.limits.is_none());
6199        assert!(c.behavior.is_none());
6200        assert!(c.upgrade_from.is_empty());
6201        assert!(c.estrategia.is_none());
6202        assert!(c.children.is_empty());
6203
6204        // And to_lisp emits a manifest with the new slots in the
6205        // empty/default state — round-trippable.
6206        let emitted = c.to_lisp();
6207        let back = Caixa::from_lisp(&emitted).unwrap();
6208        assert_eq!(c, back);
6209    }
6210
6211    #[test]
6212    fn validate_deps_accepts_canonical_caixa() {
6213        // Positive control: the bare template — zero deps, zero
6214        // deps_dev — passes the gate trivially. A future axis added to
6215        // `Dep::validate` mustn't regress an empty-deps caixa to a
6216        // build error.
6217        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6218        c.validate_deps().unwrap();
6219    }
6220
6221    #[test]
6222    fn validate_deps_rejects_invalid_versao_in_deps() {
6223        // Fail-before-pass-after pin: a malformed `:deps :versao`
6224        // surfaces at validate_deps() time, not at lacre-resolve time.
6225        // Mirrors `rejects_invalid_membro_versao_requirement` and
6226        // `validate_rejects_invalid_child_versao_requirement` on the
6227        // other two `:versao` axes.
6228        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6229        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
6230        let err = c.validate_deps().unwrap_err();
6231        assert!(
6232            matches!(
6233                err,
6234                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6235                    if nome == "caixa-teia" && versao == "^bad-version"
6236            ),
6237            "got {err:?}"
6238        );
6239    }
6240
6241    #[test]
6242    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
6243        // Parity pin: `:deps-dev` must run through the same per-entry
6244        // validator as `:deps` — a typo in either axis surfaces the
6245        // same diagnostic. Without this leg, `:deps-dev` would be a
6246        // second-class citizen of the typed surface and an author
6247        // could land a build that passes validate_deps but fails at
6248        // `feira lock`-time when the dev-dep is resolved for a test
6249        // build.
6250        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6251        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
6252        let err = c.validate_deps().unwrap_err();
6253        assert!(
6254            matches!(
6255                err,
6256                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6257                    if nome == "tatara-check" && versao == "^^0.1"
6258            ),
6259            "got {err:?}"
6260        );
6261    }
6262
6263    #[test]
6264    fn validate_deps_runs_deps_before_deps_dev() {
6265        // Order pin: when both lists carry typos, the `:deps`
6266        // diagnostic surfaces first. The author's mental model is
6267        // "runtime deps are load-bearing; dev deps are scaffolding";
6268        // surfacing the runtime axis first matches that hierarchy.
6269        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6270        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
6271        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
6272        let err = c.validate_deps().unwrap_err();
6273        assert!(
6274            matches!(
6275                err,
6276                crate::dep::DepError::VersaoInvalid { ref nome, .. }
6277                    if nome == "runtime-dep"
6278            ),
6279            "expected `:deps` typo to surface first, got {err:?}"
6280        );
6281    }
6282
6283    #[test]
6284    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
6285        // Positive control sweep across both lists. Pin every
6286        // canonical Cargo-shaped form so a future tightening of the
6287        // accepted set surfaces here as a test failure (parity with
6288        // `accepts_canonical_membro_versao_forms` and
6289        // `validate_accepts_canonical_child_versao_forms`).
6290        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6291        c.deps = vec![
6292            Dep::simple("caret", "^0.1"),
6293            Dep::simple("tilde", "~0.1.2"),
6294            Dep::simple("exact", "0.1.0"),
6295            Dep::simple("wildcard", "*"),
6296            Dep::simple("multi-range", ">=0.1, <2"),
6297        ];
6298        c.deps_dev = vec![
6299            Dep::simple("dev-caret", "^0.1"),
6300            Dep::simple("dev-wildcard", "*"),
6301        ];
6302        c.validate_deps().unwrap();
6303    }
6304
6305    #[test]
6306    fn validate_deps_diagnostic_carries_offending_dep() {
6307        // Diagnostic-shape pin: the error names the offending entry's
6308        // `:nome` + `:versao` verbatim and carries a non-empty
6309        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
6310        // run can render the diagnostic without re-parsing.
6311        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6312        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
6313        let err = c.validate_deps().unwrap_err();
6314        let crate::dep::DepError::VersaoInvalid {
6315            nome,
6316            versao,
6317            reason,
6318        } = err
6319        else {
6320            panic!("expected VersaoInvalid, got other variant");
6321        };
6322        assert_eq!(nome, "caixa-teia");
6323        assert_eq!(versao, "not-a-req");
6324        assert!(
6325            !reason.is_empty(),
6326            "VersaoInvalid `reason` must carry the parser's wording verbatim"
6327        );
6328    }
6329
6330    #[test]
6331    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
6332        // Cross-axis pin: `validate_deps` walks both :deps and
6333        // :deps-dev through `Dep::validate`, and the new fonte gate
6334        // (`:tag` + `:branch` both set — the canonical "pin drift"
6335        // footgun) must surface from the :deps-dev arm with the
6336        // offending entry's :nome named. Pin the :deps-dev arm
6337        // explicitly so a future shortcut that only walks :deps
6338        // surfaces here as a regression.
6339        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6340        c.deps_dev = vec![Dep {
6341            nome: "dev-only".into(),
6342            versao: "^0.1".into(),
6343            fonte: Some(crate::DepSource::Git {
6344                repo: "github:p/x".into(),
6345                tag: Some("v1".into()),
6346                rev: None,
6347                branch: Some("main".into()),
6348            }),
6349            opcional: false,
6350            caracteristicas: vec![],
6351        }];
6352        let err = c.validate_deps().unwrap_err();
6353        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
6354            panic!("expected FontePinAmbiguous from :deps-dev walk");
6355        };
6356        assert_eq!(nome, "dev-only");
6357        assert!(pins.contains(":tag") && pins.contains(":branch"));
6358    }
6359
6360    #[test]
6361    fn validate_deps_rejects_empty_repo_in_deps() {
6362        // Parity pin on the :deps arm: an empty :repo on the runtime
6363        // deps list surfaces the same FonteRepoEmpty diagnostic the
6364        // dep.rs per-entry tests pin, naming the offending entry.
6365        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6366        c.deps = vec![Dep {
6367            nome: "runtime".into(),
6368            versao: "^0.1".into(),
6369            fonte: Some(crate::DepSource::Git {
6370                repo: String::new(),
6371                tag: Some("v1".into()),
6372                rev: None,
6373                branch: None,
6374            }),
6375            opcional: false,
6376            caracteristicas: vec![],
6377        }];
6378        let err = c.validate_deps().unwrap_err();
6379        assert!(
6380            matches!(
6381                err,
6382                crate::dep::DepError::FonteRepoEmpty { ref nome }
6383                    if nome == "runtime"
6384            ),
6385            "got {err:?}"
6386        );
6387    }
6388
6389    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
6390
6391    #[test]
6392    fn validate_deps_rejects_duplicate_nome_in_deps() {
6393        // Fail-before-pass-after pin: two `:deps` entries naming the same
6394        // caixa carry two `:versao` / `:fonte` / feature triples that the
6395        // caixa-resolver's lacre pipeline collapses (the second silently
6396        // overwrites the first at `concrete_versao`-resolve time). The
6397        // gate surfaces the duplicate at validate-time, naming the
6398        // offending caixa + the list, before the resolver-side silent
6399        // drop. Mirrors the peer typed-graph duplicate gates
6400        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
6401        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6402        c.deps = vec![
6403            Dep::simple("caixa-teia", "^0.1"),
6404            Dep::simple("caixa-teia", "^0.2"),
6405        ];
6406        let err = c.validate_deps().unwrap_err();
6407        assert!(
6408            matches!(
6409                err,
6410                crate::dep::DepError::DuplicateNome { ref nome, list }
6411                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6412            ),
6413            "got {err:?}"
6414        );
6415    }
6416
6417    #[test]
6418    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
6419        // Parity pin: `:deps-dev` runs through the same per-list
6420        // duplicate check as `:deps` — neither axis is a second-class
6421        // citizen of the set-not-multiset discipline.
6422        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6423        c.deps_dev = vec![
6424            Dep::simple("tatara-check", "*"),
6425            Dep::simple("tatara-check", "^0.1"),
6426        ];
6427        let err = c.validate_deps().unwrap_err();
6428        assert!(
6429            matches!(
6430                err,
6431                crate::dep::DepError::DuplicateNome { ref nome, list }
6432                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
6433            ),
6434            "got {err:?}"
6435        );
6436    }
6437
6438    #[test]
6439    fn validate_deps_accepts_cross_list_same_nome() {
6440        // The Cargo `[dependencies]` + `[dev-dependencies]` override
6441        // convention is preserved: a name appearing in *both* lists is
6442        // valid (the dev-pin overrides at test/dev time). Only
6443        // within-list duplicates are structurally incoherent — pin the
6444        // permissive cross-list semantics so a future shortcut that
6445        // collapses the two seen-sets into one surfaces here as a test
6446        // failure.
6447        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6448        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
6449        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
6450        c.validate_deps().unwrap();
6451    }
6452
6453    #[test]
6454    fn validate_deps_accepts_distinct_nome_in_both_lists() {
6455        // Positive control: distinct names within each list pass — the
6456        // gate's identity element on the canonical authoring shape.
6457        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6458        c.deps = vec![
6459            Dep::simple("caixa-teia", "^0.1"),
6460            Dep::simple("pleme-mesh", "*"),
6461        ];
6462        c.deps_dev = vec![
6463            Dep::simple("tatara-check", "*"),
6464            Dep::simple("dev-shim", "^0.1"),
6465        ];
6466        c.validate_deps().unwrap();
6467    }
6468
6469    #[test]
6470    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
6471        // Diagnostic-precedence pin: a malformed `:versao` on the
6472        // duplicating entry surfaces its narrower `VersaoInvalid`
6473        // diagnostic first, before the cross-entry duplicate gate fires
6474        // — the canonical "per-entry shape before cross-entry uniqueness"
6475        // precedence every peer set-not-multiset gate establishes
6476        // (`*_invalid_fires_before_duplicate_check` pins on
6477        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
6478        // `validate_upgrade_from`).
6479        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6480        c.deps = vec![
6481            Dep::simple("caixa-teia", "^0.1"),
6482            Dep::simple("caixa-teia", "^bad-version"),
6483        ];
6484        let err = c.validate_deps().unwrap_err();
6485        assert!(
6486            matches!(
6487                err,
6488                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6489                    if nome == "caixa-teia" && versao == "^bad-version"
6490            ),
6491            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
6492        );
6493    }
6494
6495    #[test]
6496    fn validate_deps_duplicate_diagnostic_names_first_collision() {
6497        // First-collision determinism pin: with three entries naming the
6498        // same caixa, the first colliding pair surfaces — not the last.
6499        // Mirrors the peer first-collision posture on every
6500        // duplicate-target gate
6501        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
6502        // — the second entry is the first collision; this gate uses the
6503        // same shape: the second entry's `:nome` lands in the diagnostic
6504        // because `seen.insert(first.nome)` already populated the set).
6505        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6506        c.deps = vec![
6507            Dep::simple("caixa-teia", "^0.1"),
6508            Dep::simple("caixa-teia", "^0.2"),
6509            Dep::simple("caixa-teia", "^0.3"),
6510        ];
6511        let err = c.validate_deps().unwrap_err();
6512        // The diagnostic carries the offending caixa name; the
6513        // implementation surfaces on the *second* entry (the first
6514        // collision), so the test pins the `:nome` value.
6515        assert!(
6516            matches!(
6517                err,
6518                crate::dep::DepError::DuplicateNome { ref nome, list }
6519                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6520            ),
6521            "got {err:?}"
6522        );
6523    }
6524
6525    #[test]
6526    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
6527        // Cross-list precedence pin: when both lists carry duplicates,
6528        // the `:deps` diagnostic surfaces first — same author-mental-
6529        // model ordering the `validate_deps_runs_deps_before_deps_dev`
6530        // pin establishes for malformed `:versao` (runtime axis before
6531        // dev axis).
6532        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6533        c.deps = vec![
6534            Dep::simple("runtime-dep", "^0.1"),
6535            Dep::simple("runtime-dep", "^0.2"),
6536        ];
6537        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
6538        let err = c.validate_deps().unwrap_err();
6539        assert!(
6540            matches!(
6541                err,
6542                crate::dep::DepError::DuplicateNome { ref nome, list }
6543                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6544            ),
6545            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
6546        );
6547    }
6548
6549    #[test]
6550    fn validate_deps_empty_lists_pass_duplicate_gate() {
6551        // Empty-set identity pin: the bare template (zero deps, zero
6552        // deps_dev) passes the duplicate gate as the gate's identity
6553        // element. A future tighten that conflates "empty" with
6554        // "missing" would regress this baseline.
6555        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6556        c.validate_deps().unwrap();
6557    }
6558
6559    #[test]
6560    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
6561        // Diagnostic-shape pin: the `list:` field tags which list the
6562        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
6563        // `feira lint` run can route the author to the right block in
6564        // their caixa.lisp without re-deriving the list from context.
6565        // Same self-locating shape every peer per-axis diagnostic
6566        // already exposes.
6567        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6568        c.deps_dev = vec![
6569            Dep::simple("dev-thing", "*"),
6570            Dep::simple("dev-thing", "^0.1"),
6571        ];
6572        let err = c.validate_deps().unwrap_err();
6573        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
6574            panic!("expected DuplicateNome from :deps-dev walk");
6575        };
6576        assert_eq!(nome, "dev-thing");
6577        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
6578    }
6579
6580    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
6581
6582    #[test]
6583    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
6584        // Thread-through pin on `:deps`: the per-entry
6585        // `Dep::validate_caracteristicas` gate fires inside
6586        // `Caixa::validate_deps`'s linear walk, so a malformed feature
6587        // list on any `:deps` entry surfaces as a `DepError` from
6588        // `validate_deps` — the same reachability shape every per-entry
6589        // `Dep::validate` arm threads through. Without this pin a future
6590        // shortcut that skips the per-entry `Dep::validate` call on the
6591        // cross-entry-uniqueness path would mask the within-entry
6592        // `:caracteristicas` gates.
6593        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6594        c.deps = vec![Dep {
6595            nome: "caixa-teia".into(),
6596            versao: "^0.1".into(),
6597            fonte: None,
6598            opcional: false,
6599            caracteristicas: vec!["http".into(), "http".into()],
6600        }];
6601        let err = c.validate_deps().unwrap_err();
6602        let crate::dep::DepError::CaracteristicaDuplicate {
6603            nome,
6604            caracteristica,
6605        } = err
6606        else {
6607            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
6608        };
6609        assert_eq!(nome, "caixa-teia");
6610        assert_eq!(caracteristica, "http");
6611    }
6612
6613    #[test]
6614    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
6615        // Peer thread-through pin on `:deps-dev`: same reachability as
6616        // the `:deps` arm above, on the dev-only authoring axis. Pins
6617        // that the `validate_deps` walk visits both lists' per-entry
6618        // gates uniformly. The empty-feature arm carries here so both
6619        // new `:caracteristicas` arms are surfaced via at least one
6620        // `validate_deps` thread-through.
6621        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6622        c.deps_dev = vec![Dep {
6623            nome: "caixa-teia".into(),
6624            versao: "^0.1".into(),
6625            fonte: None,
6626            opcional: false,
6627            caracteristicas: vec![String::new()],
6628        }];
6629        let err = c.validate_deps().unwrap_err();
6630        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
6631            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
6632        };
6633        assert_eq!(nome, "caixa-teia");
6634    }
6635
6636    #[test]
6637    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
6638        // Thread-through pin on `:deps`: the per-entry
6639        // `Dep::validate_caracteristicas` value-shape gate (lifted via
6640        // `crate::render::is_cargo_feature_name`) fires inside
6641        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
6642        // a structurally invalid feature name on any `:deps` entry
6643        // surfaces as `DepError::CaracteristicaInvalid` from
6644        // `validate_deps` — the same reachability shape every per-entry
6645        // `Dep::validate` arm threads through. Without this pin a
6646        // future shortcut that skips the per-entry `Dep::validate` call
6647        // on the cross-entry-uniqueness path would mask the within-
6648        // entry `:caracteristicas` value-shape gate.
6649        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6650        c.deps = vec![Dep {
6651            nome: "caixa-teia".into(),
6652            versao: "^0.1".into(),
6653            fonte: None,
6654            opcional: false,
6655            caracteristicas: vec!["+http".into()],
6656        }];
6657        let err = c.validate_deps().unwrap_err();
6658        let crate::dep::DepError::CaracteristicaInvalid {
6659            nome,
6660            caracteristica,
6661            ..
6662        } = err
6663        else {
6664            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
6665        };
6666        assert_eq!(nome, "caixa-teia");
6667        assert_eq!(caracteristica, "+http");
6668    }
6669
6670    #[test]
6671    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
6672        // Peer thread-through pin on `:deps-dev`: same reachability as
6673        // the `:deps` arm above, on the dev-only authoring axis. The
6674        // `http/json` shape carries here so the segment-separator
6675        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
6676        // confusion footgun) is surfaced via the cross-entry walk too —
6677        // pinning that the `:deps-dev` list visits the same per-entry
6678        // value-shape gate as the `:deps` list.
6679        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6680        c.deps_dev = vec![Dep {
6681            nome: "caixa-teia".into(),
6682            versao: "^0.1".into(),
6683            fonte: None,
6684            opcional: false,
6685            caracteristicas: vec!["http/json".into()],
6686        }];
6687        let err = c.validate_deps().unwrap_err();
6688        let crate::dep::DepError::CaracteristicaInvalid {
6689            nome,
6690            caracteristica,
6691            ..
6692        } = err
6693        else {
6694            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
6695        };
6696        assert_eq!(nome, "caixa-teia");
6697        assert_eq!(caracteristica, "http/json");
6698    }
6699
6700    #[test]
6701    fn to_lisp_preserves_deps() {
6702        let src = r#"
6703(defcaixa
6704  :nome "x"
6705  :versao "0.1.0"
6706  :kind Biblioteca
6707  :deps ((:nome "a" :versao "^0.1")
6708         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
6709"#;
6710        let c1 = Caixa::from_lisp(src).unwrap();
6711        let emitted = c1.to_lisp();
6712        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
6713        assert_eq!(c1.deps, c2.deps);
6714    }
6715
6716    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
6717
6718    fn caixa_with_nome(nome: &str) -> Caixa {
6719        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
6720        c.nome = nome.to_string();
6721        c
6722    }
6723
6724    #[test]
6725    fn validate_nome_accepts_canonical_template() {
6726        // Positive control: the bare `feira init`-style template's
6727        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
6728        // not regress this baseline shape. A future tightening of the
6729        // accepted set surfaces here as a test failure first.
6730        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6731        c.validate_nome().unwrap();
6732    }
6733
6734    #[test]
6735    fn validate_nome_accepts_canonical_forms() {
6736        // Positive-set sweep: each realistic caixa-name shape the K8s
6737        // apiserver accepts as a `metadata.name` label must pass —
6738        // single-word, hyphen-joined, version-suffixed, single-char,
6739        // two-char, digit-start (DNS-1123 allows this; the stricter
6740        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
6741        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
6742        // the peer member-name axis.
6743        for nome in [
6744            "checkout",
6745            "cart-v2",
6746            "a",
6747            "db",
6748            "3rd-party-shim",
6749            "payment-retry",
6750            "0",
6751        ] {
6752            caixa_with_nome(nome)
6753                .validate_nome()
6754                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
6755        }
6756    }
6757
6758    #[test]
6759    fn validate_nome_rejects_empty() {
6760        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
6761        // an empty `:nome` (the derive macro stores the raw String);
6762        // the gate's empty arm names the offending axis with a narrower
6763        // diagnostic than the `NomeInvalid` parse arm would emit.
6764        let c = caixa_with_nome("");
6765        let err = c.validate_nome().unwrap_err();
6766        assert_eq!(err, ManifestError::NomeEmpty);
6767    }
6768
6769    #[test]
6770    fn validate_nome_rejects_uppercase() {
6771        // The canonical "I copied the TitleCase display name verbatim"
6772        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
6773        // admission on every derived artifact (Helm chart, ComputeUnit,
6774        // CNP, HTTPRoute, label values); the gate moves the diagnostic
6775        // to the source `caixa.lisp` and the reason suggests the
6776        // lowercased fix verbatim.
6777        let c = caixa_with_nome("MyApp");
6778        let err = c.validate_nome().unwrap_err();
6779        let ManifestError::NomeInvalid { nome, reason } = err else {
6780            panic!("expected NomeInvalid for uppercase :nome");
6781        };
6782        assert_eq!(nome, "MyApp");
6783        assert!(
6784            reason.contains("uppercase") && reason.contains("myapp"),
6785            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
6786        );
6787    }
6788
6789    #[test]
6790    fn validate_nome_rejects_underscore() {
6791        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
6792        // `_`; the apiserver rejects on admission across every derived
6793        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
6794        // and `:children :caixa` (31bfa43).
6795        let c = caixa_with_nome("my_app");
6796        let err = c.validate_nome().unwrap_err();
6797        assert!(
6798            matches!(
6799                err,
6800                ManifestError::NomeInvalid { ref nome, ref reason }
6801                    if nome == "my_app" && reason.contains('_')
6802            ),
6803            "got {err:?}"
6804        );
6805    }
6806
6807    #[test]
6808    fn validate_nome_rejects_dot() {
6809        // A `:nome` is a single DNS-1123 label, not a subdomain. The
6810        // "I want to namespace with `.`" footgun the gate redirects to
6811        // `-` via the shared predicate's reason wording.
6812        let c = caixa_with_nome("team.app");
6813        let err = c.validate_nome().unwrap_err();
6814        assert!(
6815            matches!(
6816                err,
6817                ManifestError::NomeInvalid { ref nome, ref reason }
6818                    if nome == "team.app" && reason.contains('.')
6819            ),
6820            "got {err:?}"
6821        );
6822    }
6823
6824    #[test]
6825    fn validate_nome_rejects_leading_hyphen() {
6826        // DNS-1123 boundary rule: the label must start with an ASCII
6827        // alphanumeric. Pin the leading-`-` arm explicitly.
6828        let c = caixa_with_nome("-app");
6829        let err = c.validate_nome().unwrap_err();
6830        assert!(
6831            matches!(
6832                err,
6833                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
6834            ),
6835            "got {err:?}"
6836        );
6837    }
6838
6839    #[test]
6840    fn validate_nome_rejects_trailing_hyphen() {
6841        // Symmetric arm of the boundary rule, pinned separately so a
6842        // future relaxation that only checks the leading position
6843        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
6844        // and `_with_trailing_hyphen` on the supervisor / aplicacao
6845        // axes.
6846        let c = caixa_with_nome("app-");
6847        let err = c.validate_nome().unwrap_err();
6848        assert!(
6849            matches!(
6850                err,
6851                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
6852            ),
6853            "got {err:?}"
6854        );
6855    }
6856
6857    #[test]
6858    fn validate_nome_rejects_unicode() {
6859        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
6860        // bytes are rejected by the K8s apiserver on every name axis.
6861        let c = caixa_with_nome("café");
6862        let err = c.validate_nome().unwrap_err();
6863        assert!(
6864            matches!(
6865                err,
6866                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
6867            ),
6868            "got {err:?}"
6869        );
6870    }
6871
6872    #[test]
6873    fn validate_nome_rejects_whitespace() {
6874        // The paste-from-sketch / paste-from-spec footgun. Internal
6875        // whitespace is rejected by every K8s name axis.
6876        let c = caixa_with_nome("my app");
6877        let err = c.validate_nome().unwrap_err();
6878        assert!(
6879            matches!(
6880                err,
6881                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
6882            ),
6883            "got {err:?}"
6884        );
6885    }
6886
6887    #[test]
6888    fn validate_nome_rejects_too_long() {
6889        // 64-byte boundary pin: the K8s apiserver rejects any
6890        // `metadata.name` over 63 bytes at admission; the diagnostic
6891        // names both the 63-byte cap and the actual length so the
6892        // author can shorten in one edit. Mirrors `_too_long` on the
6893        // peer member-/cluster-/child-name axes.
6894        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
6895        let c = caixa_with_nome(&over);
6896        let err = c.validate_nome().unwrap_err();
6897        let ManifestError::NomeInvalid { nome, reason } = err else {
6898            panic!("expected NomeInvalid for over-cap :nome");
6899        };
6900        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
6901        assert!(
6902            reason.contains("63") && reason.contains("64"),
6903            "diagnostic must name the cap + actual length, got {reason:?}"
6904        );
6905    }
6906
6907    #[test]
6908    fn nome_max_length_validates() {
6909        // The 63-byte cap exactly — the boundary-accepting case pinned
6910        // alongside `validate_nome_rejects_too_long` so a future cap
6911        // shift surfaces both arms simultaneously. Mirrors
6912        // `membro_caixa_max_length_validates`,
6913        // `placement_cluster_max_length_validates`,
6914        // `child_caixa_max_length_validates`.
6915        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
6916        caixa_with_nome(&at_cap).validate_nome().unwrap();
6917    }
6918
6919    #[test]
6920    fn nome_empty_takes_precedence_over_invalid() {
6921        // Order pin: the empty arm fires before the predicate is
6922        // consulted. Empty < invalid in self-locating-ness — the
6923        // narrower `NomeEmpty` diagnostic doesn't carry a useless
6924        // `nome: ""` reference into the parser-shaped reason. Mirrors
6925        // `membro_caixa_empty_takes_precedence_over_invalid` on the
6926        // peer axis (3f9d7a0).
6927        let c = caixa_with_nome("");
6928        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
6929    }
6930
6931    #[test]
6932    fn nome_invalid_diagnostic_carries_offending_nome() {
6933        // Diagnostic-shape pin: the error names the offending `:nome`
6934        // verbatim with a non-empty parser-shaped reason, so a `feira
6935        // lint` run can render the diagnostic without re-parsing.
6936        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
6937        let c = caixa_with_nome("MyApp");
6938        let err = c.validate_nome().unwrap_err();
6939        let ManifestError::NomeInvalid { nome, reason } = err else {
6940            panic!("expected NomeInvalid variant");
6941        };
6942        assert_eq!(nome, "MyApp");
6943        assert!(
6944            !reason.is_empty(),
6945            "NomeInvalid `reason` must carry the predicate's wording verbatim"
6946        );
6947    }
6948
6949    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
6950    //
6951    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
6952    // via DNS-1123; this second-axis gate caps the joint
6953    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
6954    // canonical [`crate::lareira_chart_name`] helper's doc comment
6955    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
6956    // "the M4 admission webhook will pin the joint-length invariant
6957    // when it lands". These tests pin it at the manifest-validate
6958    // layer instead, fail-before-pass-after on the 56-byte boundary.
6959
6960    #[test]
6961    fn validate_nome_chart_name_budget_accepts_canonical_template() {
6962        // Positive control: the bare `feira init`-style template's
6963        // `:nome` ("demo") sits far below the cap; the gate must not
6964        // regress this baseline. Same shape every peer
6965        // value-shape-gate baseline pin uses.
6966        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6967        c.validate_nome_chart_name_budget().unwrap();
6968    }
6969
6970    #[test]
6971    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
6972        // Positive-set sweep across the canonical author surface every
6973        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
6974        // `worker`, the `checkout-aplicacao` example members, the
6975        // `akeyless-attest` caixa-tatara fixture). Every value sits
6976        // far below the 55-byte per-`:nome` budget. Same shape every
6977        // peer per-axis baseline pin uses.
6978        for nome in [
6979            "hello-rio",
6980            "cart",
6981            "checkout",
6982            "worker",
6983            "akeyless-attest",
6984            "demo",
6985            "a",
6986        ] {
6987            caixa_with_nome(nome)
6988                .validate_nome_chart_name_budget()
6989                .unwrap_or_else(|e| {
6990                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
6991                });
6992        }
6993    }
6994
6995    #[test]
6996    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
6997        // Boundary-accepting case at the 55-byte per-`:nome` budget —
6998        // the joint chart name is exactly 63 bytes, the DNS-1123 label
6999        // cap. Pinned alongside the rejecting-arm test so a future cap
7000        // shift surfaces both arms simultaneously. Mirrors
7001        // `nome_max_length_validates` on the peer bare-`:nome` axis.
7002        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
7003        caixa_with_nome(&at_cap)
7004            .validate_nome_chart_name_budget()
7005            .unwrap();
7006    }
7007
7008    #[test]
7009    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
7010        // Fail-before-pass-after pin on the 56-byte boundary: the
7011        // smallest `:nome` length that overflows the joint chart-name
7012        // cap. The inner [`is_dns_1123_label`] gate
7013        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
7014        // this gate it silently passed the manifest-validate cascade
7015        // and surfaced as a `helm lint` / apiserver rejection on the
7016        // rendered chart name far from the source `caixa.lisp`, with
7017        // no field naming the overflow. With this gate the diagnostic
7018        // names the offending `:nome` verbatim alongside the rendered
7019        // chart name and the budget, so the author can shorten in one
7020        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
7021        // bare-`:nome` axis.
7022        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7023        let c = caixa_with_nome(&over);
7024        let err = c.validate_nome_chart_name_budget().unwrap_err();
7025        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7026            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
7027        };
7028        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7029        assert_eq!(nome, over);
7030        assert!(
7031            reason.contains("63") && reason.contains("64") && reason.contains("55"),
7032            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
7033             and the per-`:nome` budget (55), got {reason:?}"
7034        );
7035    }
7036
7037    #[test]
7038    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
7039        // The 63-byte `:nome` boundary — passes the bare-`:nome`
7040        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
7041        // joint chart name that overflows the DNS-1123 label cap
7042        // structurally. The most stringent fail-before-pass-after
7043        // surface: every `:nome` in the 56..=63-byte range passed the
7044        // prior cascade and broke at admission.
7045        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
7046        let c = caixa_with_nome(&bare_max);
7047        // The bare-`:nome` gate accepts the 63-byte length.
7048        c.validate_nome().unwrap();
7049        // The new joint-length gate rejects it.
7050        let err = c.validate_nome_chart_name_budget().unwrap_err();
7051        assert!(
7052            matches!(
7053                err,
7054                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
7055                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
7056            ),
7057            "got {err:?}"
7058        );
7059    }
7060
7061    #[test]
7062    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
7063        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
7064        // name appears verbatim in the diagnostic so the author sees
7065        // exactly the string the apiserver / `helm lint` would have
7066        // rejected — no re-derivation required to grep the source.
7067        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
7068        // on the bare-`:nome` axis.
7069        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
7070        let c = caixa_with_nome(&over);
7071        let err = c.validate_nome_chart_name_budget().unwrap_err();
7072        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7073            panic!("expected NomeChartNameBudgetExceeded variant");
7074        };
7075        assert_eq!(nome, over);
7076        let expected_chart = crate::lareira_chart_name(&over);
7077        assert!(
7078            reason.contains(&expected_chart),
7079            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
7080             got {reason:?}"
7081        );
7082        assert!(
7083            reason.contains("lareira-"),
7084            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
7085        );
7086    }
7087
7088    #[test]
7089    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
7090        // Order pin on the layout cascade: the narrower
7091        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
7092        // joint-length budget. A structurally-malformed `:nome` (here:
7093        // uppercase) surfaces its specific shape error rather than
7094        // the chart-name-budget error, even when the joint length
7095        // would also overflow — the narrower diagnostic is more
7096        // self-locating. Mirrors the cascade-precedence pins peer
7097        // gates already use (e.g. `EntradaParaEmpty` before
7098        // `EntradaParaInvalid`).
7099        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7100        let c = caixa_with_nome(&over);
7101        // The bare-shape gate fires first.
7102        let err = c.validate_nome().unwrap_err();
7103        assert!(
7104            matches!(err, ManifestError::NomeInvalid { .. }),
7105            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
7106        );
7107        // And the layout verify cascade surfaces that diagnostic, not
7108        // the budget arm. Inject a path-exists oracle so the cascade
7109        // gets past the manifest-presence check and into the
7110        // value-shape gates.
7111        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7112        let err = crate::LayoutInvariants::verify(
7113            &layout,
7114            &c,
7115            std::path::Path::new("/tmp/caixa-test-fake-root"),
7116        )
7117        .unwrap_err();
7118        let issue = err.to_string();
7119        assert!(
7120            issue.contains("DNS-1123") || issue.contains("uppercase"),
7121            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
7122             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
7123        );
7124    }
7125
7126    #[test]
7127    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
7128        // Cross-axis envelope pin: the layout cascade wraps both
7129        // bare-`:nome` and joint-length-`:nome` failures through the
7130        // same [`LayoutError::NomeViolation`] envelope, since both
7131        // arms are on the `:nome` axis. The user's diagnostic stays
7132        // self-locating ("which axis"), and a future consumer that
7133        // dispatches on the layout-error variant (e.g. a `feira lint`
7134        // exit-code mapping) sees a single per-axis envelope. The
7135        // wrapped `issue:` carries the full inner diagnostic.
7136        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7137        let c = caixa_with_nome(&over);
7138        // The bare-shape gate accepts.
7139        c.validate_nome().unwrap();
7140        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7141        let err = crate::LayoutInvariants::verify(
7142            &layout,
7143            &c,
7144            std::path::Path::new("/tmp/caixa-test-fake-root"),
7145        )
7146        .unwrap_err();
7147        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
7148            panic!("expected LayoutError::NomeViolation, got {err:?}");
7149        };
7150        assert_eq!(caixa, over);
7151        assert!(
7152            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
7153            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
7154        );
7155    }
7156
7157    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
7158
7159    fn caixa_with_versao(versao: &str) -> Caixa {
7160        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7161        c.versao = versao.to_string();
7162        c
7163    }
7164
7165    #[test]
7166    fn validate_versao_accepts_canonical_template() {
7167        // Positive control: the bare `feira init`-style template's
7168        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
7169        // must not regress this baseline shape. A future tightening of
7170        // the accepted set surfaces here as a test failure first.
7171        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7172        c.validate_versao().unwrap();
7173    }
7174
7175    #[test]
7176    fn validate_versao_accepts_canonical_forms() {
7177        // Positive-set sweep: each realistic SemVer-2 shape the
7178        // substrate's downstream consumers accept must pass — bare
7179        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
7180        // build metadata (`+build.42`), the combined form, and the
7181        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
7182        // the peer `:nome` axis (6c992f8).
7183        for versao in [
7184            "0.1.0",
7185            "0.0.0",
7186            "1.0.0",
7187            "0.2.0-rc.1",
7188            "1.0.0-alpha.0",
7189            "1.0.0+build.42",
7190            "1.0.0-rc.1+build.42",
7191            "10.20.30",
7192        ] {
7193            caixa_with_versao(versao)
7194                .validate_versao()
7195                .unwrap_or_else(|e| {
7196                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
7197                });
7198        }
7199    }
7200
7201    #[test]
7202    fn validate_versao_rejects_empty() {
7203        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
7204        // an empty `:versao` (the derive macro stores the raw String);
7205        // the gate's empty arm names the offending axis with a narrower
7206        // diagnostic than the `VersaoInvalid` parse arm would emit.
7207        // Mirrors `validate_nome_rejects_empty` (6c992f8).
7208        let c = caixa_with_versao("");
7209        let err = c.validate_versao().unwrap_err();
7210        assert_eq!(err, ManifestError::VersaoEmpty);
7211    }
7212
7213    #[test]
7214    fn validate_versao_rejects_git_tag_shape() {
7215        // The canonical "I copied the git tag verbatim" footgun —
7216        // `feira publish` *emits* `v<versao>` git tags, so a leaked
7217        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
7218        // shift every downstream consumer's version axis. `semver`
7219        // rejects the leading `v` at parse time; the gate moves the
7220        // diagnostic to the source `caixa.lisp`.
7221        let c = caixa_with_versao("v0.1.0");
7222        let err = c.validate_versao().unwrap_err();
7223        let ManifestError::VersaoInvalid { versao, reason } = err else {
7224            panic!("expected VersaoInvalid for git-tag-shape :versao");
7225        };
7226        assert_eq!(versao, "v0.1.0");
7227        assert!(
7228            !reason.is_empty(),
7229            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
7230        );
7231    }
7232
7233    #[test]
7234    fn validate_versao_rejects_missing_patch() {
7235        // The canonical "I shortened it" footgun — SemVer-2 requires
7236        // three parts. Cargo's `version =` field accepts the shortened
7237        // form as a requirement, conflating the two leaks across the
7238        // typed `:deps :versao` vs top-level `:versao` axes; the gate
7239        // pins the top-level axis to the strict three-part shape.
7240        let c = caixa_with_versao("0.1");
7241        let err = c.validate_versao().unwrap_err();
7242        assert!(
7243            matches!(
7244                err,
7245                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
7246            ),
7247            "got {err:?}"
7248        );
7249    }
7250
7251    #[test]
7252    fn validate_versao_rejects_requirement_shape() {
7253        // The canonical "I leaked a requirement into a version" footgun —
7254        // the typed `:deps :versao` / `:membros :versao` axes accept
7255        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
7256        // concrete `Version`. Without this gate the two typed surfaces
7257        // would silently overlap, and a top-level `^0.1` would surface
7258        // at `helm install` time as a Chart.yaml version rejection far
7259        // from the source `caixa.lisp`.
7260        let c = caixa_with_versao("^0.1");
7261        let err = c.validate_versao().unwrap_err();
7262        assert!(
7263            matches!(
7264                err,
7265                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
7266            ),
7267            "got {err:?}"
7268        );
7269    }
7270
7271    #[test]
7272    fn validate_versao_rejects_docker_tag_shape() {
7273        // The "I confused it with a docker tag" footgun — `latest`,
7274        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
7275        // SemVer rejects at parse time; the gate moves the diagnostic
7276        // to the source `caixa.lisp`.
7277        for bad in ["latest", "main", "stable"] {
7278            let c = caixa_with_versao(bad);
7279            let err = c.validate_versao().unwrap_err();
7280            assert!(
7281                matches!(
7282                    err,
7283                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
7284                ),
7285                "got {err:?} for {bad:?}"
7286            );
7287        }
7288    }
7289
7290    #[test]
7291    fn validate_versao_rejects_four_part_form() {
7292        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
7293        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
7294        // semver crate rejects the extra `.0` at parse time.
7295        let c = caixa_with_versao("0.1.0.0");
7296        let err = c.validate_versao().unwrap_err();
7297        assert!(
7298            matches!(
7299                err,
7300                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
7301            ),
7302            "got {err:?}"
7303        );
7304    }
7305
7306    #[test]
7307    fn versao_empty_takes_precedence_over_invalid() {
7308        // Order pin: the empty arm fires before the parser is consulted.
7309        // Empty < invalid in self-locating-ness — the narrower
7310        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
7311        // reference into the parser-shaped reason. Mirrors
7312        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
7313        // peer axis.
7314        let c = caixa_with_versao("");
7315        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
7316    }
7317
7318    #[test]
7319    fn versao_invalid_diagnostic_carries_offending_versao() {
7320        // Diagnostic-shape pin: the error names the offending `:versao`
7321        // verbatim with a non-empty parser-shaped reason, so a `feira
7322        // lint` run can render the diagnostic without re-parsing.
7323        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
7324        let c = caixa_with_versao("v0.1.0");
7325        let err = c.validate_versao().unwrap_err();
7326        let ManifestError::VersaoInvalid { versao, reason } = err else {
7327            panic!("expected VersaoInvalid variant");
7328        };
7329        assert_eq!(versao, "v0.1.0");
7330        assert!(
7331            !reason.is_empty(),
7332            "VersaoInvalid `reason` must carry the parser's wording verbatim"
7333        );
7334    }
7335
7336    #[test]
7337    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
7338        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
7339        // for `:upgrade-from :from` must also pass `validate_versao` —
7340        // the two `:versao`-typed surfaces (top-level `:versao`,
7341        // `:upgrade-from :from`) consume the *same* `semver::Version`
7342        // parser, so they must agree on the accepted set. Without this
7343        // pin, a future tightening of one axis could silently diverge
7344        // from the other. Mirrors the `:versao` requirement-axis
7345        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
7346        // commits established.
7347        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
7348            // From the canonical UpgradeFromEntry round-trip fixture
7349            // (`upgrade::tests::round_trip_load_module` peers).
7350            let entry = crate::UpgradeFromEntry {
7351                from: versao.to_string(),
7352                instructions: Vec::new(),
7353            };
7354            entry
7355                .validate()
7356                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
7357            caixa_with_versao(versao)
7358                .validate_versao()
7359                .unwrap_or_else(|e| {
7360                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
7361                });
7362        }
7363    }
7364
7365    // ── Caixa::validate_restart_window — supervisor restart-window
7366    //    folds through the shared `supervisor::duration_codec` ────────
7367
7368    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
7369        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
7370        c.kind = CaixaKind::Supervisor;
7371        c.restart_window = window.map(str::to_string);
7372        c
7373    }
7374
7375    #[test]
7376    fn validate_restart_window_accepts_none() {
7377        // The canonical "omit the slot to express no reset" shape — a
7378        // `None` raw string is the absence of the typed
7379        // `:restart-window` slot, which is exactly the SupervisorSpec
7380        // "never reset" semantics. The gate must be a no-op here; a
7381        // future tightening that rejected `None` would force every
7382        // supervisor caixa to authoring-time pin a window even when
7383        // the OTP semantics call for none.
7384        caixa_with_restart_window(None)
7385            .validate_restart_window()
7386            .unwrap();
7387    }
7388
7389    #[test]
7390    fn validate_restart_window_accepts_canonical_forms() {
7391        // Positive-set sweep across the canonical authoring units the
7392        // shared `supervisor::duration_codec::parse` accepts —
7393        // matches the codec-side `parse_accepts_integer_canonical_units`
7394        // pin in supervisor::tests so a future codec-side tightening
7395        // surfaces simultaneously on both axes.
7396        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
7397            caixa_with_restart_window(Some(window))
7398                .validate_restart_window()
7399                .unwrap_or_else(|e| {
7400                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
7401                });
7402        }
7403    }
7404
7405    #[test]
7406    fn validate_restart_window_rejects_fractional_seconds() {
7407        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
7408        // as f64 to 1.5 → renders back as `"1500ms"` on first
7409        // serialize). Prior to the fold + this gate, the inline
7410        // `parse_window_inline` accepted f64 magnitudes and silently
7411        // produced a `Duration::from_secs_f64(1.5)`, divergent from
7412        // the shared codec's integer-magnitude discipline on the
7413        // serde-routed siblings. The gate now surfaces a self-locating
7414        // diagnostic at the manifest layer.
7415        let err = caixa_with_restart_window(Some("1.5s"))
7416            .validate_restart_window()
7417            .unwrap_err();
7418        let ManifestError::RestartWindowMalformed {
7419            restart_window,
7420            reason,
7421        } = err
7422        else {
7423            panic!("expected RestartWindowMalformed for fractional seconds");
7424        };
7425        assert_eq!(restart_window, "1.5s");
7426        assert!(
7427            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
7428            "diagnostic must carry shared-codec wording, got {reason:?}"
7429        );
7430    }
7431
7432    #[test]
7433    fn validate_restart_window_rejects_decimal_shaped_integer() {
7434        // The `"1.0s"` class — numerically `1s` exactly, but the
7435        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
7436        // gets the same canonical-form diagnostic.
7437        let err = caixa_with_restart_window(Some("1.0s"))
7438            .validate_restart_window()
7439            .unwrap_err();
7440        assert!(
7441            matches!(
7442                err,
7443                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7444                    if restart_window == "1.0s"
7445            ),
7446            "got {err:?}"
7447        );
7448    }
7449
7450    #[test]
7451    fn validate_restart_window_rejects_half_unit_minute() {
7452        // `"0.5m"` is the unit-fraction footgun — author writes a
7453        // human-readable half-minute, the prior inline parser silently
7454        // produced `Duration::from_secs_f64(30.0)` and serde
7455        // re-emitted as `"30s"`, rewriting author intent. The gate
7456        // closes the loop at the manifest layer.
7457        let err = caixa_with_restart_window(Some("0.5m"))
7458            .validate_restart_window()
7459            .unwrap_err();
7460        let ManifestError::RestartWindowMalformed {
7461            restart_window,
7462            reason,
7463        } = err
7464        else {
7465            panic!("expected RestartWindowMalformed");
7466        };
7467        assert_eq!(restart_window, "0.5m");
7468        assert!(
7469            reason.contains("\"30s\""),
7470            "diagnostic must point at the canonical-form remediation, got {reason:?}"
7471        );
7472    }
7473
7474    #[test]
7475    fn validate_restart_window_rejects_leading_sign() {
7476        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
7477        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
7478        // and was caught by the `num < 0.0` arm which silently
7479        // returned `None`, dropping the author-supplied window). The
7480        // shared codec's digit-only gate rejects both with a unified
7481        // canonical-form diagnostic; the manifest-layer wrapper names
7482        // the offending value.
7483        for bad in ["+30s", "-30s"] {
7484            let err = caixa_with_restart_window(Some(bad))
7485                .validate_restart_window()
7486                .unwrap_err();
7487            assert!(
7488                matches!(
7489                    err,
7490                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
7491                        if restart_window == bad
7492                ),
7493                "got {err:?} for {bad:?}"
7494            );
7495        }
7496    }
7497
7498    #[test]
7499    fn validate_restart_window_rejects_unknown_unit() {
7500        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
7501        // unit dispatch surfaces an `unknown duration unit` reason;
7502        // the manifest-layer wrapper names the offending value.
7503        let err = caixa_with_restart_window(Some("30x"))
7504            .validate_restart_window()
7505            .unwrap_err();
7506        let ManifestError::RestartWindowMalformed {
7507            restart_window,
7508            reason,
7509        } = err
7510        else {
7511            panic!("expected RestartWindowMalformed for unknown unit");
7512        };
7513        assert_eq!(restart_window, "30x");
7514        assert!(
7515            reason.contains("unknown duration unit"),
7516            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
7517        );
7518    }
7519
7520    #[test]
7521    fn validate_restart_window_rejects_garbage() {
7522        // Pure non-numeric magnitude (`"abc"`) falls through to the
7523        // shared codec's narrower `"bad duration magnitude"` arm. Same
7524        // diagnostic shape as the codec-side
7525        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
7526        let err = caixa_with_restart_window(Some("abc"))
7527            .validate_restart_window()
7528            .unwrap_err();
7529        let ManifestError::RestartWindowMalformed {
7530            restart_window,
7531            reason,
7532        } = err
7533        else {
7534            panic!("expected RestartWindowMalformed for garbage");
7535        };
7536        assert_eq!(restart_window, "abc");
7537        assert!(
7538            reason.contains("bad duration magnitude"),
7539            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
7540        );
7541    }
7542
7543    #[test]
7544    fn validate_restart_window_rejects_empty_string() {
7545        // The empty-after-trim edge case — distinct from the `None`
7546        // canonical "omit the slot" shape. The shared codec's
7547        // digit-only gate refuses an empty magnitude; the manifest
7548        // layer names the offending `""` so the author can grep for
7549        // the literal empty value in their `caixa.lisp` and either
7550        // remove the slot (the canonical "no reset" shape) or pin a
7551        // positive duration.
7552        let err = caixa_with_restart_window(Some(""))
7553            .validate_restart_window()
7554            .unwrap_err();
7555        assert!(
7556            matches!(
7557                err,
7558                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7559                    if restart_window.is_empty()
7560            ),
7561            "got {err:?}"
7562        );
7563    }
7564
7565    #[test]
7566    fn validate_restart_window_diagnostic_carries_offending_value() {
7567        // Diagnostic-shape pin (peer with
7568        // `nome_invalid_diagnostic_carries_offending_nome` /
7569        // `versao_invalid_diagnostic_carries_offending_versao`): the
7570        // error names the offending raw `:restart-window` verbatim
7571        // with a non-empty shared-codec-shaped reason, so a `feira
7572        // lint` run can render the diagnostic without re-parsing.
7573        let err = caixa_with_restart_window(Some("1.5s"))
7574            .validate_restart_window()
7575            .unwrap_err();
7576        let ManifestError::RestartWindowMalformed {
7577            restart_window,
7578            reason,
7579        } = err
7580        else {
7581            panic!("expected RestartWindowMalformed variant");
7582        };
7583        assert_eq!(restart_window, "1.5s");
7584        assert!(
7585            !reason.is_empty(),
7586            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
7587        );
7588    }
7589
7590    #[test]
7591    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
7592        // Behavioral parity pin after the fold (`parse_window_inline`
7593        // deletion): the canonical `"60s"` still produces
7594        // `Duration::from_secs(60)` on the typed view — the fold is
7595        // semantically equivalent to the prior inline parser on the
7596        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
7597        // pin, narrowed to the parser-side contract.
7598        let c = caixa_with_restart_window(Some("60s"));
7599        let view = c.supervisor_view().expect("Supervisor kind has a view");
7600        assert_eq!(
7601            view.restart_window,
7602            Some(std::time::Duration::from_secs(60))
7603        );
7604    }
7605
7606    #[test]
7607    fn supervisor_view_soft_swallows_what_validate_rejects() {
7608        // Parity pin between the view-construction path and the
7609        // manifest-level validator: the same `"1.5s"` that surfaces
7610        // `RestartWindowMalformed` at `validate_restart_window` time
7611        // becomes `restart_window: None` on the typed view (the fold
7612        // preserves the existing best-effort shape of `supervisor_view`).
7613        // The contract is: a layout-verifier / `feira lint` flow that
7614        // cares about the malformed-window axis MUST consult
7615        // `validate_restart_window` — relying solely on the view's
7616        // `None` swallows the diagnostic silently. This pin makes the
7617        // expectation a typed invariant.
7618        let c = caixa_with_restart_window(Some("1.5s"));
7619        let view = c.supervisor_view().expect("Supervisor kind has a view");
7620        assert_eq!(
7621            view.restart_window, None,
7622            "view-construction path soft-swallows the parse error to None"
7623        );
7624        // And the manifest-level validator does NOT soft-swallow:
7625        assert!(
7626            matches!(
7627                c.validate_restart_window().unwrap_err(),
7628                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7629                    if restart_window == "1.5s"
7630            ),
7631            "validator must surface the offending value",
7632        );
7633    }
7634
7635    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
7636
7637    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
7638        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7639        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
7640        c.exe = exe.into_iter().map(String::from).collect();
7641        c.servicos = servicos.into_iter().map(String::from).collect();
7642        c
7643    }
7644
7645    #[test]
7646    fn validate_code_paths_accepts_canonical_template() {
7647        // The bare `Caixa::template` shape is the gate's identity element
7648        // on the canonical authoring shape — `:bibliotecas
7649        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
7650        // that the gate is non-disruptive against every existing caixa.
7651        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7652        c.validate_code_paths().unwrap();
7653    }
7654
7655    #[test]
7656    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
7657        // Positive control sweep: a canonical-shaped path on every slot
7658        // passes. Mirrors the peer
7659        // `behavior::validate_every_slot_relative_is_ok` pin.
7660        let c = caixa_with_code_paths(
7661            vec!["lib/demo.lisp", "lib/helpers.lisp"],
7662            vec!["exe/demo", "exe/tool"],
7663            vec!["servicos/demo.computeunit.yaml"],
7664        );
7665        c.validate_code_paths().unwrap();
7666    }
7667
7668    #[test]
7669    fn validate_code_paths_accepts_all_empty_lists() {
7670        // The empty-list identity element: every Caixa with no declared
7671        // code paths trivially passes (Supervisor / Aplicacao kinds rely
7672        // on this — the OwnCode gate already rejected them before the
7673        // path-shape gate runs in the layout, but the validator itself
7674        // must accept the empty shape).
7675        let c = caixa_with_code_paths(vec![], vec![], vec![]);
7676        c.validate_code_paths().unwrap();
7677    }
7678
7679    #[test]
7680    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
7681        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
7682        let err = c.validate_code_paths().unwrap_err();
7683        assert!(
7684            matches!(
7685                err,
7686                ManifestError::CodePathEmpty {
7687                    slot: ":bibliotecas"
7688                }
7689            ),
7690            "got {err:?}",
7691        );
7692    }
7693
7694    #[test]
7695    fn validate_code_paths_rejects_empty_exe_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: ":exe" }),
7700            "got {err:?}",
7701        );
7702    }
7703
7704    #[test]
7705    fn validate_code_paths_rejects_empty_servicos_entry() {
7706        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
7707        let err = c.validate_code_paths().unwrap_err();
7708        assert!(
7709            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
7710            "got {err:?}",
7711        );
7712    }
7713
7714    #[test]
7715    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
7716        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
7717        // so an absolute path that resolves on disk silently passes the
7718        // layout's existence check — the canonical sandbox-escape on
7719        // the biblioteca axis.
7720        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7721        let err = c.validate_code_paths().unwrap_err();
7722        let ManifestError::CodePathAbsolute { slot, path } = err else {
7723            panic!("expected CodePathAbsolute, got {err:?}");
7724        };
7725        assert_eq!(slot, ":bibliotecas");
7726        assert_eq!(path, PathBuf::from("/etc/passwd"));
7727    }
7728
7729    #[test]
7730    fn validate_code_paths_rejects_absolute_exe_entry() {
7731        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
7732        let err = c.validate_code_paths().unwrap_err();
7733        let ManifestError::CodePathAbsolute { slot, path } = err else {
7734            panic!("expected CodePathAbsolute, got {err:?}");
7735        };
7736        assert_eq!(slot, ":exe");
7737        assert_eq!(path, PathBuf::from("/usr/bin/env"));
7738    }
7739
7740    #[test]
7741    fn validate_code_paths_rejects_absolute_servicos_entry() {
7742        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
7743        let err = c.validate_code_paths().unwrap_err();
7744        let ManifestError::CodePathAbsolute { slot, path } = err else {
7745            panic!("expected CodePathAbsolute, got {err:?}");
7746        };
7747        assert_eq!(slot, ":servicos");
7748        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
7749    }
7750
7751    #[test]
7752    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
7753        // Canonical "I want a lib from a sibling caixa" footgun on the
7754        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
7755        // downstream, so a leading `..` traverses to the parent of the
7756        // caixa root with no diagnostic at layout time if the resolved
7757        // target exists.
7758        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
7759        let err = c.validate_code_paths().unwrap_err();
7760        let ManifestError::CodePathParentEscape { slot, path } = err else {
7761            panic!("expected CodePathParentEscape, got {err:?}");
7762        };
7763        assert_eq!(slot, ":bibliotecas");
7764        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
7765    }
7766
7767    #[test]
7768    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
7769        // Mid-path `..` defeats the layout's component-aware
7770        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
7771        // `starts_with(<root>/exe)` is true, but the canonical resolution
7772        // lives outside the caixa root. Caught regardless of where the
7773        // `..` sits — mirrors the peer
7774        // `behavior::validate_rejects_parent_escape_mid_path` pin.
7775        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
7776        let err = c.validate_code_paths().unwrap_err();
7777        let ManifestError::CodePathParentEscape { slot, path } = err else {
7778            panic!("expected CodePathParentEscape, got {err:?}");
7779        };
7780        assert_eq!(slot, ":exe");
7781        assert_eq!(path, PathBuf::from("exe/../../escape"));
7782    }
7783
7784    #[test]
7785    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
7786        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
7787        let err = c.validate_code_paths().unwrap_err();
7788        let ManifestError::CodePathParentEscape { slot, path } = err else {
7789            panic!("expected CodePathParentEscape, got {err:?}");
7790        };
7791        assert_eq!(slot, ":servicos");
7792        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
7793    }
7794
7795    #[test]
7796    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
7797        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
7798        // `:servicos`. A manifest with malformed entries on all three
7799        // surfaces surfaces the `:bibliotecas` defect first, mirroring
7800        // the canonical declaration order
7801        // `Caixa::declared_foreign_code_slots` already establishes for
7802        // the foreign-code-slot diagnostic.
7803        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
7804        let err = c.validate_code_paths().unwrap_err();
7805        assert!(
7806            matches!(
7807                err,
7808                ManifestError::CodePathEmpty {
7809                    slot: ":bibliotecas"
7810                }
7811            ),
7812            "got {err:?}",
7813        );
7814    }
7815
7816    #[test]
7817    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
7818        // Within-slot precedence pin: empty → absolute → parent-escape,
7819        // matching the [`PathShapeViolation`] arm-ordering every peer
7820        // `is_sandboxed_relative_path` caller follows (b0c8389
7821        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
7822        // `:bibliotecas` list whose first entry is empty *and* whose
7823        // later entries are absolute/parent-escape surfaces the empty
7824        // arm first, on the lexicographically-earliest offending entry.
7825        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
7826        let err = c.validate_code_paths().unwrap_err();
7827        assert!(
7828            matches!(
7829                err,
7830                ManifestError::CodePathEmpty {
7831                    slot: ":bibliotecas"
7832                }
7833            ),
7834            "got {err:?}",
7835        );
7836    }
7837
7838    #[test]
7839    fn validate_code_paths_first_offender_per_slot_wins() {
7840        // Within a single slot, the first declaration-order offender
7841        // surfaces — pins that the gate is left-to-right deterministic
7842        // (peer of every `*_first_collision_*` pin on duplicate gates).
7843        let c = caixa_with_code_paths(
7844            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
7845            vec![],
7846            vec![],
7847        );
7848        let err = c.validate_code_paths().unwrap_err();
7849        let ManifestError::CodePathAbsolute { slot, path } = err else {
7850            panic!("expected CodePathAbsolute, got {err:?}");
7851        };
7852        assert_eq!(slot, ":bibliotecas");
7853        assert_eq!(path, PathBuf::from("/etc/escape"));
7854    }
7855
7856    #[test]
7857    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
7858        // Diagnostic-shape pin (peer with
7859        // `nome_invalid_diagnostic_carries_offending_nome` /
7860        // `versao_invalid_diagnostic_carries_offending_versao`): the
7861        // error's Display surfaces both the offending `:slot` tag and
7862        // the offending path verbatim, so a `feira lint` run can render
7863        // the diagnostic without re-parsing.
7864        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7865        let rendered = c.validate_code_paths().unwrap_err().to_string();
7866        assert!(
7867            rendered.contains(":bibliotecas"),
7868            "diagnostic must name the offending slot: {rendered}",
7869        );
7870        assert!(
7871            rendered.contains("/etc/passwd"),
7872            "diagnostic must quote the offending path: {rendered}",
7873        );
7874    }
7875
7876    #[test]
7877    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
7878        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
7879        // axis. Without the gate `feira build` re-parses the same lib
7880        // twice, wasting work and silently masking the author's intent
7881        // to declare a *second* biblioteca.
7882        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
7883        let err = c.validate_code_paths().unwrap_err();
7884        let ManifestError::CodePathDuplicate { slot, path } = err else {
7885            panic!("expected CodePathDuplicate, got {err:?}");
7886        };
7887        assert_eq!(slot, ":bibliotecas");
7888        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
7889    }
7890
7891    #[test]
7892    fn validate_code_paths_rejects_duplicate_exe_entry() {
7893        // Same footgun on the Binario surface. The future `caixa-flake`
7894        // emitter that materializes each `:exe` entry as a flake
7895        // `packages.<name>` derivation would collide on the duplicate
7896        // package key — surfaced here at the typed-validate layer with a
7897        // self-locating diagnostic instead.
7898        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
7899        let err = c.validate_code_paths().unwrap_err();
7900        let ManifestError::CodePathDuplicate { slot, path } = err else {
7901            panic!("expected CodePathDuplicate, got {err:?}");
7902        };
7903        assert_eq!(slot, ":exe");
7904        assert_eq!(path, PathBuf::from("exe/cli"));
7905    }
7906
7907    #[test]
7908    fn validate_code_paths_rejects_duplicate_servicos_entry() {
7909        // Same footgun on the Servico surface. The peer caixa-helm /
7910        // caixa-flux renderers refuse `:servicos.len() != 1` with the
7911        // narrower `UnsupportedServicoCount` diagnostic, but that
7912        // diagnostic surfaces "too many servicos" without naming
7913        // "duplicate entry" — the typed self-locating framing only lands
7914        // at this gate.
7915        let c = caixa_with_code_paths(
7916            vec![],
7917            vec![],
7918            vec![
7919                "servicos/demo.computeunit.yaml",
7920                "servicos/demo.computeunit.yaml",
7921            ],
7922        );
7923        let err = c.validate_code_paths().unwrap_err();
7924        let ManifestError::CodePathDuplicate { slot, path } = err else {
7925            panic!("expected CodePathDuplicate, got {err:?}");
7926        };
7927        assert_eq!(slot, ":servicos");
7928        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
7929    }
7930
7931    #[test]
7932    fn validate_code_paths_accepts_same_path_across_slots() {
7933        // Per-list scope pin: a `:bibliotecas` entry that happens to
7934        // collide with an `:exe` or `:servicos` entry as a *string* is
7935        // not a duplicate by this gate (each list gets its own HashSet),
7936        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
7937        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
7938        // shape on the dep axis). The structural `starts_with(<exe |
7939        // servicos>_dir)` fence at layout time prevents the realistic
7940        // cross-slot collision case from existing on disk, but the gate's
7941        // per-list scope is correct independent of that downstream fence.
7942        let c = caixa_with_code_paths(
7943            vec!["lib/x.lisp"],
7944            vec!["exe/x"],
7945            vec!["servicos/x.computeunit.yaml"],
7946        );
7947        c.validate_code_paths().unwrap();
7948    }
7949
7950    #[test]
7951    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
7952        // Within-slot ordering pin: structural defects (empty / absolute
7953        // / parent-escape) fire before the duplicate gate on the same
7954        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
7955        // surfaces the narrower `CodePathEmpty` for the empty entry
7956        // first, not the duplicate on the later pair — same arm-ordering
7957        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
7958        // `:autores` 86c769b, `:deps` 359fba5).
7959        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
7960        let err = c.validate_code_paths().unwrap_err();
7961        assert!(
7962            matches!(
7963                err,
7964                ManifestError::CodePathEmpty {
7965                    slot: ":bibliotecas"
7966                }
7967            ),
7968            "got {err:?}",
7969        );
7970    }
7971
7972    #[test]
7973    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
7974        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
7975        // duplicates surface before `:exe` duplicates, matching the
7976        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
7977        // order every peer per-slot diagnostic on this surface follows.
7978        let c = caixa_with_code_paths(
7979            vec!["lib/x.lisp", "lib/x.lisp"],
7980            vec!["exe/y", "exe/y"],
7981            vec![],
7982        );
7983        let err = c.validate_code_paths().unwrap_err();
7984        let ManifestError::CodePathDuplicate { slot, path } = err else {
7985            panic!("expected CodePathDuplicate, got {err:?}");
7986        };
7987        assert_eq!(slot, ":bibliotecas");
7988        assert_eq!(path, PathBuf::from("lib/x.lisp"));
7989    }
7990
7991    #[test]
7992    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
7993        // Diagnostic-shape pin (peer with
7994        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
7995        // on the structural arm): the duplicate-arm Display surfaces both
7996        // the offending `:slot` tag and the offending path verbatim, so a
7997        // `feira lint` run can render the diagnostic without re-parsing.
7998        let c = caixa_with_code_paths(
7999            vec![],
8000            vec![],
8001            vec![
8002                "servicos/demo.computeunit.yaml",
8003                "servicos/demo.computeunit.yaml",
8004            ],
8005        );
8006        let rendered = c.validate_code_paths().unwrap_err().to_string();
8007        assert!(
8008            rendered.contains(":servicos"),
8009            "diagnostic must name the offending slot: {rendered}",
8010        );
8011        assert!(
8012            rendered.contains("servicos/demo.computeunit.yaml"),
8013            "diagnostic must quote the offending path: {rendered}",
8014        );
8015    }
8016
8017    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
8018    //
8019    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
8020    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
8021    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
8022    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
8023    // at parse time — the same downstream consumer the peer `:behavior
8024    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
8025    // `:upgrade-from :state-change :script` (33cc830,
8026    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
8027    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
8028    // nix-built executable surface (`"exe/<name>"` shape per the canonical
8029    // [`crate::LayoutError::ExeOutsideDir`] error message and every
8030    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
8031    // is the `.computeunit.yaml` ComputeUnit-CR axis.
8032
8033    #[test]
8034    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
8035        // Canonical "I dragged the wrong file from the workspace tree"
8036        // footgun on the biblioteca axis. Without the gate `feira build`
8037        // hands the extensionless path to `tatara_lisp::read` and fails
8038        // with a parser-shaped diagnostic far from the source caixa.lisp,
8039        // with no field naming the offending `:bibliotecas` entry.
8040        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
8041            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8042            let err = c.validate_code_paths().unwrap_err();
8043            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8044                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8045            };
8046            assert_eq!(slot, ":bibliotecas");
8047            assert_eq!(path, PathBuf::from(relpath));
8048        }
8049    }
8050
8051    #[test]
8052    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
8053        // Wrong-extension sweep across common authoring footguns. Same
8054        // sweep posture as the peer
8055        // `behavior::validate_rejects_wrong_extension` (c97815a) and
8056        // `upgrade::tests::state_change_rejects_wrong_extension_script`
8057        // (33cc830) cases.
8058        for relpath in [
8059            "lib/demo.rs",
8060            "lib/demo.txt",
8061            "lib/demo.md",
8062            "lib/demo.json",
8063            "lib/demo.yaml",
8064            "lib/demo.toml",
8065            "lib/demo.lisp.bak",
8066            "lib/demo.lispx",
8067            "lib/demo.lis",
8068        ] {
8069            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8070            let err = c.validate_code_paths().unwrap_err();
8071            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8072                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8073            };
8074            assert_eq!(slot, ":bibliotecas");
8075            assert_eq!(path, PathBuf::from(relpath));
8076        }
8077    }
8078
8079    #[test]
8080    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
8081        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
8082        // contract. An uppercase `.LISP` shape that the layout's existence
8083        // check would (case-insensitively, on case-insensitive volumes)
8084        // match the on-disk file still mismatches the canonical form the
8085        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
8086        // contract. Mirrors the peer
8087        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
8088        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
8089        // (33cc830) sweeps.
8090        for relpath in [
8091            "lib/demo.LISP",
8092            "lib/demo.Lisp",
8093            "lib/demo.LiSp",
8094            "lib/demo.lISP",
8095        ] {
8096            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8097            let err = c.validate_code_paths().unwrap_err();
8098            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8099                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8100            };
8101            assert_eq!(slot, ":bibliotecas");
8102            assert_eq!(path, PathBuf::from(relpath));
8103        }
8104    }
8105
8106    #[test]
8107    fn validate_code_paths_accepts_canonical_lisp_shapes() {
8108        // Positive-control sweep through every canonical authoring shape
8109        // every in-tree fixture and the `Caixa::template` scaffold use.
8110        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
8111        // (c97815a) and the lifted predicate's own
8112        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
8113        // (33cc830).
8114        for relpath in [
8115            "lib/demo.lisp",
8116            "lib/handlers.lisp",
8117            "lib/migrations/v01-to-v02.lisp",
8118            "demo.lisp",
8119            "a.lisp",
8120            "./lib/demo.lisp",
8121            "lib/./handlers.lisp",
8122            "lib/migrations/v.0.1.lisp",
8123        ] {
8124            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8125            c.validate_code_paths()
8126                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8127        }
8128    }
8129
8130    #[test]
8131    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
8132        // The file-type gate is per-slot — only `:bibliotecas` carries the
8133        // tatara-lisp-source contract. An extensionless `:exe` entry
8134        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
8135        // canonical shapes every in-tree fixture uses, and must continue
8136        // to pass validate. Pins that a future tightening that broadens
8137        // the `.lisp` gate to either axis surfaces as a test failure
8138        // rather than as a silent breaking change to existing valid
8139        // manifests.
8140        let c = caixa_with_code_paths(
8141            vec![],
8142            vec!["exe/demo", "exe/tool"],
8143            vec!["servicos/demo.computeunit.yaml"],
8144        );
8145        c.validate_code_paths().unwrap();
8146    }
8147
8148    #[test]
8149    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
8150        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
8151        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
8152        // sandbox-shape diagnostic first (the `.lisp` remediation would
8153        // be misleading when the offending path can never resolve under
8154        // the caixa root anyway). Mirrors the peer
8155        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
8156        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
8157        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
8158        // on `:upgrade-from :state-change :script` (33cc830).
8159        //
8160        // Empty wins (the strictly-smaller-scope structural arm).
8161        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
8162        assert!(
8163            matches!(
8164                c.validate_code_paths().unwrap_err(),
8165                ManifestError::CodePathEmpty {
8166                    slot: ":bibliotecas"
8167                }
8168            ),
8169            "empty must win over non-lisp-extension",
8170        );
8171        // Absolute wins (the path can't resolve under the caixa root).
8172        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8173        let err = c.validate_code_paths().unwrap_err();
8174        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8175            panic!("absolute must win over non-lisp-extension, got {err:?}");
8176        };
8177        assert_eq!(slot, ":bibliotecas");
8178        // ParentEscape wins (the path escapes the caixa root).
8179        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
8180        let err = c.validate_code_paths().unwrap_err();
8181        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8182            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
8183        };
8184        assert_eq!(slot, ":bibliotecas");
8185    }
8186
8187    #[test]
8188    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
8189        // Within-slot precedence pin: the per-entry file-type shape gate
8190        // fires before the cross-entry duplicate gate, so the narrower
8191        // structural defect dominates the uniqueness diagnostic. A
8192        // `("lib/x.txt" "lib/x.txt")` shape surfaces
8193        // `CodePathNonLispExtension` on the first entry rather than
8194        // `CodePathDuplicate` on the pair — same posture every per-entry
8195        // shape-gate-precedes-duplicate cascade follows on this surface
8196        // (the empty / absolute / parent-escape arms already precede the
8197        // duplicate arm; the lifted file-type arm joins that set).
8198        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
8199        let err = c.validate_code_paths().unwrap_err();
8200        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8201            panic!("expected CodePathNonLispExtension, got {err:?}");
8202        };
8203        assert_eq!(slot, ":bibliotecas");
8204        assert_eq!(path, PathBuf::from("lib/x.txt"));
8205    }
8206
8207    #[test]
8208    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
8209        // Diagnostic-shape pin (peer with
8210        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8211        // on the sandbox-shape arms and
8212        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
8213        // on the duplicate arm): the file-type-arm Display surfaces both
8214        // the offending `:slot` tag, the offending path verbatim, and the
8215        // expected `.lisp` extension named in the remediation text, so a
8216        // `feira lint` run can render the diagnostic without re-parsing.
8217        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
8218        let rendered = c.validate_code_paths().unwrap_err().to_string();
8219        assert!(
8220            rendered.contains(":bibliotecas"),
8221            "diagnostic must name the offending slot: {rendered}",
8222        );
8223        assert!(
8224            rendered.contains("lib/demo.rs"),
8225            "diagnostic must quote the offending path: {rendered}",
8226        );
8227        assert!(
8228            rendered.contains(".lisp"),
8229            "diagnostic must name the expected extension: {rendered}",
8230        );
8231    }
8232
8233    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
8234    //
8235    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
8236    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
8237    // contract. The peer caixa-helm / caixa-flux renderers consume each
8238    // `:servicos` entry through `serde_yaml::from_str` as a typed
8239    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
8240    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
8241    // axis `Path::extension` can't express on its own.
8242
8243    #[test]
8244    fn validate_code_paths_rejects_no_extension_servicos_entry() {
8245        // Canonical "I dragged the wrong file from the workspace tree"
8246        // footgun on the Servico axis. Without the gate the peer
8247        // caixa-helm / caixa-flux renderers hand the extensionless path
8248        // to `serde_yaml::from_str` and fail with a parser-shaped
8249        // diagnostic far from the source caixa.lisp, with no field
8250        // naming the offending `:servicos` entry.
8251        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
8252            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8253            let err = c.validate_code_paths().unwrap_err();
8254            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8255                panic!(
8256                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8257                     got {err:?}"
8258                );
8259            };
8260            assert_eq!(slot, ":servicos");
8261            assert_eq!(path, PathBuf::from(relpath));
8262        }
8263    }
8264
8265    #[test]
8266    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
8267        // Wrong-extension sweep across common authoring footguns on the
8268        // Servico axis. Bare `.yaml` is the canonical "I forgot the
8269        // `.computeunit` segment" typo; the off-by-one-segment shapes
8270        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
8271        // bare `Path::extension` view but mismatch the typed compound
8272        // suffix the renderers' `serde_yaml::from_str` consumer demands.
8273        // Same sweep-posture as the peer
8274        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
8275        // (64772a9) on the sibling tatara-lisp-source axis.
8276        for relpath in [
8277            "servicos/demo.yaml",
8278            "servicos/demo.yml",
8279            "servicos/demo.json",
8280            "servicos/demo.toml",
8281            "servicos/demo.txt",
8282            "servicos/demo.computeunit.yaml.bak",
8283            "servicos/demo.computeunit.yam",
8284            "servicos/demo.computeunit",
8285            "servicos/demo-computeunit.yaml",
8286            "servicos/demo_computeunit.yaml",
8287        ] {
8288            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8289            let err = c.validate_code_paths().unwrap_err();
8290            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8291                panic!(
8292                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8293                     got {err:?}"
8294                );
8295            };
8296            assert_eq!(slot, ":servicos");
8297            assert_eq!(path, PathBuf::from(relpath));
8298        }
8299    }
8300
8301    #[test]
8302    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
8303        // Case-sensitivity sweep — pins the strict lowercase
8304        // `.computeunit.yaml` contract. A case-folded shape that the
8305        // layout's existence check would (case-insensitively, on
8306        // case-insensitive volumes) match the on-disk file still
8307        // mismatches the canonical form the codec emits, breaking the
8308        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
8309        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
8310        // (64772a9) sweep on the sibling tatara-lisp-source axis.
8311        for relpath in [
8312            "servicos/demo.ComputeUnit.yaml",
8313            "servicos/demo.COMPUTEUNIT.yaml",
8314            "servicos/demo.computeunit.YAML",
8315            "servicos/demo.computeunit.Yaml",
8316            "servicos/demo.COMPUTEUNIT.YAML",
8317        ] {
8318            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8319            let err = c.validate_code_paths().unwrap_err();
8320            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8321                panic!(
8322                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8323                     got {err:?}"
8324                );
8325            };
8326            assert_eq!(slot, ":servicos");
8327            assert_eq!(path, PathBuf::from(relpath));
8328        }
8329    }
8330
8331    #[test]
8332    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
8333        // Degenerate hidden-file shape: a file name exactly equal to the
8334        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
8335        // the structural "Servico declared with no identity" footgun.
8336        // The substrate identifies each ComputeUnit by the file-stem
8337        // segment that precedes `.computeunit.yaml` (the rendered
8338        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
8339        // the M3 `:contratos` membership lookup), so an empty stem
8340        // leaves the Servico unidentifiable. Pinned at the typed-axis
8341        // level so a future regression that drops the `name.len() >
8342        // SUFFIX.len()` bound at the predicate surfaces here, not
8343        // piecemeal as a `lareira-` chart-name collision at render time.
8344        for relpath in ["servicos/.computeunit.yaml"] {
8345            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8346            let err = c.validate_code_paths().unwrap_err();
8347            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8348                panic!(
8349                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8350                     got {err:?}"
8351                );
8352            };
8353            assert_eq!(slot, ":servicos");
8354            assert_eq!(path, PathBuf::from(relpath));
8355        }
8356    }
8357
8358    #[test]
8359    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
8360        // Positive-control sweep through every canonical authoring shape
8361        // every in-tree fixture and the `Caixa::template` scaffold use.
8362        // Mirrors the peer
8363        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
8364        // and the lifted predicate's own
8365        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
8366        // render.rs.
8367        for relpath in [
8368            "servicos/demo.computeunit.yaml",
8369            "servicos/hello-rio.computeunit.yaml",
8370            "servicos/my-service.computeunit.yaml",
8371            "servicos/a.computeunit.yaml",
8372            "./servicos/demo.computeunit.yaml",
8373            "servicos/./demo.computeunit.yaml",
8374            "servicos/sub/nested.computeunit.yaml",
8375            "servicos/v0.1.computeunit.yaml",
8376        ] {
8377            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8378            c.validate_code_paths()
8379                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8380        }
8381    }
8382
8383    #[test]
8384    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
8385        // The file-type gate is per-slot — only `:servicos` carries the
8386        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
8387        // entry and an extensionless `:exe` entry are the canonical
8388        // shapes every in-tree fixture uses, and must continue to pass
8389        // validate. Peer of
8390        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
8391        // (64772a9) — together pin that the typed
8392        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
8393        // cross-axis leakage in either direction.
8394        let c = caixa_with_code_paths(
8395            vec!["lib/demo.lisp"],
8396            vec!["exe/demo", "exe/tool"],
8397            vec!["servicos/demo.computeunit.yaml"],
8398        );
8399        c.validate_code_paths().unwrap();
8400    }
8401
8402    #[test]
8403    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
8404        // Cross-arm precedence pin: a `:servicos` entry that is *both*
8405        // sandbox-escaping and wrong-extension surfaces the more
8406        // fundamental sandbox-shape diagnostic first (the
8407        // `.computeunit.yaml` remediation would be misleading when the
8408        // offending path can never resolve under the caixa root
8409        // anyway). Mirrors the peer
8410        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
8411        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
8412        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
8413        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
8414        // table establishes.
8415        //
8416        // Empty wins (the strictly-smaller-scope structural arm).
8417        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
8418        assert!(
8419            matches!(
8420                c.validate_code_paths().unwrap_err(),
8421                ManifestError::CodePathEmpty { slot: ":servicos" }
8422            ),
8423            "empty must win over non-computeunit-yaml-extension",
8424        );
8425        // Absolute wins (the path can't resolve under the caixa root).
8426        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
8427        let err = c.validate_code_paths().unwrap_err();
8428        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8429            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
8430        };
8431        assert_eq!(slot, ":servicos");
8432        // ParentEscape wins (the path escapes the caixa root).
8433        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
8434        let err = c.validate_code_paths().unwrap_err();
8435        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8436            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
8437        };
8438        assert_eq!(slot, ":servicos");
8439    }
8440
8441    #[test]
8442    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
8443        // Within-slot precedence pin: the per-entry file-type shape gate
8444        // fires before the cross-entry duplicate gate, so the narrower
8445        // structural defect dominates the uniqueness diagnostic. A
8446        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
8447        // `CodePathNonComputeUnitYamlExtension` on the first entry
8448        // rather than `CodePathDuplicate` on the pair — same posture
8449        // every per-entry shape-gate-precedes-duplicate cascade follows
8450        // on this surface, peer of the 64772a9 `:bibliotecas`
8451        // `("lib/x.txt" "lib/x.txt")` ordering.
8452        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
8453        let err = c.validate_code_paths().unwrap_err();
8454        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8455            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
8456        };
8457        assert_eq!(slot, ":servicos");
8458        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
8459    }
8460
8461    #[test]
8462    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
8463     {
8464        // Diagnostic-shape pin (peer with
8465        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
8466        // on the sibling tatara-lisp-source axis): the file-type-arm
8467        // Display surfaces both the offending `:slot` tag, the
8468        // offending path verbatim, and the expected
8469        // `.computeunit.yaml` compound suffix named in the remediation
8470        // text, so a `feira lint` run can render the diagnostic without
8471        // re-parsing.
8472        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
8473        let rendered = c.validate_code_paths().unwrap_err().to_string();
8474        assert!(
8475            rendered.contains(":servicos"),
8476            "diagnostic must name the offending slot: {rendered}",
8477        );
8478        assert!(
8479            rendered.contains("servicos/demo.yaml"),
8480            "diagnostic must quote the offending path: {rendered}",
8481        );
8482        assert!(
8483            rendered.contains(".computeunit.yaml"),
8484            "diagnostic must name the expected compound suffix: {rendered}",
8485        );
8486    }
8487
8488    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
8489
8490    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
8491        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8492        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
8493        c
8494    }
8495
8496    #[test]
8497    fn validate_etiquetas_accepts_empty_list() {
8498        // The empty-list identity: every caixa with no declared tags
8499        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
8500        // so the gate is non-disruptive against every existing manifest.
8501        let c = caixa_with_etiquetas(vec![]);
8502        c.validate_etiquetas().unwrap();
8503    }
8504
8505    #[test]
8506    fn validate_etiquetas_accepts_canonical_forms() {
8507        // Positive control sweep: a canonical-shaped non-empty distinct
8508        // tag list passes, mirroring the example checkout-aplicacao
8509        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
8510        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
8511        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
8512        c.validate_etiquetas().unwrap();
8513    }
8514
8515    #[test]
8516    fn validate_etiquetas_rejects_empty_entry() {
8517        // Canonical paste-from-blank-doc footgun. Without the gate the
8518        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
8519        // no-op tag indexing nothing in the future caixa-registry.
8520        let c = caixa_with_etiquetas(vec![""]);
8521        let err = c.validate_etiquetas().unwrap_err();
8522        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8523    }
8524
8525    #[test]
8526    fn validate_etiquetas_rejects_duplicate_entry() {
8527        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
8528        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
8529        // collect at chart render — a "second wins / one silently
8530        // disappears" shape divergent from every peer typed-graph set
8531        // gate. The duplicate-arm names the offending tag verbatim.
8532        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8533        let err = c.validate_etiquetas().unwrap_err();
8534        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8535            panic!("expected EtiquetaDuplicate, got {err:?}");
8536        };
8537        assert_eq!(etiqueta, "demo");
8538    }
8539
8540    #[test]
8541    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
8542        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
8543        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
8544        // structural "this entry has no value" defect dominates the
8545        // cross-entry uniqueness diagnostic. Mirrors the peer
8546        // empty-before-duplicate cascades on `:caracteristicas`
8547        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8548        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
8549        // `MembroDuplicate`).
8550        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
8551        let err = c.validate_etiquetas().unwrap_err();
8552        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8553    }
8554
8555    #[test]
8556    fn validate_etiquetas_duplicate_reports_first_collision() {
8557        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8558        // duplicate (the lexicographically-earliest offending position
8559        // — the second `"a"` at index 2 collides with the first `"a"`
8560        // at index 0), not the later `"b"` collision at index 3,
8561        // peer with every other first-collision diagnostic posture on
8562        // this surface (`validate_load_singularity_reports_first_collision`,
8563        // `validate_cleanup_singularity_reports_first_collision`).
8564        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
8565        let err = c.validate_etiquetas().unwrap_err();
8566        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8567            panic!("expected EtiquetaDuplicate, got {err:?}");
8568        };
8569        assert_eq!(etiqueta, "a");
8570    }
8571
8572    #[test]
8573    fn validate_etiquetas_case_sensitive() {
8574        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
8575        // mirroring the peer `:membros :caixa` / `:children :caixa`
8576        // exact-string-match discipline. The shape gate this routine
8577        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
8578        // grammar) accepts mixed case — crates.io's keyword rule is
8579        // "case-insensitive" at the index layer but admits mixed case
8580        // at the entry layer (the canonical Helm chart `keywords:`
8581        // shape is lowercase by convention, but the grammar admits
8582        // uppercase). Case-sensitivity at the duplicate-set layer
8583        // remains structural — two distinct strings are two distinct
8584        // entries.
8585        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
8586        c.validate_etiquetas().unwrap();
8587    }
8588
8589    #[test]
8590    fn validate_etiquetas_diagnostic_carries_offending_tag() {
8591        // Diagnostic-shape pin (peer with
8592        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
8593        // the error's Display surfaces the offending tag verbatim, so a
8594        // `feira lint` run can render the diagnostic without re-parsing
8595        // and the author can grep their caixa.lisp for the offending
8596        // value.
8597        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8598        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8599        assert!(
8600            rendered.contains(":etiquetas"),
8601            "diagnostic must name the offending slot: {rendered}",
8602        );
8603        assert!(
8604            rendered.contains("demo"),
8605            "diagnostic must quote the offending tag: {rendered}",
8606        );
8607    }
8608
8609    #[test]
8610    fn validate_etiquetas_rejects_leading_whitespace_entry() {
8611        // Canonical paste-from-aligned-doc footgun. Without the shape
8612        // gate `" mesh"` silently passed validate and landed as a
8613        // YAML plain-style scalar with leading whitespace in the
8614        // rendered Chart.yaml `keywords:` array — every YAML 1.2
8615        // dumper trims leading whitespace from plain-style scalars,
8616        // so the authored space round-tripped inconsistently back
8617        // through `caixa.lisp`. Mirrors the peer
8618        // `validate_autores_rejects_leading_whitespace_entry`.
8619        let c = caixa_with_etiquetas(vec![" mesh"]);
8620        let err = c.validate_etiquetas().unwrap_err();
8621        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8622            panic!("expected EtiquetaInvalid, got {err:?}");
8623        };
8624        assert_eq!(etiqueta, " mesh");
8625        assert!(reason.contains("whitespace"), "got: {reason}");
8626    }
8627
8628    #[test]
8629    fn validate_etiquetas_rejects_embedded_newline_entry() {
8630        // Canonical paste-from-multiline-doc footgun — the author
8631        // pasted a multi-tag block into one `:etiquetas` entry
8632        // instead of splitting into one entry per tag. Without the
8633        // shape gate `"mesh\nhttp"` silently passed validate and
8634        // landed as a YAML-illegal multi-line scalar in the rendered
8635        // Chart.yaml `keywords:` array.
8636        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8637        let err = c.validate_etiquetas().unwrap_err();
8638        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8639            panic!("expected EtiquetaInvalid, got {err:?}");
8640        };
8641        assert_eq!(etiqueta, "mesh\nhttp");
8642        assert!(reason.contains("newline"), "got: {reason}");
8643    }
8644
8645    #[test]
8646    fn validate_etiquetas_rejects_embedded_comma_entry() {
8647        // Canonical CSV-list-separator-confusion footgun: the author
8648        // confused the CSV-style separator convention with the
8649        // `:etiquetas` list grammar. Without the shape gate
8650        // `"mesh,http,grpc"` silently passed validate and landed as a
8651        // single malformed search tag in the rendered Chart.yaml
8652        // `keywords:` array — Artifact Hub's keyword index would
8653        // either silently drop the tag or index it as
8654        // `mesh,http,grpc` instead of three separate tags.
8655        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
8656        let err = c.validate_etiquetas().unwrap_err();
8657        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8658            panic!("expected EtiquetaInvalid, got {err:?}");
8659        };
8660        assert_eq!(etiqueta, "mesh,http,grpc");
8661        assert!(reason.contains('`'), "got: {reason}");
8662        assert!(reason.contains(','), "got: {reason}");
8663    }
8664
8665    #[test]
8666    fn validate_etiquetas_rejects_embedded_slash_entry() {
8667        // Canonical path-separator-confusion footgun: the author
8668        // confused namespace-path notation with the keyword grammar.
8669        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
8670        let err = c.validate_etiquetas().unwrap_err();
8671        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8672            panic!("expected EtiquetaInvalid, got {err:?}");
8673        };
8674        assert_eq!(etiqueta, "caixa/servico");
8675        assert!(reason.contains('/'), "got: {reason}");
8676    }
8677
8678    #[test]
8679    fn validate_etiquetas_rejects_leading_digit_entry() {
8680        // Canonical paste-from-numbered-list footgun: the author
8681        // copied `1. mesh` from a numbered doc and the `1` leaked
8682        // into the tag.
8683        let c = caixa_with_etiquetas(vec!["1mesh"]);
8684        let err = c.validate_etiquetas().unwrap_err();
8685        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8686            panic!("expected EtiquetaInvalid, got {err:?}");
8687        };
8688        assert_eq!(etiqueta, "1mesh");
8689        assert!(reason.contains("digit"), "got: {reason}");
8690    }
8691
8692    #[test]
8693    fn validate_etiquetas_rejects_leading_hyphen_entry() {
8694        // Canonical kebab-leak footgun.
8695        let c = caixa_with_etiquetas(vec!["-foo"]);
8696        let err = c.validate_etiquetas().unwrap_err();
8697        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8698            panic!("expected EtiquetaInvalid, got {err:?}");
8699        };
8700        assert_eq!(etiqueta, "-foo");
8701        assert!(reason.contains('-'), "got: {reason}");
8702    }
8703
8704    #[test]
8705    fn validate_etiquetas_rejects_non_ascii_entry() {
8706        // Canonical paste-from-Unicode-doc footgun. Every legitimate
8707        // search tag is strict ASCII; raw non-ASCII silently
8708        // round-trips inconsistently across NFC/NFD normalization on
8709        // APFS / case-folding filesystems and breaks the Artifact Hub
8710        // keyword search index lookup.
8711        let c = caixa_with_etiquetas(vec!["café"]);
8712        let err = c.validate_etiquetas().unwrap_err();
8713        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8714            panic!("expected EtiquetaInvalid, got {err:?}");
8715        };
8716        assert_eq!(etiqueta, "café");
8717        assert!(reason.contains("non-ASCII"), "got: {reason}");
8718    }
8719
8720    #[test]
8721    fn validate_etiquetas_rejects_period_entry() {
8722        // Canonical namespace-confusion / version-suffix footgun
8723        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
8724        // excludes `.` from the continuation set even though the
8725        // sibling `:caracteristicas` axis (Cargo's feature-name
8726        // grammar) admits it. Tighter than the sibling axis, peer
8727        // with Cargo's own crates.io keyword shape.
8728        let c = caixa_with_etiquetas(vec!["http.1"]);
8729        let err = c.validate_etiquetas().unwrap_err();
8730        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8731            panic!("expected EtiquetaInvalid, got {err:?}");
8732        };
8733        assert_eq!(etiqueta, "http.1");
8734        assert!(reason.contains('.'), "got: {reason}");
8735    }
8736
8737    #[test]
8738    fn validate_etiquetas_empty_takes_precedence_over_shape() {
8739        // Per-entry empty-first cascade pin: an entry that is both
8740        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
8741        // narrower "this entry has no value" structural defect
8742        // dominates the broader shape-predicate diagnostic). The
8743        // empty arm fires before the shape predicate is consulted,
8744        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
8745        // cascade established on the sibling universal-axis Vec<String>
8746        // surface.
8747        let c = caixa_with_etiquetas(vec![""]);
8748        let err = c.validate_etiquetas().unwrap_err();
8749        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8750    }
8751
8752    #[test]
8753    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
8754        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
8755        // entry that is malformed surfaces `EtiquetaInvalid` even when
8756        // a later entry would have collided on duplicate. The
8757        // per-entry shape arm fires inside the same loop iteration as
8758        // the empty arm, before the seen-set insert at end-of-iteration
8759        // — structural per-entry defects dominate the cross-entry
8760        // uniqueness diagnostic. Mirrors the peer
8761        // `validate_autores_shape_takes_precedence_over_duplicate`.
8762        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
8763        let err = c.validate_etiquetas().unwrap_err();
8764        assert!(
8765            matches!(err, ManifestError::EtiquetaInvalid { .. }),
8766            "got {err:?}",
8767        );
8768    }
8769
8770    #[test]
8771    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
8772        // Diagnostic-shape pin on the new shape arm (peer with
8773        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
8774        // the rendered Display surfaces both the offending slot name
8775        // and the offending value verbatim, so a `feira lint` run
8776        // points the author at the exact `:etiquetas` entry to fix.
8777        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8778        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8779        assert!(
8780            rendered.contains(":etiquetas"),
8781            "diagnostic must name the offending slot: {rendered}",
8782        );
8783        assert!(
8784            rendered.contains("mesh\\nhttp"),
8785            "diagnostic must quote the offending value (debug-escaped): {rendered}",
8786        );
8787    }
8788
8789    #[test]
8790    fn validate_etiquetas_rejects_at_21_byte_boundary() {
8791        // The 20-byte cap pin — boundary-exceeding case rejected,
8792        // boundary-accepting case passes. Mirrors the peer
8793        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
8794        // side pin, surfaced at the per-axis caller so the cap
8795        // propagates through validate end-to-end. Constructed as a
8796        // single all-`a` token so only the cap arm fires.
8797        let max_ok = "a".repeat(20);
8798        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
8799        c.validate_etiquetas().unwrap();
8800        let too_long = "a".repeat(21);
8801        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
8802        let err = c.validate_etiquetas().unwrap_err();
8803        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
8804            panic!("expected EtiquetaInvalid, got {err:?}");
8805        };
8806        assert!(reason.contains("20"), "got: {reason}");
8807        assert!(reason.contains("21"), "got: {reason}");
8808    }
8809
8810    #[test]
8811    fn validate_etiquetas_accepts_canonical_shaped_forms() {
8812        // Positive control sweep: every canonical-shaped tag from the
8813        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
8814        // example fixtures plus the substrate-fixed tags caixa-helm
8815        // unions in at chart render. Drift between this list and the
8816        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
8817        // sweep surfaces here — one source of truth for the rule.
8818        let c = caixa_with_etiquetas(vec![
8819            "example",
8820            "aplicacao",
8821            "mesh",
8822            "ecommerce",
8823            "demo",
8824            "infrastructure",
8825            "aws",
8826            "akeyless",
8827            "pangea-native",
8828            "hello-world",
8829            "wasm",
8830            "rust",
8831            "tatara-lisp",
8832            "caixa-servico",
8833            "lareira",
8834        ]);
8835        c.validate_etiquetas().unwrap();
8836    }
8837
8838    // ── validate_autores — universal-axis maintainer shape ────────────
8839
8840    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
8841        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8842        c.autores = autores.into_iter().map(String::from).collect();
8843        c
8844    }
8845
8846    #[test]
8847    fn validate_autores_accepts_empty_list() {
8848        // The empty-list identity: `Caixa::template` emits `:autores ()`,
8849        // so the gate is non-disruptive against every existing manifest.
8850        let c = caixa_with_autores(vec![]);
8851        c.validate_autores().unwrap();
8852    }
8853
8854    #[test]
8855    fn validate_autores_accepts_canonical_forms() {
8856        // Positive control sweep: every canonical-shaped non-empty
8857        // distinct maintainer list passes — the hello-rio / checkout-
8858        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
8859        // multi-author shape downstream packaging surfaces emit.
8860        let c = caixa_with_autores(vec!["pleme-io"]);
8861        c.validate_autores().unwrap();
8862        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
8863        c.validate_autores().unwrap();
8864    }
8865
8866    #[test]
8867    fn validate_autores_rejects_empty_entry() {
8868        // Canonical paste-from-blank-doc footgun. Without the gate the
8869        // empty entry rendered as `maintainers: [{name: "", email: null}]`
8870        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
8871        // to.
8872        let c = caixa_with_autores(vec![""]);
8873        let err = c.validate_autores().unwrap_err();
8874        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8875    }
8876
8877    #[test]
8878    fn validate_autores_rejects_duplicate_entry() {
8879        // Canonical copy-paste-the-wrong-author footgun. Unlike the
8880        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
8881        // dedups the rendered `keywords:` array), the `maintainers:`
8882        // rendering has *no* dedup — duplicates stack verbatim. The
8883        // duplicate-arm names the offending author verbatim.
8884        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
8885        let err = c.validate_autores().unwrap_err();
8886        let ManifestError::AutorDuplicate { autor } = err else {
8887            panic!("expected AutorDuplicate, got {err:?}");
8888        };
8889        assert_eq!(autor, "pleme-io");
8890    }
8891
8892    #[test]
8893    fn validate_autores_empty_takes_precedence_over_duplicate() {
8894        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
8895        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
8896        // "this entry has no value" defect dominates the cross-entry
8897        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
8898        // cascades on `:etiquetas` (`EtiquetaEmpty` before
8899        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
8900        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8901        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
8902        // `MembroDuplicate`).
8903        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
8904        let err = c.validate_autores().unwrap_err();
8905        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8906    }
8907
8908    #[test]
8909    fn validate_autores_duplicate_reports_first_collision() {
8910        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8911        // duplicate (the lexicographically-earliest offending position
8912        // — the second `"a"` at index 2 collides with the first `"a"`
8913        // at index 0), not the later `"b"` collision at index 3,
8914        // peer with every other first-collision diagnostic posture on
8915        // this surface.
8916        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
8917        let err = c.validate_autores().unwrap_err();
8918        let ManifestError::AutorDuplicate { autor } = err else {
8919            panic!("expected AutorDuplicate, got {err:?}");
8920        };
8921        assert_eq!(autor, "a");
8922    }
8923
8924    #[test]
8925    fn validate_autores_case_sensitive() {
8926        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
8927        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
8928        // / `:children :caixa` exact-string-match discipline.
8929        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
8930        c.validate_autores().unwrap();
8931    }
8932
8933    #[test]
8934    fn validate_autores_diagnostic_carries_offending_author() {
8935        // Diagnostic-shape pin (peer with
8936        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
8937        // error's Display surfaces the offending author verbatim, so a
8938        // `feira lint` run can render the diagnostic without re-parsing
8939        // and the author can grep their caixa.lisp for the offending
8940        // value.
8941        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
8942        let rendered = c.validate_autores().unwrap_err().to_string();
8943        assert!(
8944            rendered.contains(":autores"),
8945            "diagnostic must name the offending slot: {rendered}",
8946        );
8947        assert!(
8948            rendered.contains("pleme-io"),
8949            "diagnostic must quote the offending author: {rendered}",
8950        );
8951    }
8952
8953    #[test]
8954    fn validate_autores_rejects_leading_whitespace_entry() {
8955        // Canonical paste-from-aligned-doc footgun. Without the shape
8956        // gate `" pleme-io"` silently passed validate and landed as a
8957        // YAML plain-style scalar with leading whitespace in the
8958        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
8959        // dumper trims leading whitespace from plain-style scalars, so
8960        // the authored space round-tripped inconsistently back through
8961        // `caixa.lisp`. Mirrors the peer
8962        // `validate_descricao_rejects_leading_whitespace`.
8963        let c = caixa_with_autores(vec![" pleme-io"]);
8964        let err = c.validate_autores().unwrap_err();
8965        let ManifestError::AutorInvalid { autor, reason } = err else {
8966            panic!("expected AutorInvalid, got {err:?}");
8967        };
8968        assert_eq!(autor, " pleme-io");
8969        assert!(reason.contains("whitespace"), "got: {reason}");
8970    }
8971
8972    #[test]
8973    fn validate_autores_rejects_trailing_whitespace_entry() {
8974        // Canonical paste-from-doc footgun.
8975        let c = caixa_with_autores(vec!["pleme-io "]);
8976        let err = c.validate_autores().unwrap_err();
8977        let ManifestError::AutorInvalid { autor, reason } = err else {
8978            panic!("expected AutorInvalid, got {err:?}");
8979        };
8980        assert_eq!(autor, "pleme-io ");
8981        assert!(reason.contains("whitespace"), "got: {reason}");
8982    }
8983
8984    #[test]
8985    fn validate_autores_rejects_embedded_newline_entry() {
8986        // Canonical paste-from-multiline-doc footgun — the author
8987        // pasted a multi-line block of author records into one
8988        // `:autores` entry instead of splitting into one entry per
8989        // author. Without the shape gate `"alice\nbob"` silently
8990        // passed validate and landed as a YAML-illegal multi-line
8991        // scalar in the rendered Chart.yaml `maintainers:` array.
8992        let c = caixa_with_autores(vec!["alice\nbob"]);
8993        let err = c.validate_autores().unwrap_err();
8994        let ManifestError::AutorInvalid { autor, reason } = err else {
8995            panic!("expected AutorInvalid, got {err:?}");
8996        };
8997        assert_eq!(autor, "alice\nbob");
8998        assert!(reason.contains("newline"), "got: {reason}");
8999    }
9000
9001    #[test]
9002    fn validate_autores_rejects_embedded_carriage_return_entry() {
9003        // Canonical paste-from-Windows-CRLF-doc footgun.
9004        let c = caixa_with_autores(vec!["alice\rbob"]);
9005        let err = c.validate_autores().unwrap_err();
9006        let ManifestError::AutorInvalid { autor, reason } = err else {
9007            panic!("expected AutorInvalid, got {err:?}");
9008        };
9009        assert_eq!(autor, "alice\rbob");
9010        assert!(reason.contains("carriage return"), "got: {reason}");
9011    }
9012
9013    #[test]
9014    fn validate_autores_rejects_embedded_tab_entry() {
9015        // Canonical tab-from-aligned-doc footgun.
9016        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
9017        let err = c.validate_autores().unwrap_err();
9018        let ManifestError::AutorInvalid { autor, reason } = err else {
9019            panic!("expected AutorInvalid, got {err:?}");
9020        };
9021        assert_eq!(autor, "Pleme\tContributors");
9022        assert!(reason.contains("tab"), "got: {reason}");
9023    }
9024
9025    #[test]
9026    fn validate_autores_rejects_embedded_control_bytes_entry() {
9027        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
9028        // surface the same control-byte arm.
9029        for entry in [
9030            "alice\x00bob",
9031            "alice\x07bob",
9032            "alice\x1bbob",
9033            "alice\x7fbob",
9034        ] {
9035            let c = caixa_with_autores(vec![entry]);
9036            let err = c.validate_autores().unwrap_err();
9037            let ManifestError::AutorInvalid { autor, reason } = err else {
9038                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
9039            };
9040            assert_eq!(autor, entry);
9041            assert!(
9042                reason.contains("control character"),
9043                "{entry:?} reason: {reason}",
9044            );
9045        }
9046    }
9047
9048    #[test]
9049    fn validate_autores_accepts_unicode_entry() {
9050        // Unicode positive control: realistic maintainer names carry
9051        // Unicode (`François`, `日本語`, `naïve`). The predicate must
9052        // round-trip Unicode losslessly, peer with the
9053        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
9054        // sweep.
9055        let c = caixa_with_autores(vec![
9056            "François Dupont",
9057            "日本語の名前",
9058            "naïve <naive@example.com>",
9059        ]);
9060        c.validate_autores().unwrap();
9061    }
9062
9063    #[test]
9064    fn validate_autores_empty_takes_precedence_over_shape() {
9065        // Per-entry empty-first cascade pin: an entry that is both
9066        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
9067        // "this entry has no value" structural defect dominates the
9068        // broader shape-predicate diagnostic). The empty arm fires
9069        // before the shape predicate is consulted, mirroring the peer
9070        // `validate_repositorio_empty_takes_precedence_over_shape`
9071        // cascade on the universal `Option<String>` siblings — and now
9072        // established on the Vec<String> per-entry surface.
9073        let c = caixa_with_autores(vec![""]);
9074        let err = c.validate_autores().unwrap_err();
9075        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9076    }
9077
9078    #[test]
9079    fn validate_autores_shape_takes_precedence_over_duplicate() {
9080        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
9081        // entry that is malformed surfaces `AutorInvalid` even when a
9082        // later entry would have collided on duplicate. The per-entry
9083        // shape arm fires inside the same loop iteration as the empty
9084        // arm, before the seen-set insert at end-of-iteration —
9085        // structural per-entry defects dominate the cross-entry
9086        // uniqueness diagnostic.
9087        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
9088        let err = c.validate_autores().unwrap_err();
9089        assert!(
9090            matches!(err, ManifestError::AutorInvalid { .. }),
9091            "got {err:?}",
9092        );
9093    }
9094
9095    #[test]
9096    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
9097        // Diagnostic-shape pin on the new shape arm (peer with
9098        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
9099        // the rendered Display surfaces both the offending slot name
9100        // and the offending value verbatim, so a `feira lint` run
9101        // points the author at the exact `:autores` entry to fix.
9102        let c = caixa_with_autores(vec!["alice\nbob"]);
9103        let rendered = c.validate_autores().unwrap_err().to_string();
9104        assert!(
9105            rendered.contains(":autores"),
9106            "diagnostic must name the offending slot: {rendered}",
9107        );
9108        assert!(
9109            rendered.contains("alice\\nbob"),
9110            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9111        );
9112    }
9113
9114    #[test]
9115    fn validate_autores_rejects_at_129_byte_boundary() {
9116        // The 128-byte cap pin — boundary-exceeding case rejected,
9117        // boundary-accepting case passes. Mirrors the peer
9118        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
9119        // substrate-side pin, surfaced at the per-axis caller so the
9120        // cap propagates through validate end-to-end. Constructed as
9121        // a single all-`a` token so only the cap arm fires.
9122        let max_ok = "a".repeat(128);
9123        let c = caixa_with_autores(vec![max_ok.as_str()]);
9124        c.validate_autores().unwrap();
9125        let too_long = "a".repeat(129);
9126        let c = caixa_with_autores(vec![too_long.as_str()]);
9127        let err = c.validate_autores().unwrap_err();
9128        let ManifestError::AutorInvalid { reason, .. } = err else {
9129            panic!("expected AutorInvalid, got {err:?}");
9130        };
9131        assert!(reason.contains("128"), "got: {reason}");
9132        assert!(reason.contains("129"), "got: {reason}");
9133    }
9134
9135    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
9136
9137    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
9138        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9139        c.repositorio = repositorio.map(String::from);
9140        c
9141    }
9142
9143    #[test]
9144    fn validate_repositorio_accepts_none() {
9145        // The omit-the-slot identity: `:repositorio` is optional. The
9146        // gate is a no-op when the author didn't declare a value —
9147        // every caixa without a `:repositorio` line trivially passes,
9148        // and the substrate-side renderers fall back to their
9149        // documented placeholder (`caixa-helm`'s `home: None`,
9150        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
9151        // URL). Mirrors the peer `validate_restart_window_accepts_none`
9152        // posture on the other `Option<String>` Caixa slot.
9153        let c = caixa_with_repositorio(None);
9154        c.validate_repositorio().unwrap();
9155    }
9156
9157    #[test]
9158    fn validate_repositorio_accepts_canonical_forms() {
9159        // Positive control sweep across every documented `:repositorio`
9160        // authoring shape — the same union the shared
9161        // `crate::render::is_git_repo_url` predicate accepts and the
9162        // peer `:deps :fonte :repo` axis already routes through.
9163        // Covers the `github:` shorthand (the canonical pleme-io
9164        // convention used in the `:repositorio` field of every
9165        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
9166        // `examples/`), the `https://…` URL the README quickstart uses,
9167        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
9168        // `file://` URL schemes the shared predicate documents.
9169        for repo in [
9170            "github:pleme-io/hello-rio",
9171            "github:pleme-io/checkout",
9172            "https://github.com/pleme-io/hello-rio",
9173            "ssh://git@github.com/pleme-io/hello-rio.git",
9174            "git://github.com/pleme-io/hello-rio.git",
9175            "git@github.com:pleme-io/hello-rio.git",
9176            "file:///srv/pleme/hello-rio",
9177        ] {
9178            let c = caixa_with_repositorio(Some(repo));
9179            c.validate_repositorio()
9180                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
9181        }
9182    }
9183
9184    #[test]
9185    fn validate_repositorio_rejects_empty_some() {
9186        // Canonical paste-from-blank-doc footgun. The narrower
9187        // [`ManifestError::RepositorioEmpty`] arm fires before the
9188        // shape predicate is consulted, mirroring the empty-first
9189        // cascade every peer per-axis identity gate uses
9190        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9191        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
9192        // the empty `Some("")` silently passed the renderer's
9193        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9194        // on `None`) and landed as `home: ""` in `Chart.yaml` /
9195        // `url: ""` in the FluxCD `GitRepository`.
9196        let c = caixa_with_repositorio(Some(""));
9197        let err = c.validate_repositorio().unwrap_err();
9198        assert!(
9199            matches!(err, ManifestError::RepositorioEmpty),
9200            "got {err:?}",
9201        );
9202    }
9203
9204    #[test]
9205    fn validate_repositorio_rejects_whitespace() {
9206        // Paste-from-doc whitespace footgun. The shared
9207        // `is_git_repo_url` predicate refuses any whitespace byte; a
9208        // trailing space in a `:repositorio` value silently broke
9209        // `git clone '<value> '` at clone time. The diagnostic names
9210        // the offending value verbatim.
9211        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
9212        let err = c.validate_repositorio().unwrap_err();
9213        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
9214            panic!("expected RepositorioInvalid, got {err:?}");
9215        };
9216        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
9217    }
9218
9219    #[test]
9220    fn validate_repositorio_rejects_control_char() {
9221        // Paste-from-multiline-doc CRLF footgun — control characters
9222        // at the URL boundary are a class of subprocess-arg injection
9223        // and break git's URL parser at every porcelain entry point.
9224        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
9225        let err = c.validate_repositorio().unwrap_err();
9226        assert!(
9227            matches!(err, ManifestError::RepositorioInvalid { .. }),
9228            "got {err:?}",
9229        );
9230    }
9231
9232    #[test]
9233    fn validate_repositorio_rejects_leading_dash() {
9234        // Canonical CLI-argument-injection footgun: `git clone <repo>`
9235        // interprets a leading `-` as a CLI flag, so a
9236        // `-upload-pack=…` value escapes the subprocess argument
9237        // boundary. The shared predicate refuses every leading-`-`
9238        // shape at validate time.
9239        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
9240        let err = c.validate_repositorio().unwrap_err();
9241        assert!(
9242            matches!(err, ManifestError::RepositorioInvalid { .. }),
9243            "got {err:?}",
9244        );
9245    }
9246
9247    #[test]
9248    fn validate_repositorio_rejects_missing_colon_separator() {
9249        // The bare `org/repo` ambiguity footgun — `git clone` reads
9250        // a no-`:` form as a relative filesystem path rather than the
9251        // GitHub-shorthand expansion the author probably intended.
9252        // The shared predicate refuses every shape without a `:`
9253        // separator.
9254        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9255        let err = c.validate_repositorio().unwrap_err();
9256        assert!(
9257            matches!(err, ManifestError::RepositorioInvalid { .. }),
9258            "got {err:?}",
9259        );
9260    }
9261
9262    #[test]
9263    fn validate_repositorio_rejects_fragment_anchor() {
9264        // Paste-from-browser-address-bar footgun on the
9265        // `:repositorio` axis — an author copies a GitHub permalink
9266        // to a README section / line-permalink and forgets to trim
9267        // the `#fragment` tail. The shared `is_git_repo_url`
9268        // predicate refuses the byte at the URL-grammar layer
9269        // (libcurl strips the fragment before opening the
9270        // transport, so the byte rides verbatim into the rendered
9271        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
9272        // fields but is silently dropped on the wire — two
9273        // manifest variants whose values differ only in their
9274        // fragment anchor lock to two distinct rendered artifacts
9275        // for the byte-identical clone, defeating the THEORY.md
9276        // §V.2 render-determinism contract on the `:repositorio`
9277        // axis the peer `:fonte :repo` axis already closes).
9278        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
9279        let err = c.validate_repositorio().unwrap_err();
9280        let ManifestError::RepositorioInvalid {
9281            repositorio,
9282            reason,
9283        } = err
9284        else {
9285            panic!("expected RepositorioInvalid, got {err:?}");
9286        };
9287        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
9288        assert!(
9289            reason.contains("must not contain `#`"),
9290            "reason must surface the fragment-`#` arm, got {reason:?}"
9291        );
9292    }
9293
9294    #[test]
9295    fn validate_repositorio_rejects_query_string() {
9296        // Paste-from-browser-address-bar footgun on the
9297        // `:repositorio` axis (peer with the a68f818 fragment-`#`
9298        // arm on the same axis). An author copies a GitHub tab
9299        // deep-link out of the address bar and forgets to trim
9300        // the `?tab=…` query tail. The shared `is_git_repo_url`
9301        // predicate refuses the byte at the URL-grammar layer
9302        // (GitHub / GitLab / Bitbucket silently ignore the
9303        // `?query` tail and serve the same repo regardless, so
9304        // the byte rides verbatim into the rendered `Chart.yaml`
9305        // `home:` and FluxCD `GitRepository` `url:` fields but
9306        // is silently masked at the wire — two manifest variants
9307        // whose values differ only in their query tail lock to
9308        // two distinct rendered artifacts for the byte-identical
9309        // clone, defeating the THEORY.md §V.2 render-determinism
9310        // contract on the `:repositorio` axis the peer `:fonte
9311        // :repo` axis already closes).
9312        let c = caixa_with_repositorio(Some(
9313            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
9314        ));
9315        let err = c.validate_repositorio().unwrap_err();
9316        let ManifestError::RepositorioInvalid {
9317            repositorio,
9318            reason,
9319        } = err
9320        else {
9321            panic!("expected RepositorioInvalid, got {err:?}");
9322        };
9323        assert_eq!(
9324            repositorio,
9325            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
9326        );
9327        assert!(
9328            reason.contains("must not contain `?`"),
9329            "reason must surface the query-`?` arm, got {reason:?}"
9330        );
9331    }
9332
9333    #[test]
9334    fn validate_repositorio_rejects_embedded_backslash() {
9335        // Windows-file-path-confusion footgun on the `:repositorio`
9336        // axis (peer with the prior fragment-`#` / query-`?` arms on
9337        // the same axis, and peer with the new dep-level `:fonte :repo`
9338        // backslash arm on the URL-grammar trajectory). An author
9339        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
9340        // hello-rio` into the `:repositorio` slot, expecting the
9341        // `lareira-<nome>` chart's `home:` field and the FluxCD
9342        // `GitRepository` `url:` field to render the canonical local
9343        // file-URI. The shared `is_git_repo_url` predicate refuses
9344        // the byte at the URL-grammar layer (libcurl silently
9345        // translates `\` → `/` on some platforms and refuses it on
9346        // others, so the byte rides verbatim into the rendered
9347        // artifacts but is silently rewritten or rejected at the wire
9348        // — two manifest variants whose values differ only in
9349        // backslash-vs-forward-slash lock to two distinct rendered
9350        // artifacts for the byte-identical clone, defeating the
9351        // THEORY.md §V.2 render-determinism contract on the
9352        // `:repositorio` axis the peer `:fonte :repo` axis already
9353        // closes).
9354        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
9355        let err = c.validate_repositorio().unwrap_err();
9356        let ManifestError::RepositorioInvalid {
9357            repositorio,
9358            reason,
9359        } = err
9360        else {
9361            panic!("expected RepositorioInvalid, got {err:?}");
9362        };
9363        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
9364        assert!(
9365            reason.contains("must not contain `\\`"),
9366            "reason must surface the backslash-`\\` arm, got {reason:?}"
9367        );
9368    }
9369
9370    #[test]
9371    fn validate_repositorio_rejects_uri_template_placeholder() {
9372        // URI Template (RFC 6570) placeholder footgun on the
9373        // `:repositorio` axis (peer with the prior fragment-`#` /
9374        // query-`?` / backslash-`\` arms on the same axis, and peer
9375        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
9376        // URL-grammar trajectory). An author pastes a quick-start
9377        // README snippet / OpenAPI `servers:` URL / Helm chart
9378        // `home:` template carrying unresolved `{org}` / `{repo}`
9379        // placeholders into the `:repositorio` slot, expecting the
9380        // substrate to resolve the placeholder downstream. The
9381        // shared `is_git_repo_url` predicate refuses the byte at the
9382        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
9383        // `%7B` / `%7D` on the wire, so the byte round-trips
9384        // inconsistently between the rendered `Chart.yaml home:` /
9385        // FluxCD `GitRepository url:` and the resolver's `git clone`
9386        // invocation, defeating the THEORY.md §V.2 render-
9387        // determinism contract on the `:repositorio` axis the peer
9388        // `:fonte :repo` axis already closes; every git porcelain
9389        // entry-point additionally fetches a nonexistent literal-
9390        // `{placeholder}`-named path far from the source caixa.lisp).
9391        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
9392        let err = c.validate_repositorio().unwrap_err();
9393        let ManifestError::RepositorioInvalid {
9394            repositorio,
9395            reason,
9396        } = err
9397        else {
9398            panic!("expected RepositorioInvalid, got {err:?}");
9399        };
9400        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
9401        assert!(
9402            reason.contains("must not contain `{`"),
9403            "reason must surface the open-brace `{{` arm, got {reason:?}"
9404        );
9405        assert!(
9406            reason.contains("URI Template") || reason.contains("RFC 6570"),
9407            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
9408        );
9409    }
9410
9411    #[test]
9412    fn validate_repositorio_empty_takes_precedence_over_shape() {
9413        // Empty-first cascade pin: the empty `Some("")` surfaces the
9414        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
9415        // `RepositorioInvalid`, mirroring the peer
9416        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9417        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
9418        // `is_git_repo_url` predicate also rejects the empty input
9419        // (defensively, with its own `"must not be empty"` reason),
9420        // but the manifest-layer empty arm runs first to surface the
9421        // narrower diagnostic verbatim.
9422        let c = caixa_with_repositorio(Some(""));
9423        let err = c.validate_repositorio().unwrap_err();
9424        assert!(
9425            matches!(err, ManifestError::RepositorioEmpty),
9426            "got {err:?}",
9427        );
9428    }
9429
9430    #[test]
9431    fn validate_repositorio_diagnostic_carries_offending_value() {
9432        // Diagnostic-shape pin (peer with
9433        // `validate_autores_diagnostic_carries_offending_author`): the
9434        // error's Display surfaces the offending value + slot name
9435        // verbatim, so a `feira lint` run can render the diagnostic
9436        // without re-parsing and the author can grep their caixa.lisp
9437        // for the offending `:repositorio` value.
9438        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9439        let rendered = c.validate_repositorio().unwrap_err().to_string();
9440        assert!(
9441            rendered.contains(":repositorio"),
9442            "diagnostic must name the offending slot: {rendered}",
9443        );
9444        assert!(
9445            rendered.contains("pleme-io/hello-rio"),
9446            "diagnostic must quote the offending value: {rendered}",
9447        );
9448    }
9449
9450    // ── validate_descricao — universal-axis Chart.yaml description shape ──
9451
9452    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
9453        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9454        c.descricao = descricao.map(String::from);
9455        c
9456    }
9457
9458    #[test]
9459    fn validate_descricao_accepts_none() {
9460        // The omit-the-slot identity: `:descricao` is optional. The
9461        // gate is a no-op when the author didn't declare a value —
9462        // every caixa without a `:descricao` line trivially passes,
9463        // and the substrate-side renderers fall back to their
9464        // documented `caixa.nome`-derived placeholder. Mirrors the
9465        // peer `validate_repositorio_accepts_none` posture on the
9466        // sibling `Option<String>` Caixa slot.
9467        let c = caixa_with_descricao(None);
9468        c.validate_descricao().unwrap();
9469    }
9470
9471    #[test]
9472    fn validate_descricao_accepts_canonical_summary() {
9473        // Positive control: the canonical pleme-io descricao shape —
9474        // a short free-form prose summary — passes the gate. Covers
9475        // the fixture shapes the `caixa-helm` / `caixa-flux` /
9476        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
9477        // wasip2 caixa Servico."`, `"Checkout flow."`).
9478        for desc in [
9479            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9480            "Checkout flow.",
9481            "AWS provider caixa for tatara-lisp",
9482            "FIXME — describe this caixa",
9483            "x",
9484        ] {
9485            let c = caixa_with_descricao(Some(desc));
9486            c.validate_descricao()
9487                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
9488        }
9489    }
9490
9491    #[test]
9492    fn validate_descricao_rejects_empty_some() {
9493        // Canonical paste-from-blank-doc footgun. Without this gate
9494        // the empty `Some("")` silently passed the renderer's
9495        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9496        // on `None`) and landed as `description: ""` in `Chart.yaml`
9497        // and a blank `README.md` header. Mirrors the peer
9498        // [`ManifestError::RepositorioEmpty`] empty-arm on the
9499        // sibling `Option<String>` Caixa slot.
9500        let c = caixa_with_descricao(Some(""));
9501        let err = c.validate_descricao().unwrap_err();
9502        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9503    }
9504
9505    #[test]
9506    fn validate_descricao_rejects_leading_whitespace() {
9507        // Paste-from-aligned-doc footgun: a leading ASCII space the
9508        // bare empty-arm gate accepted, the shape predicate now
9509        // refuses. The diagnostic carries the offending value
9510        // verbatim (with the leading space preserved) so the author
9511        // can grep their caixa.lisp for the exact `:descricao` line
9512        // and fix the round-trip-inconsistent leading whitespace.
9513        // Mirrors the peer
9514        // `validate_licenca_rejects_leading_whitespace` arm on the
9515        // sibling `:licenca` axis.
9516        let c = caixa_with_descricao(Some(" Checkout flow."));
9517        let err = c.validate_descricao().unwrap_err();
9518        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9519            panic!("expected DescricaoInvalid, got {err:?}");
9520        };
9521        assert_eq!(descricao, " Checkout flow.");
9522        assert!(reason.contains("whitespace"), "got: {reason:?}");
9523    }
9524
9525    #[test]
9526    fn validate_descricao_rejects_trailing_whitespace() {
9527        // Paste-from-doc footgun: a trailing ASCII space the bare
9528        // empty-arm gate accepted, the shape predicate now refuses.
9529        let c = caixa_with_descricao(Some("Checkout flow. "));
9530        let err = c.validate_descricao().unwrap_err();
9531        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9532            panic!("expected DescricaoInvalid, got {err:?}");
9533        };
9534        assert_eq!(descricao, "Checkout flow. ");
9535        assert!(reason.contains("whitespace"), "got: {reason:?}");
9536    }
9537
9538    #[test]
9539    fn validate_descricao_rejects_embedded_newline() {
9540        // Paste-from-multiline-doc footgun: an embedded LF the bare
9541        // empty-arm gate accepted, the shape predicate now refuses.
9542        // Without this gate the embedded newline silently landed in
9543        // the rendered Chart.yaml as a multi-line YAML block scalar,
9544        // and every chart-aware UI (`helm list`, `helm search`,
9545        // Artifact Hub) renders the description in a single-line
9546        // column so the embedded newline is silently dropped at
9547        // every downstream consumer.
9548        let c = caixa_with_descricao(Some("Checkout\nflow."));
9549        let err = c.validate_descricao().unwrap_err();
9550        assert!(
9551            matches!(err, ManifestError::DescricaoInvalid { .. }),
9552            "got {err:?}",
9553        );
9554        assert!(err.to_string().contains("newline"), "got {err}");
9555    }
9556
9557    #[test]
9558    fn validate_descricao_rejects_embedded_carriage_return() {
9559        // Paste-from-Windows-CRLF-doc footgun.
9560        let c = caixa_with_descricao(Some("Checkout\rflow."));
9561        let err = c.validate_descricao().unwrap_err();
9562        assert!(
9563            matches!(err, ManifestError::DescricaoInvalid { .. }),
9564            "got {err:?}",
9565        );
9566        assert!(err.to_string().contains("carriage return"), "got {err}");
9567    }
9568
9569    #[test]
9570    fn validate_descricao_rejects_embedded_tab() {
9571        // Tab-from-aligned-doc footgun.
9572        let c = caixa_with_descricao(Some("Checkout\tflow."));
9573        let err = c.validate_descricao().unwrap_err();
9574        assert!(
9575            matches!(err, ManifestError::DescricaoInvalid { .. }),
9576            "got {err:?}",
9577        );
9578        assert!(err.to_string().contains("tab"), "got {err}");
9579    }
9580
9581    #[test]
9582    fn validate_descricao_rejects_embedded_control_bytes() {
9583        // Paste-from-binary-blob footgun: every other control byte
9584        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
9585        // the peer SPDX-expression control-byte arm.
9586        for s in [
9587            "Checkout\x00flow.",
9588            "Checkout\x07flow.",
9589            "Checkout\x1bflow.",
9590            "Checkout\x7fflow.",
9591        ] {
9592            let c = caixa_with_descricao(Some(s));
9593            let err = c.validate_descricao().unwrap_err();
9594            assert!(
9595                matches!(err, ManifestError::DescricaoInvalid { .. }),
9596                "{s:?} got {err:?}",
9597            );
9598            assert!(
9599                err.to_string().contains("control character"),
9600                "{s:?} got {err}",
9601            );
9602        }
9603    }
9604
9605    #[test]
9606    fn validate_descricao_accepts_unicode_prose() {
9607        // Positive control: Unicode prose is accepted — the
9608        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
9609        // and `Caixa::template`'s `"FIXME — describe this caixa"`
9610        // scaffold every `feira init` emits must continue to pass.
9611        for s in [
9612            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9613            "FIXME — describe this caixa",
9614            "Caixa pour le projet tâche",
9615            "日本語の説明",
9616        ] {
9617            let c = caixa_with_descricao(Some(s));
9618            c.validate_descricao()
9619                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
9620        }
9621    }
9622
9623    #[test]
9624    fn validate_descricao_empty_takes_precedence_over_shape() {
9625        // Cascade pin: a `Some("")` surfaces the narrower
9626        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
9627        // shape-predicate arm. Mirrors the peer
9628        // `validate_licenca_empty_takes_precedence_over_shape` pin
9629        // on the sibling `:licenca` axis.
9630        let c = caixa_with_descricao(Some(""));
9631        let err = c.validate_descricao().unwrap_err();
9632        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9633    }
9634
9635    #[test]
9636    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
9637        // Diagnostic-shape pin: the error's Display surfaces both
9638        // the `:descricao` slot name and the offending value
9639        // verbatim, so a `feira lint` run can render the diagnostic
9640        // without re-parsing and the author can grep their caixa.lisp
9641        // for the offending `:descricao` line. Mirrors the peer
9642        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
9643        // pin (ee2e888) on the sibling `:licenca` axis.
9644        // The `{descricao:?}` Debug format escapes embedded control
9645        // bytes; the quoted offending value surfaces as
9646        // `"Checkout\nflow."` (literal backslash-n) in the rendered
9647        // diagnostic. The author can grep their caixa.lisp for the
9648        // literal `Checkout` summary prefix.
9649        let c = caixa_with_descricao(Some("Checkout\nflow."));
9650        let rendered = c.validate_descricao().unwrap_err().to_string();
9651        assert!(
9652            rendered.contains(":descricao"),
9653            "diagnostic must name the offending slot: {rendered}",
9654        );
9655        assert!(
9656            rendered.contains("Checkout\\nflow."),
9657            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9658        );
9659    }
9660
9661    #[test]
9662    fn validate_descricao_template_passes() {
9663        // Round-trip pin: the bare `Caixa::template` shape carries
9664        // `:descricao "FIXME — describe this caixa"` (a non-empty
9665        // sentinel), so the template-derived Caixa passes the gate by
9666        // construction. A future template-shape change that omits or
9667        // empties `:descricao` would surface here as a regression.
9668        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9669        c.validate_descricao().unwrap();
9670    }
9671
9672    #[test]
9673    fn validate_descricao_diagnostic_names_offending_slot() {
9674        // Diagnostic-shape pin (peer with
9675        // `validate_repositorio_diagnostic_carries_offending_value`):
9676        // the error's Display surfaces the `:descricao` slot name
9677        // verbatim, so a `feira lint` run can render the diagnostic
9678        // without re-parsing and the author can grep their caixa.lisp
9679        // for the offending `:descricao` line.
9680        let c = caixa_with_descricao(Some(""));
9681        let rendered = c.validate_descricao().unwrap_err().to_string();
9682        assert!(
9683            rendered.contains(":descricao"),
9684            "diagnostic must name the offending slot: {rendered}",
9685        );
9686    }
9687
9688    // ── validate_licenca — universal-axis chart README license shape ──
9689
9690    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
9691        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9692        c.licenca = licenca.map(String::from);
9693        c
9694    }
9695
9696    #[test]
9697    fn validate_licenca_accepts_none() {
9698        // The omit-the-slot identity: `:licenca` is optional. The
9699        // gate is a no-op when the author didn't declare a value —
9700        // every caixa without a `:licenca` line trivially passes,
9701        // and the substrate-side `caixa-helm` renderer falls back to
9702        // the documented `"MIT"` placeholder. Mirrors the peer
9703        // `validate_descricao_accepts_none` posture on the sibling
9704        // `Option<String>` Caixa slot.
9705        let c = caixa_with_licenca(None);
9706        c.validate_licenca().unwrap();
9707    }
9708
9709    #[test]
9710    fn validate_licenca_accepts_canonical_expressions() {
9711        // Positive control: every canonical SPDX expression shape
9712        // pleme-io carries in its existing fixtures + the canonical
9713        // SPDX dual-license / with-exception / `+`-suffix / grouped /
9714        // user-defined-reference shapes all pass the gate. Covers
9715        // the single-license, `OR`-compound, `AND`-compound,
9716        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
9717        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
9718        // production the SPDX 2.1 expression grammar admits that
9719        // sits within the alphabet floor the
9720        // `is_spdx_expression_shape` predicate enforces.
9721        for lic in [
9722            "MIT",
9723            "Apache-2.0",
9724            "Apache-2.0 OR MIT",
9725            "Apache-2.0 AND MIT",
9726            "BSD-3-Clause",
9727            "MPL-2.0",
9728            "GPL-3.0-or-later",
9729            "GPL-2.0+",
9730            "Apache-2.0 WITH LLVM-exception",
9731            "(MIT OR Apache-2.0) AND BSD-3-Clause",
9732            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
9733            "LicenseRef-MyLicense",
9734            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
9735            "x",
9736        ] {
9737            let c = caixa_with_licenca(Some(lic));
9738            c.validate_licenca()
9739                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
9740        }
9741    }
9742
9743    #[test]
9744    fn validate_licenca_rejects_trailing_whitespace() {
9745        // Paste-from-doc whitespace footgun. A trailing space in the
9746        // `:licenca` value would silently break a downstream SPDX
9747        // parser that splits on exact `AND` / `OR` / `WITH` keyword
9748        // boundaries. The shape predicate refuses every trailing
9749        // whitespace byte by construction. Peer with
9750        // `validate_repositorio_rejects_whitespace` and
9751        // `validate_edicao_rejects_trailing_whitespace`.
9752        let c = caixa_with_licenca(Some("MIT "));
9753        let err = c.validate_licenca().unwrap_err();
9754        let ManifestError::LicencaInvalid { licenca, .. } = err else {
9755            panic!("expected LicencaInvalid, got {err:?}");
9756        };
9757        assert_eq!(licenca, "MIT ");
9758    }
9759
9760    #[test]
9761    fn validate_licenca_rejects_leading_whitespace() {
9762        // Symmetric paste-from-doc whitespace footgun on the leading
9763        // boundary — the gate refuses every shape that starts with a
9764        // space byte by construction. Peer with
9765        // `validate_edicao_rejects_leading_whitespace`.
9766        let c = caixa_with_licenca(Some(" MIT"));
9767        let err = c.validate_licenca().unwrap_err();
9768        assert!(
9769            matches!(err, ManifestError::LicencaInvalid { .. }),
9770            "got {err:?}",
9771        );
9772    }
9773
9774    #[test]
9775    fn validate_licenca_rejects_control_char() {
9776        // Paste-from-multiline-doc CRLF footgun — control characters
9777        // at the value boundary land as a malformed line in the
9778        // rendered chart `README.md` `## License` section. Peer with
9779        // `validate_repositorio_rejects_control_char` and
9780        // `validate_edicao_rejects_control_char`.
9781        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
9782            let c = caixa_with_licenca(Some(lic));
9783            let err = c.validate_licenca().unwrap_err();
9784            assert!(
9785                matches!(err, ManifestError::LicencaInvalid { .. }),
9786                "expected LicencaInvalid on {lic:?}, got {err:?}",
9787            );
9788        }
9789    }
9790
9791    #[test]
9792    fn validate_licenca_rejects_tab() {
9793        // Tab-from-aligned-doc footgun — SPDX expressions use a
9794        // single ASCII space between tokens; a tab breaks every
9795        // downstream SPDX parser that splits on exact `" "`
9796        // boundaries.
9797        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
9798        let err = c.validate_licenca().unwrap_err();
9799        assert!(
9800            matches!(err, ManifestError::LicencaInvalid { .. }),
9801            "got {err:?}",
9802        );
9803    }
9804
9805    #[test]
9806    fn validate_licenca_rejects_non_ascii() {
9807        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
9808        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
9809        // ".")` production. The shape predicate refuses every
9810        // non-ASCII byte by construction; peer with
9811        // `validate_edicao_rejects_non_ascii_lookalike`.
9812        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
9813            let c = caixa_with_licenca(Some(lic));
9814            let err = c.validate_licenca().unwrap_err();
9815            assert!(
9816                matches!(err, ManifestError::LicencaInvalid { .. }),
9817                "expected LicencaInvalid on {lic:?}, got {err:?}",
9818            );
9819        }
9820    }
9821
9822    #[test]
9823    fn validate_licenca_rejects_underscore() {
9824        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
9825        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
9826        // snake-case identifier conventions that don't apply to the
9827        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
9828        // "-" / "."`). The shape predicate refuses every underscore
9829        // byte by construction.
9830        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
9831            let c = caixa_with_licenca(Some(lic));
9832            let err = c.validate_licenca().unwrap_err();
9833            assert!(
9834                matches!(err, ManifestError::LicencaInvalid { .. }),
9835                "expected LicencaInvalid on {lic:?}, got {err:?}",
9836            );
9837        }
9838    }
9839
9840    #[test]
9841    fn validate_licenca_rejects_comma_separator() {
9842        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
9843        // SPDX expressions compose multiple licenses via `AND` / `OR`
9844        // keywords, not the comma separator. The shape predicate
9845        // refuses every comma byte by construction.
9846        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
9847            let c = caixa_with_licenca(Some(lic));
9848            let err = c.validate_licenca().unwrap_err();
9849            assert!(
9850                matches!(err, ManifestError::LicencaInvalid { .. }),
9851                "expected LicencaInvalid on {lic:?}, got {err:?}",
9852            );
9853        }
9854    }
9855
9856    #[test]
9857    fn validate_licenca_rejects_slash_dual_license() {
9858        // Slash-dual-license colloquial idiom footgun — the
9859        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
9860        // `package.license` field but non-SPDX; the SPDX equivalent
9861        // is `MIT OR Apache-2.0`. The shape predicate refuses every
9862        // forward-slash byte by construction.
9863        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
9864            let c = caixa_with_licenca(Some(lic));
9865            let err = c.validate_licenca().unwrap_err();
9866            assert!(
9867                matches!(err, ManifestError::LicencaInvalid { .. }),
9868                "expected LicencaInvalid on {lic:?}, got {err:?}",
9869            );
9870        }
9871    }
9872
9873    #[test]
9874    fn validate_licenca_rejects_semicolon_separator() {
9875        // Semicolon-list-separator confusion footgun — adjacent to
9876        // the comma-separator idiom, every list-separator-belongs-
9877        // to-list-grammar confusion lands here.
9878        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
9879        let err = c.validate_licenca().unwrap_err();
9880        assert!(
9881            matches!(err, ManifestError::LicencaInvalid { .. }),
9882            "got {err:?}",
9883        );
9884    }
9885
9886    #[test]
9887    fn validate_licenca_empty_takes_precedence_over_shape() {
9888        // Empty-first cascade pin: the empty `Some("")` surfaces the
9889        // narrower `LicencaEmpty` not the shape-predicate-wrapped
9890        // `LicencaInvalid`, mirroring the peer
9891        // `validate_edicao_empty_takes_precedence_over_shape` and
9892        // `validate_repositorio_empty_takes_precedence_over_shape`
9893        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
9894        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
9895        // The shape predicate also refuses the empty input
9896        // (defensively — `"must not be empty"`), but the manifest-
9897        // layer empty arm runs first to surface the narrower
9898        // diagnostic verbatim.
9899        let c = caixa_with_licenca(Some(""));
9900        let err = c.validate_licenca().unwrap_err();
9901        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
9902    }
9903
9904    #[test]
9905    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
9906        // Diagnostic-shape pin on the shape-predicate arm (peer with
9907        // `validate_edicao_invalid_diagnostic_carries_offending_value`
9908        // and `validate_repositorio_diagnostic_carries_offending_value`):
9909        // the error's Display surfaces the offending value + slot
9910        // name verbatim, so a `feira lint` run can render the
9911        // diagnostic without re-parsing and the author can grep
9912        // their caixa.lisp for the offending `:licenca` value.
9913        let c = caixa_with_licenca(Some("Apache_2.0"));
9914        let rendered = c.validate_licenca().unwrap_err().to_string();
9915        assert!(
9916            rendered.contains(":licenca"),
9917            "diagnostic must name the offending slot: {rendered}",
9918        );
9919        assert!(
9920            rendered.contains("Apache_2.0"),
9921            "diagnostic must quote the offending value: {rendered}",
9922        );
9923    }
9924
9925    #[test]
9926    fn validate_licenca_rejects_empty_some() {
9927        // Canonical paste-from-blank-doc footgun. Without this gate
9928        // the empty `Some("")` silently passed the renderer's
9929        // `Option::unwrap_or_else(|| "MIT".into())` (which only
9930        // fires on `None`) and landed as a bare trailing period in
9931        // the rendered chart `README.md` `## License` section.
9932        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
9933        // arm on the sibling `Option<String>` Caixa slot.
9934        let c = caixa_with_licenca(Some(""));
9935        let err = c.validate_licenca().unwrap_err();
9936        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
9937    }
9938
9939    #[test]
9940    fn validate_licenca_template_passes() {
9941        // Round-trip pin: the bare `Caixa::template` shape (whether
9942        // it carries `:licenca` or omits it) passes the gate by
9943        // construction. A future template-shape change that
9944        // introduced `(:licenca "")` would surface here as a
9945        // regression. Mirrors the peer
9946        // `validate_descricao_template_passes` pin.
9947        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9948        c.validate_licenca().unwrap();
9949    }
9950
9951    #[test]
9952    fn validate_licenca_diagnostic_names_offending_slot() {
9953        // Diagnostic-shape pin (peer with
9954        // `validate_descricao_diagnostic_names_offending_slot`):
9955        // the error's Display surfaces the `:licenca` slot name
9956        // verbatim, so a `feira lint` run can render the diagnostic
9957        // without re-parsing and the author can grep their caixa.lisp
9958        // for the offending `:licenca` line.
9959        let c = caixa_with_licenca(Some(""));
9960        let rendered = c.validate_licenca().unwrap_err().to_string();
9961        assert!(
9962            rendered.contains(":licenca"),
9963            "diagnostic must name the offending slot: {rendered}",
9964        );
9965    }
9966
9967    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
9968
9969    #[test]
9970    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
9971        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
9972        // pin: [`Caixa::licenca`] must return the `:licenca` typed
9973        // byte-string verbatim as an `Option<&str>`, byte-equal to the
9974        // raw `self.licenca.as_deref()` access across every
9975        // representative value in the accept-set — `None` (the "omit
9976        // the slot to defer to the caixa-helm renderer's `MIT`
9977        // fallback" arm every existing fixture without a `:licenca`
9978        // line carries), `Some("")` (a past-the-guard sentinel that
9979        // pins the accessor doesn't perform a silent
9980        // `Some("") → None` collapse on the empty arm — validate
9981        // rejects `Some("")` through `LicencaEmpty` but the accessor
9982        // must ship the raw slot verbatim so a validate-time gate
9983        // regression surfaces at the caixa-helm emit boundary rather
9984        // than being silently absorbed into the fallback), `Some("MIT")`
9985        // (the canonical single-license shape every `feira init`
9986        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
9987        // canonical `OR`-compound shape the peer
9988        // `validate_licenca_accepts_canonical_expressions` positive
9989        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
9990        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
9991        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
9992        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
9993        // guard sentinels — validate rejects each through
9994        // `LicencaInvalid` but the accessor must ship the raw slot
9995        // verbatim).
9996        //
9997        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
9998        // accessor pin on the substrate primitive — opens the "outer
9999        // [`Caixa`] `Option<&str>` scalar" projection pattern the
10000        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
10001        // future lifts fold on. Sibling in shape to the peer per-`:placement`
10002        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10003        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10004        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10005        // axes, extended onto the outer top-level [`Caixa`] universal-
10006        // axis surface. Pins against a future silent detour that
10007        // returned an owned `Option<String>` (which would type-check
10008        // but silently allocate on every accessor call, breaking the
10009        // zero-cost projection every peer sibling accessor carries), a
10010        // `Some("") → None` collapse (which would silently absorb the
10011        // `LicencaEmpty` refusal case at the accessor boundary and the
10012        // caixa-helm emit path would silently fall back to `"MIT"` on
10013        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
10014        // `None → Some("MIT")` collapse (which would silently reify
10015        // the caixa-helm renderer's `"MIT"` fallback at the accessor
10016        // boundary and every downstream consumer keying off the
10017        // `Option::is_none()` discriminator would lose the "author
10018        // omitted the slot" signal).
10019        for licenca in [
10020            None,
10021            Some(""),
10022            Some("MIT"),
10023            Some("Apache-2.0 OR MIT"),
10024            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
10025            Some("MIT "),
10026            Some(" MIT"),
10027            Some("MIT\n"),
10028            Some("Apache_2.0"),
10029            Some("MIT,Apache-2.0"),
10030        ] {
10031            let c = caixa_with_licenca(licenca);
10032            assert_eq!(
10033                c.licenca(),
10034                licenca,
10035                "Caixa::licenca must return :licenca verbatim (got {:?}, \
10036                 expected {licenca:?})",
10037                c.licenca(),
10038            );
10039            assert_eq!(
10040                c.licenca(),
10041                c.licenca.as_deref(),
10042                "Caixa::licenca must byte-equal the raw \
10043                 `self.licenca.as_deref()` field access across every \
10044                 value in the Option<&str> accept-set",
10045            );
10046        }
10047    }
10048
10049    #[test]
10050    fn validate_licenca_empty_arm_routes_through_accessor() {
10051        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
10052        // must key off [`Caixa::licenca`], not the raw
10053        // `self.licenca.as_deref()` field access. Structurally: a
10054        // `Caixa { licenca: Some(""), .. }` must surface the
10055        // `LicencaEmpty` refusal exactly, and a
10056        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
10057        // single-license form) must pass validate. The pair jointly
10058        // pins the accessor + validate-gate composition: any future
10059        // silent detour that had the accessor return `None` on the
10060        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10061        // silently absorb the `LicencaEmpty` refusal at the accessor
10062        // boundary and the validate gate would accept a struct-literal
10063        // `Caixa { licenca: Some(""), .. }` — the composition pin
10064        // catches that at caixa-core build time.
10065        //
10066        // Peer of the per-`:politicas :circuit-breaker`
10067        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
10068        // accessor-composition pin
10069        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
10070        // on the sibling per-M3-mesh-slot required-`u32` axis — same
10071        // "the validate / shape-gate predicate must route through the
10072        // substrate-primitive typed dispatch" discipline extended onto
10073        // the outer top-level [`Caixa`] universal-axis
10074        // `Option<&str>`-composition surface.
10075        let c = caixa_with_licenca(Some(""));
10076        assert!(
10077            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
10078            "validate_licenca must reject licenca == Some(\"\") with \
10079             LicencaEmpty — the accessor and the validate gate must \
10080             route through the same substrate-primitive typed dispatch \
10081             on the :licenca empty arm",
10082        );
10083        let c = caixa_with_licenca(Some("MIT"));
10084        assert!(
10085            c.validate_licenca().is_ok(),
10086            "validate_licenca must accept licenca == Some(\"MIT\") \
10087             (the canonical single-license SPDX shape)",
10088        );
10089    }
10090
10091    #[test]
10092    fn licenca_projects_option_str_by_borrow() {
10093        // The by-borrow pin: [`Caixa::licenca`] returns
10094        // `Option<&str>` by borrow — the `&str` borrows the underlying
10095        // `String` storage of the `Option<String>` slot and the
10096        // accessor must not allocate a fresh `String` on every call.
10097        // Peer of the per-`:placement`
10098        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10099        // borrow pin on the peer per-M3-mesh-slot
10100        // `Option<&str>`-return axis, extended onto the outer top-
10101        // level [`Caixa`] universal-axis `Option<&str>` shape — the
10102        // accessor's returned `&str` must borrow from `&self` (the
10103        // returned reference's lifetime is tied to `&self`), and
10104        // calling the accessor twice on the same [`Caixa`] must yield
10105        // the same `Option<&str>` verbatim (idempotent, no side
10106        // effects on `&self`).
10107        //
10108        // Pins against a future silent detour that returned an owned
10109        // `Option<String>` (which would type-check but silently
10110        // allocate on every call, breaking the zero-cost projection
10111        // every peer sibling accessor carries), or a one-arm-only
10112        // accessor that returned a saturating value on some sentinel
10113        // input (breaking the pass-through invariant the sibling
10114        // required-scalar accessors carry).
10115        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
10116            let c = caixa_with_licenca(licenca);
10117            let first = c.licenca();
10118            let second = c.licenca();
10119            assert_eq!(
10120                first, second,
10121                "Caixa::licenca must be idempotent — two successive \
10122                 calls on the same &self must return the same \
10123                 Option<&str>",
10124            );
10125            assert_eq!(
10126                first, licenca,
10127                "Caixa::licenca must return :licenca verbatim by \
10128                 borrow — got {first:?}, expected {licenca:?}",
10129            );
10130        }
10131    }
10132
10133    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
10134
10135    #[test]
10136    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
10137        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
10138        // pin: [`Caixa::repositorio`] must return the `:repositorio`
10139        // typed byte-string verbatim as an `Option<&str>`, byte-equal
10140        // to the raw `self.repositorio.as_deref()` access across every
10141        // representative value in the accept-set — `None` (the "omit
10142        // the slot to defer to the per-renderer placeholder" arm every
10143        // existing fixture without a `:repositorio` line carries),
10144        // `Some("")` (a past-the-guard sentinel that pins the accessor
10145        // doesn't perform a silent `Some("") → None` collapse on the
10146        // empty arm — validate rejects `Some("")` through
10147        // `RepositorioEmpty` but the accessor must ship the raw slot
10148        // verbatim so a validate-time gate regression surfaces at the
10149        // caixa-helm / caixa-flux emit boundary rather than being
10150        // silently absorbed into the per-renderer fallback),
10151        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
10152        // shorthand every existing manifest fixture across
10153        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
10154        // `Some("https://github.com/pleme-io/checkout")` (the canonical
10155        // `https://` URL the README quickstart uses),
10156        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
10157        // `Some("git://github.com/pleme-io/checkout.git")` /
10158        // `Some("git@github.com:pleme-io/checkout.git")` /
10159        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
10160        // github scheme the shared `is_git_repo_url` predicate
10161        // documents), and five past-the-guard sentinels for the
10162        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
10163        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
10164        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
10165        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
10166        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
10167        // sentinels pin the accessor doesn't silently absorb the
10168        // refusal cases into a fallback).
10169        //
10170        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
10171        // accessor pin on the substrate primitive — sibling of the peer
10172        // [`Caixa::licenca`] (6d5bc28) pin
10173        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
10174        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
10175        // projection pin pattern this pin folds on. Sibling in shape to
10176        // the peer per-`:placement`
10177        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10178        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10179        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10180        // axes, extended onto the outer top-level [`Caixa`] universal-
10181        // axis surface. Pins against a future silent detour that
10182        // returned an owned `Option<String>` (which would type-check
10183        // but silently allocate on every accessor call, breaking the
10184        // zero-cost projection every peer sibling accessor carries), a
10185        // `Some("") → None` collapse (which would silently absorb the
10186        // `RepositorioEmpty` refusal case at the accessor boundary and
10187        // the caixa-helm `Chart.yaml` `home:` fold would silently
10188        // render a `home: null` / omitted field on a struct-literal
10189        // `Caixa { repositorio: Some(""), .. }`), or a
10190        // `None → Some(<default>)` collapse (which would silently reify
10191        // the per-renderer fallback at the accessor boundary and every
10192        // downstream consumer keying off the `Option::is_none()`
10193        // discriminator would lose the "author omitted the slot"
10194        // signal).
10195        for repositorio in [
10196            None,
10197            Some(""),
10198            Some("github:pleme-io/hello-rio"),
10199            Some("https://github.com/pleme-io/checkout"),
10200            Some("ssh://git@github.com/pleme-io/checkout.git"),
10201            Some("git://github.com/pleme-io/checkout.git"),
10202            Some("git@github.com:pleme-io/checkout.git"),
10203            Some("file:///opt/mirrors/pleme-io/checkout"),
10204            Some("pleme-io/checkout"),
10205            Some("-upload-pack=evil"),
10206            Some("github:pleme-io/checkout?ref=main"),
10207            Some("github:pleme-io/checkout#main"),
10208            Some("github:pleme-io/{tpl}"),
10209        ] {
10210            let c = caixa_with_repositorio(repositorio);
10211            assert_eq!(
10212                c.repositorio(),
10213                repositorio,
10214                "Caixa::repositorio must return :repositorio verbatim \
10215                 (got {:?}, expected {repositorio:?})",
10216                c.repositorio(),
10217            );
10218            assert_eq!(
10219                c.repositorio(),
10220                c.repositorio.as_deref(),
10221                "Caixa::repositorio must byte-equal the raw \
10222                 `self.repositorio.as_deref()` field access across every \
10223                 value in the Option<&str> accept-set",
10224            );
10225        }
10226    }
10227
10228    #[test]
10229    fn validate_repositorio_empty_arm_routes_through_accessor() {
10230        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
10231        // gate must key off [`Caixa::repositorio`], not the raw
10232        // `self.repositorio.as_deref()` field access. Structurally: a
10233        // `Caixa { repositorio: Some(""), .. }` must surface the
10234        // `RepositorioEmpty` refusal exactly, and a
10235        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
10236        // (the canonical `github:` shorthand form) must pass validate.
10237        // The pair jointly pins the accessor + validate-gate
10238        // composition: any future silent detour that had the accessor
10239        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
10240        // collapse) would silently absorb the `RepositorioEmpty` refusal
10241        // at the accessor boundary and the validate gate would accept a
10242        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
10243        // composition pin catches that at caixa-core build time.
10244        //
10245        // Peer of the [`Caixa::licenca`] (6d5bc28)
10246        // `validate_licenca_empty_arm_routes_through_accessor`
10247        // composition pin on the sibling outer top-level [`Caixa`]
10248        // `Option<&str>` universal-axis surface — same "the validate /
10249        // shape-gate predicate must route through the substrate-
10250        // primitive typed dispatch" discipline extended onto the second
10251        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10252        // composition surface.
10253        let c = caixa_with_repositorio(Some(""));
10254        assert!(
10255            matches!(
10256                c.validate_repositorio(),
10257                Err(ManifestError::RepositorioEmpty),
10258            ),
10259            "validate_repositorio must reject repositorio == Some(\"\") \
10260             with RepositorioEmpty — the accessor and the validate gate \
10261             must route through the same substrate-primitive typed \
10262             dispatch on the :repositorio empty arm",
10263        );
10264        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
10265        assert!(
10266            c.validate_repositorio().is_ok(),
10267            "validate_repositorio must accept repositorio == \
10268             Some(\"github:pleme-io/hello-rio\") (the canonical \
10269             `github:` shorthand git-repo-URL shape)",
10270        );
10271    }
10272
10273    #[test]
10274    fn repositorio_projects_option_str_by_borrow() {
10275        // The by-borrow pin: [`Caixa::repositorio`] returns
10276        // `Option<&str>` by borrow — the `&str` borrows the underlying
10277        // `String` storage of the `Option<String>` slot and the
10278        // accessor must not allocate a fresh `String` on every call.
10279        // Peer of the per-`:placement`
10280        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
10281        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
10282        // `Option<&str>`-return axes, extended onto the second outer
10283        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
10284        // the accessor's returned `&str` must borrow from `&self` (the
10285        // returned reference's lifetime is tied to `&self`), and
10286        // calling the accessor twice on the same [`Caixa`] must yield
10287        // the same `Option<&str>` verbatim (idempotent, no side effects
10288        // on `&self`).
10289        //
10290        // Pins against a future silent detour that returned an owned
10291        // `Option<String>` (which would type-check but silently
10292        // allocate on every call, breaking the zero-cost projection
10293        // every peer sibling accessor carries), or a one-arm-only
10294        // accessor that returned a saturating value on some sentinel
10295        // input (breaking the pass-through invariant the sibling
10296        // required-scalar accessors carry).
10297        for repositorio in [
10298            None,
10299            Some(""),
10300            Some("github:pleme-io/hello-rio"),
10301            Some("https://github.com/pleme-io/checkout"),
10302        ] {
10303            let c = caixa_with_repositorio(repositorio);
10304            let first = c.repositorio();
10305            let second = c.repositorio();
10306            assert_eq!(
10307                first, second,
10308                "Caixa::repositorio must be idempotent — two successive \
10309                 calls on the same &self must return the same \
10310                 Option<&str>",
10311            );
10312            assert_eq!(
10313                first, repositorio,
10314                "Caixa::repositorio must return :repositorio verbatim by \
10315                 borrow — got {first:?}, expected {repositorio:?}",
10316            );
10317        }
10318    }
10319
10320    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
10321
10322    #[test]
10323    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
10324        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
10325        // pin: [`Caixa::descricao`] must return the `:descricao` typed
10326        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10327        // raw `self.descricao.as_deref()` access across every
10328        // representative value in the accept-set — `None` (the "omit
10329        // the slot to defer to the per-renderer `caixa.nome`-derived
10330        // fallback" arm every existing fixture without a `:descricao`
10331        // line carries), `Some("")` (a past-the-guard sentinel that
10332        // pins the accessor doesn't perform a silent `Some("") → None`
10333        // collapse on the empty arm — validate rejects `Some("")`
10334        // through `DescricaoEmpty` but the accessor must ship the raw
10335        // slot verbatim so a validate-time gate regression surfaces at
10336        // the caixa-helm / caixa-feira emit boundary rather than being
10337        // silently absorbed into the per-renderer `caixa.nome`-derived
10338        // fallback), `Some("Checkout flow.")` (the canonical one-line
10339        // prose descriptor the peer
10340        // `validate_descricao_accepts_canonical_value` positive sweep
10341        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
10342        // Servico.")` (the multi-byte Unicode continuation-byte shape
10343        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
10344        // multi-glyph Unicode shape the peer
10345        // `is_chart_description_shape` predicate accepts), and five
10346        // past-the-guard sentinels for the `DescricaoInvalid` refusal
10347        // cases (`Some(" Checkout flow.")` leading-whitespace,
10348        // `Some("Checkout flow. ")` trailing-whitespace,
10349        // `Some("Checkout\nflow.")` embedded-LF,
10350        // `Some("Checkout\tflow.")` embedded-TAB, and
10351        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
10352        // the accessor doesn't silently absorb the refusal cases into
10353        // a fallback).
10354        //
10355        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
10356        // accessor pin on the substrate primitive — sibling of the peer
10357        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
10358        // (cc7332d) pins that opened the "outer [`Caixa`]
10359        // `Option<&str>` scalar" projection pin pattern this pin folds
10360        // on. Sibling in shape to the peer per-`:placement`
10361        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10362        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10363        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10364        // axes, extended onto the outer top-level [`Caixa`] universal-
10365        // axis surface. Pins against a future silent detour that
10366        // returned an owned `Option<String>` (which would type-check
10367        // but silently allocate on every accessor call, breaking the
10368        // zero-cost projection every peer sibling accessor carries), a
10369        // `Some("") → None` collapse (which would silently absorb the
10370        // `DescricaoEmpty` refusal case at the accessor boundary and
10371        // the caixa-helm `Chart.yaml` `description:` fold would
10372        // silently render a `caixa.nome`-derived fallback on a
10373        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
10374        // `None → Some(<default>)` collapse (which would silently
10375        // reify the per-renderer `caixa.nome`-derived fallback at the
10376        // accessor boundary and every downstream consumer keying off
10377        // the `Option::is_none()` discriminator would lose the "author
10378        // omitted the slot" signal).
10379        for descricao in [
10380            None,
10381            Some(""),
10382            Some("Checkout flow."),
10383            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
10384            Some("→ — · ✓"),
10385            Some(" Checkout flow."),
10386            Some("Checkout flow. "),
10387            Some("Checkout\nflow."),
10388            Some("Checkout\tflow."),
10389            Some("Checkout\x00flow."),
10390        ] {
10391            let c = caixa_with_descricao(descricao);
10392            assert_eq!(
10393                c.descricao(),
10394                descricao,
10395                "Caixa::descricao must return :descricao verbatim (got \
10396                 {:?}, expected {descricao:?})",
10397                c.descricao(),
10398            );
10399            assert_eq!(
10400                c.descricao(),
10401                c.descricao.as_deref(),
10402                "Caixa::descricao must byte-equal the raw \
10403                 `self.descricao.as_deref()` field access across every \
10404                 value in the Option<&str> accept-set",
10405            );
10406        }
10407    }
10408
10409    #[test]
10410    fn validate_descricao_empty_arm_routes_through_accessor() {
10411        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
10412        // gate must key off [`Caixa::descricao`], not the raw
10413        // `self.descricao.as_deref()` field access. Structurally: a
10414        // `Caixa { descricao: Some(""), .. }` must surface the
10415        // `DescricaoEmpty` refusal exactly, and a
10416        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
10417        // canonical one-line-prose form) must pass validate. The pair
10418        // jointly pins the accessor + validate-gate composition: any
10419        // future silent detour that had the accessor return `None` on
10420        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10421        // silently absorb the `DescricaoEmpty` refusal at the accessor
10422        // boundary and the validate gate would accept a struct-literal
10423        // `Caixa { descricao: Some(""), .. }` — the composition pin
10424        // catches that at caixa-core build time.
10425        //
10426        // Peer of the [`Caixa::licenca`] (6d5bc28)
10427        // `validate_licenca_empty_arm_routes_through_accessor` and
10428        // [`Caixa::repositorio`] (cc7332d)
10429        // `validate_repositorio_empty_arm_routes_through_accessor`
10430        // composition pins on the sibling outer top-level [`Caixa`]
10431        // `Option<&str>` universal-axis surface — same "the validate /
10432        // shape-gate predicate must route through the substrate-
10433        // primitive typed dispatch" discipline extended onto the third
10434        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10435        // composition surface.
10436        let c = caixa_with_descricao(Some(""));
10437        assert!(
10438            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
10439            "validate_descricao must reject descricao == Some(\"\") \
10440             with DescricaoEmpty — the accessor and the validate gate \
10441             must route through the same substrate-primitive typed \
10442             dispatch on the :descricao empty arm",
10443        );
10444        let c = caixa_with_descricao(Some("Checkout flow."));
10445        assert!(
10446            c.validate_descricao().is_ok(),
10447            "validate_descricao must accept descricao == \
10448             Some(\"Checkout flow.\") (the canonical one-line-prose \
10449             chart-description shape)",
10450        );
10451    }
10452
10453    #[test]
10454    fn descricao_projects_option_str_by_borrow() {
10455        // The by-borrow pin: [`Caixa::descricao`] returns
10456        // `Option<&str>` by borrow — the `&str` borrows the underlying
10457        // `String` storage of the `Option<String>` slot and the
10458        // accessor must not allocate a fresh `String` on every call.
10459        // Peer of the [`Caixa::licenca`] (6d5bc28) and
10460        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
10461        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
10462        // the per-`:placement`
10463        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10464        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
10465        // return axis, extended onto the third outer top-level
10466        // [`Caixa`] universal-axis `Option<&str>` shape — the
10467        // accessor's returned `&str` must borrow from `&self` (the
10468        // returned reference's lifetime is tied to `&self`), and
10469        // calling the accessor twice on the same [`Caixa`] must yield
10470        // the same `Option<&str>` verbatim (idempotent, no side
10471        // effects on `&self`).
10472        //
10473        // Pins against a future silent detour that returned an owned
10474        // `Option<String>` (which would type-check but silently
10475        // allocate on every call, breaking the zero-cost projection
10476        // every peer sibling accessor carries), or a one-arm-only
10477        // accessor that returned a saturating value on some sentinel
10478        // input (breaking the pass-through invariant the sibling
10479        // required-scalar accessors carry).
10480        for descricao in [
10481            None,
10482            Some(""),
10483            Some("Checkout flow."),
10484            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
10485        ] {
10486            let c = caixa_with_descricao(descricao);
10487            let first = c.descricao();
10488            let second = c.descricao();
10489            assert_eq!(
10490                first, second,
10491                "Caixa::descricao must be idempotent — two successive \
10492                 calls on the same &self must return the same \
10493                 Option<&str>",
10494            );
10495            assert_eq!(
10496                first, descricao,
10497                "Caixa::descricao must return :descricao verbatim by \
10498                 borrow — got {first:?}, expected {descricao:?}",
10499            );
10500        }
10501    }
10502
10503    // ── validate_edicao — universal-axis language-edition shape ──
10504
10505    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
10506        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10507        c.edicao = edicao.map(String::from);
10508        c
10509    }
10510
10511    #[test]
10512    fn validate_edicao_accepts_none() {
10513        // The omit-the-slot identity: `:edicao` is optional. The
10514        // gate is a no-op when the author didn't declare a value —
10515        // every caixa without an `:edicao` line trivially passes,
10516        // and the substrate-side build pipeline falls back to the
10517        // documented default edition. Mirrors the peer
10518        // `validate_licenca_accepts_none` posture on the sibling
10519        // `Option<String>` Caixa slot.
10520        let c = caixa_with_edicao(None);
10521        c.validate_edicao().unwrap();
10522    }
10523
10524    #[test]
10525    fn validate_edicao_accepts_canonical_value() {
10526        // Positive control: the canonical `"2026"` edition every
10527        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
10528        // `caixa-mesh`) carries by construction passes the gate.
10529        // Future-introduced sibling editions (`"2027"`, `"2030"`,
10530        // `"2049"`) that match the same 4-digit ASCII decimal year
10531        // shape must also trivially pass — the structural shape
10532        // predicate accepts every well-formed year regardless of
10533        // whether the substrate yet understands the specific value
10534        // (a future known-edition allowlist tightens that).
10535        for ed in ["2026", "2027", "2030", "2049"] {
10536            let c = caixa_with_edicao(Some(ed));
10537            c.validate_edicao()
10538                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
10539        }
10540    }
10541
10542    #[test]
10543    fn validate_edicao_rejects_empty_some() {
10544        // Canonical paste-from-blank-doc footgun. Without this gate
10545        // the empty `Some("")` silently lands as `(:edicao "")` in
10546        // the rendered caixa.lisp and a future renderer-side
10547        // consumer's `Option::unwrap_or_else` (which only fires on
10548        // `None`) skips its fallback. Mirrors the peer
10549        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
10550        // `Option<String>` Caixa slot.
10551        let c = caixa_with_edicao(Some(""));
10552        let err = c.validate_edicao().unwrap_err();
10553        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
10554    }
10555
10556    #[test]
10557    fn validate_edicao_rejects_free_form_non_year() {
10558        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
10559        // `"nightly"` shapes carry no operational meaning on the
10560        // substrate's build-time edition selector. Until this gate
10561        // landed the bare empty-arm check let every such value
10562        // through and broke far from the source caixa.lisp. Peer
10563        // with the shape-predicate cascade
10564        // `validate_repositorio_rejects_missing_colon_separator`
10565        // establishes past its own empty arm.
10566        for ed in ["x", "latest", "nightly", "stable"] {
10567            let c = caixa_with_edicao(Some(ed));
10568            let err = c.validate_edicao().unwrap_err();
10569            assert!(
10570                matches!(err, ManifestError::EdicaoInvalid { .. }),
10571                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10572            );
10573        }
10574    }
10575
10576    #[test]
10577    fn validate_edicao_rejects_trailing_whitespace() {
10578        // Paste-from-doc whitespace footgun. A trailing space in
10579        // the `:edicao` value would silently break the substrate's
10580        // build-time edition match-table lookup at the rendered
10581        // artifact's edition-selector consumer. The shape predicate
10582        // refuses every whitespace byte by construction (any byte
10583        // outside `0-9` fails `is_ascii_digit`). Peer with
10584        // `validate_repositorio_rejects_whitespace`.
10585        let c = caixa_with_edicao(Some("2026 "));
10586        let err = c.validate_edicao().unwrap_err();
10587        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
10588            panic!("expected EdicaoInvalid, got {err:?}");
10589        };
10590        assert_eq!(edicao, "2026 ");
10591    }
10592
10593    #[test]
10594    fn validate_edicao_rejects_leading_whitespace() {
10595        // Symmetric paste-from-doc whitespace footgun on the leading
10596        // boundary — the gate refuses every shape with a non-digit
10597        // byte by construction.
10598        let c = caixa_with_edicao(Some(" 2026"));
10599        let err = c.validate_edicao().unwrap_err();
10600        assert!(
10601            matches!(err, ManifestError::EdicaoInvalid { .. }),
10602            "got {err:?}",
10603        );
10604    }
10605
10606    #[test]
10607    fn validate_edicao_rejects_control_char() {
10608        // Paste-from-multiline-doc CRLF footgun — control characters
10609        // at the value boundary break the substrate's build-time
10610        // edition-selector parser. Peer with
10611        // `validate_repositorio_rejects_control_char`.
10612        let c = caixa_with_edicao(Some("2026\n"));
10613        let err = c.validate_edicao().unwrap_err();
10614        assert!(
10615            matches!(err, ManifestError::EdicaoInvalid { .. }),
10616            "got {err:?}",
10617        );
10618    }
10619
10620    #[test]
10621    fn validate_edicao_rejects_non_ascii_lookalike() {
10622        // Fullwidth-keyboard look-alike footgun — `"2026"` is
10623        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
10624        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
10625        // edition selector wants an ASCII year, and the gate
10626        // refuses every non-ASCII shape by construction (length in
10627        // bytes is 12 ≠ 4, *and* every byte falls outside
10628        // `is_ascii_digit`'s `0-9` range).
10629        let c = caixa_with_edicao(Some("2026"));
10630        let err = c.validate_edicao().unwrap_err();
10631        assert!(
10632            matches!(err, ManifestError::EdicaoInvalid { .. }),
10633            "got {err:?}",
10634        );
10635    }
10636
10637    #[test]
10638    fn validate_edicao_rejects_version_tag_prefix() {
10639        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
10640        // / `"r2026"` are familiar shapes from git-tag / Rust
10641        // edition / release-tag conventions that don't apply to
10642        // the year-shaped edition axis. The shape predicate refuses
10643        // every leading non-digit prefix.
10644        for ed in ["v2026", "e2026", "r2026"] {
10645            let c = caixa_with_edicao(Some(ed));
10646            let err = c.validate_edicao().unwrap_err();
10647            assert!(
10648                matches!(err, ManifestError::EdicaoInvalid { .. }),
10649                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10650            );
10651        }
10652    }
10653
10654    #[test]
10655    fn validate_edicao_rejects_decimal_shape() {
10656        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
10657        // `"2026.0"` are familiar shapes from semver / float
10658        // conventions that don't apply to the year-shaped edition
10659        // axis. The shape predicate refuses every non-digit byte
10660        // (`.` falls outside `is_ascii_digit`).
10661        for ed in ["2026.1", "2026.0", "2026.0.1"] {
10662            let c = caixa_with_edicao(Some(ed));
10663            let err = c.validate_edicao().unwrap_err();
10664            assert!(
10665                matches!(err, ManifestError::EdicaoInvalid { .. }),
10666                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10667            );
10668        }
10669    }
10670
10671    #[test]
10672    fn validate_edicao_rejects_wrong_length_numeric() {
10673        // Wrong-length numeric footgun — `"26"` (truncated) /
10674        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
10675        // (zero-padded too wide) all parse as integers but don't
10676        // name a 4-digit year. The shape predicate refuses every
10677        // value whose length isn't exactly 4 bytes.
10678        for ed in ["26", "202", "20260", "00026", "9"] {
10679            let c = caixa_with_edicao(Some(ed));
10680            let err = c.validate_edicao().unwrap_err();
10681            assert!(
10682                matches!(err, ManifestError::EdicaoInvalid { .. }),
10683                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10684            );
10685        }
10686    }
10687
10688    #[test]
10689    fn validate_edicao_empty_takes_precedence_over_shape() {
10690        // Empty-first cascade pin: the empty `Some("")` surfaces
10691        // the narrower `EdicaoEmpty` not the shape-predicate-
10692        // wrapped `EdicaoInvalid`, mirroring the peer
10693        // `validate_repositorio_empty_takes_precedence_over_shape`
10694        // (`RepositorioEmpty` → `RepositorioInvalid`),
10695        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
10696        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
10697        // cascades. The shape predicate also refuses the empty
10698        // input (defensively — `s.len() != 4`), but the
10699        // manifest-layer empty arm runs first to surface the
10700        // narrower diagnostic verbatim.
10701        let c = caixa_with_edicao(Some(""));
10702        let err = c.validate_edicao().unwrap_err();
10703        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
10704    }
10705
10706    #[test]
10707    fn validate_edicao_template_passes() {
10708        // Round-trip pin: the bare `Caixa::template` shape (which
10709        // carries `:edicao "2026"` verbatim) passes the gate by
10710        // construction. A future template-shape change that
10711        // introduced `(:edicao "")` or a non-year value would
10712        // surface here as a regression. Mirrors the peer
10713        // `validate_licenca_template_passes` pin.
10714        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10715        c.validate_edicao().unwrap();
10716    }
10717
10718    #[test]
10719    fn validate_edicao_diagnostic_names_offending_slot() {
10720        // Diagnostic-shape pin (peer with
10721        // `validate_licenca_diagnostic_names_offending_slot`): the
10722        // error's Display surfaces the `:edicao` slot name verbatim,
10723        // so a `feira lint` run can render the diagnostic without
10724        // re-parsing and the author can grep their caixa.lisp for
10725        // the offending `:edicao` line.
10726        let c = caixa_with_edicao(Some(""));
10727        let rendered = c.validate_edicao().unwrap_err().to_string();
10728        assert!(
10729            rendered.contains(":edicao"),
10730            "diagnostic must name the offending slot: {rendered}",
10731        );
10732    }
10733
10734    #[test]
10735    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
10736        // Diagnostic-shape pin on the shape-predicate arm (peer
10737        // with `validate_repositorio_diagnostic_carries_offending_value`):
10738        // the error's Display surfaces the offending value + slot
10739        // name verbatim, so a `feira lint` run can render the
10740        // diagnostic without re-parsing and the author can grep
10741        // their caixa.lisp for the offending `:edicao` value.
10742        let c = caixa_with_edicao(Some("v2026"));
10743        let rendered = c.validate_edicao().unwrap_err().to_string();
10744        assert!(
10745            rendered.contains(":edicao"),
10746            "diagnostic must name the offending slot: {rendered}",
10747        );
10748        assert!(
10749            rendered.contains("v2026"),
10750            "diagnostic must quote the offending value: {rendered}",
10751        );
10752    }
10753
10754    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
10755
10756    #[test]
10757    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
10758        // The canonical per-`Caixa` `:edicao` language-edition scalar
10759        // pin: [`Caixa::edicao`] must return the `:edicao` typed
10760        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10761        // raw `self.edicao.as_deref()` access across every representative
10762        // value in the accept-set — `None` (the "omit the slot to defer
10763        // to the substrate's default edition" arm every existing
10764        // [`caixa-resolver`] fixture without an `:edicao` line carries),
10765        // `Some("")` (a past-the-guard sentinel that pins the accessor
10766        // doesn't perform a silent `Some("") → None` collapse on the
10767        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
10768        // but the accessor must ship the raw slot verbatim so a
10769        // validate-time gate regression surfaces at any future edition-
10770        // aware consumer's boundary rather than being silently absorbed
10771        // into the substrate's default edition), `Some("2026")` (the
10772        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
10773        // template scaffolds via [`Caixa::template`] and every
10774        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
10775        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
10776        // carries by construction), `Some("2018")` / `Some("2021")` /
10777        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
10778        // peer with Cargo's `[package] edition` grammar every future-
10779        // introduced sibling to `"2026"` will follow), and eight
10780        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
10781        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
10782        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
10783        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
10784        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
10785        // length-numeric, `Some("latest")` free-form-non-year — the
10786        // sentinels pin the accessor doesn't silently absorb the
10787        // refusal cases into a substrate-default-edition fallback).
10788        //
10789        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
10790        // return scalar accessor pin on the substrate primitive —
10791        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
10792        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
10793        // (3f16e2f) pins that opened the "outer [`Caixa`]
10794        // `Option<&str>` scalar" projection pin pattern this pin folds
10795        // on. Sibling in shape to the peer per-`:placement`
10796        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10797        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10798        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10799        // axes, extended onto the outer top-level [`Caixa`] universal-
10800        // axis surface's last unlifted `Option<String>` slot. Pins
10801        // against a future silent detour that returned an owned
10802        // `Option<String>` (which would type-check but silently
10803        // allocate on every accessor call, breaking the zero-cost
10804        // projection every peer sibling accessor carries), a
10805        // `Some("") → None` collapse (which would silently absorb the
10806        // `EdicaoEmpty` refusal case at the accessor boundary and any
10807        // future edition-aware consumer would silently fall back to
10808        // the substrate's default edition on a struct-literal
10809        // `Caixa { edicao: Some(""), .. }`), or a
10810        // `None → Some("2026")` collapse (which would silently reify
10811        // the substrate's default edition at the accessor boundary
10812        // and every downstream consumer keying off the
10813        // `Option::is_none()` discriminator would lose the "author
10814        // omitted the slot" signal).
10815        for edicao in [
10816            None,
10817            Some(""),
10818            Some("2026"),
10819            Some("2018"),
10820            Some("2021"),
10821            Some("2024"),
10822            Some("2026 "),
10823            Some(" 2026"),
10824            Some("2026\n"),
10825            Some("2026"),
10826            Some("v2026"),
10827            Some("2026.1"),
10828            Some("26"),
10829            Some("latest"),
10830        ] {
10831            let c = caixa_with_edicao(edicao);
10832            assert_eq!(
10833                c.edicao(),
10834                edicao,
10835                "Caixa::edicao must return :edicao verbatim (got {:?}, \
10836                 expected {edicao:?})",
10837                c.edicao(),
10838            );
10839            assert_eq!(
10840                c.edicao(),
10841                c.edicao.as_deref(),
10842                "Caixa::edicao must byte-equal the raw \
10843                 `self.edicao.as_deref()` field access across every \
10844                 value in the Option<&str> accept-set",
10845            );
10846        }
10847    }
10848
10849    #[test]
10850    fn validate_edicao_empty_arm_routes_through_accessor() {
10851        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
10852        // must key off [`Caixa::edicao`], not the raw
10853        // `self.edicao.as_deref()` field access. Structurally: a
10854        // `Caixa { edicao: Some(""), .. }` must surface the
10855        // `EdicaoEmpty` refusal exactly, and a
10856        // `Caixa { edicao: Some("2026"), .. }` (the canonical
10857        // 4-digit-ASCII-decimal-year form) must pass validate. The
10858        // pair jointly pins the accessor + validate-gate composition:
10859        // any future silent detour that had the accessor return `None`
10860        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
10861        // would silently absorb the `EdicaoEmpty` refusal at the
10862        // accessor boundary and the validate gate would accept a
10863        // struct-literal `Caixa { edicao: Some(""), .. }` — the
10864        // composition pin catches that at caixa-core build time.
10865        //
10866        // Peer of the [`Caixa::licenca`] (6d5bc28)
10867        // `validate_licenca_empty_arm_routes_through_accessor`,
10868        // [`Caixa::repositorio`] (cc7332d)
10869        // `validate_repositorio_empty_arm_routes_through_accessor`,
10870        // and [`Caixa::descricao`] (3f16e2f)
10871        // `validate_descricao_empty_arm_routes_through_accessor`
10872        // composition pins on the sibling outer top-level [`Caixa`]
10873        // `Option<&str>` universal-axis surface — same "the validate /
10874        // shape-gate predicate must route through the substrate-
10875        // primitive typed dispatch" discipline extended onto the
10876        // fourth and final outer top-level [`Caixa`] universal-axis
10877        // `Option<&str>`-composition surface, closing the accessor-
10878        // composition family.
10879        let c = caixa_with_edicao(Some(""));
10880        assert!(
10881            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
10882            "validate_edicao must reject edicao == Some(\"\") with \
10883             EdicaoEmpty — the accessor and the validate gate must \
10884             route through the same substrate-primitive typed dispatch \
10885             on the :edicao empty arm",
10886        );
10887        let c = caixa_with_edicao(Some("2026"));
10888        assert!(
10889            c.validate_edicao().is_ok(),
10890            "validate_edicao must accept edicao == Some(\"2026\") \
10891             (the canonical 4-digit-ASCII-decimal-year shape)",
10892        );
10893    }
10894
10895    #[test]
10896    fn edicao_projects_option_str_by_borrow() {
10897        // The by-borrow pin: [`Caixa::edicao`] returns
10898        // `Option<&str>` by borrow — the `&str` borrows the underlying
10899        // `String` storage of the `Option<String>` slot and the
10900        // accessor must not allocate a fresh `String` on every call.
10901        // Peer of the [`Caixa::licenca`] (6d5bc28),
10902        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
10903        // (3f16e2f) by-borrow pins on the peer outer top-level
10904        // [`Caixa`] `Option<&str>`-return axes, and of the
10905        // per-`:placement`
10906        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10907        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
10908        // return axis, extended onto the fourth and final outer top-
10909        // level [`Caixa`] universal-axis `Option<&str>` shape — the
10910        // accessor's returned `&str` must borrow from `&self` (the
10911        // returned reference's lifetime is tied to `&self`), and
10912        // calling the accessor twice on the same [`Caixa`] must yield
10913        // the same `Option<&str>` verbatim (idempotent, no side
10914        // effects on `&self`).
10915        //
10916        // Pins against a future silent detour that returned an owned
10917        // `Option<String>` (which would type-check but silently
10918        // allocate on every call, breaking the zero-cost projection
10919        // every peer sibling accessor carries), or a one-arm-only
10920        // accessor that returned a saturating value on some sentinel
10921        // input (breaking the pass-through invariant the sibling
10922        // required-scalar accessors carry).
10923        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
10924            let c = caixa_with_edicao(edicao);
10925            let first = c.edicao();
10926            let second = c.edicao();
10927            assert_eq!(
10928                first, second,
10929                "Caixa::edicao must be idempotent — two successive \
10930                 calls on the same &self must return the same \
10931                 Option<&str>",
10932            );
10933            assert_eq!(
10934                first, edicao,
10935                "Caixa::edicao must return :edicao verbatim by \
10936                 borrow — got {first:?}, expected {edicao:?}",
10937            );
10938        }
10939    }
10940
10941    #[test]
10942    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
10943        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
10944        // label caixa-identity scalar pin: [`Caixa::nome`] must return
10945        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
10946        // the raw field access across every representative value in
10947        // the accept-set — the canonical `"demo"` template baseline
10948        // (the same `feira init`-scaffolded default the sibling
10949        // `validate_nome_accepts_canonical_template` positive-control
10950        // gate pins), plus every sibling per-typed-slot atom accessor's
10951        // canonical positive-arm byte-string (`"catalog"` per
10952        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
10953        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
10954        // `caixa-helm`/`caixa-flux` cross-crate integration-test
10955        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
10956        // canonical example), plus every past-the-guard sentinel for
10957        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
10958        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
10959        // the bare DNS-1123 63-byte cap but overflows the joint
10960        // `lareira-<nome>` chart-name budget the sibling
10961        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
10962        //
10963        // The past-the-guard sentinels pin the accessor doesn't
10964        // silently absorb the refusal cases into a template-derived
10965        // fallback (a future `.nome().is_empty().then(|| "demo")`
10966        // collapse would silently absorb the `NomeEmpty` refusal at
10967        // the accessor boundary and the validate gate would accept a
10968        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
10969        // catches that at caixa-core build time).
10970        //
10971        // First outer top-level [`Caixa`] `&str`-return required-
10972        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
10973        // required-scalar" projection pattern the sibling per-`Caixa`
10974        // `:versao` future lift folds on. Sibling in shape to the peer
10975        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
10976        // required-`String`-carry accessor pin on the sibling per-
10977        // sub-struct required-axis, extended onto the outer top-level
10978        // [`Caixa`] universal-axis required-`String`-carry axis.
10979        for nome in [
10980            "demo",
10981            "catalog",
10982            "cart",
10983            "hello-rio",
10984            "checkout",
10985            "",
10986            "Bad_Name",
10987            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
10988        ] {
10989            let c = caixa_with_nome(nome);
10990            assert_eq!(
10991                c.nome(),
10992                nome,
10993                "Caixa::nome must return :nome verbatim (got {}, \
10994                 expected {nome})",
10995                c.nome(),
10996            );
10997            assert_eq!(
10998                c.nome(),
10999                c.nome.as_str(),
11000                "Caixa::nome must byte-equal the raw .nome field \
11001                 access across every value in the String accept-set",
11002            );
11003        }
11004    }
11005
11006    #[test]
11007    fn validate_nome_empty_arm_routes_through_accessor() {
11008        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
11009        // key off [`Caixa::nome`], not the raw `.nome` field access.
11010        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
11011        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
11012        // template baseline (the peer positive-arm the sibling
11013        // `validate_nome_accepts_canonical_template` gate carves out)
11014        // must pass validate. The pair jointly pins the accessor +
11015        // validate-gate composition: any future silent detour that
11016        // had the accessor return a fresh `"demo"` on the empty arm
11017        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
11018        // would silently absorb the `NomeEmpty` refusal at the
11019        // accessor boundary and the validate gate would accept a
11020        // struct-literal `Caixa { nome: "".into(), .. }` — the
11021        // composition pin catches that at caixa-core build time.
11022        //
11023        // Peer of the sibling per-`Caixa`
11024        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
11025        // / `validate_repositorio_empty_arm_routes_through_accessor`
11026        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
11027        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
11028        // (2641cbd) composition pins on the sibling outer top-level
11029        // [`Caixa`] `Option<&str>` axes — same "the validate /
11030        // shape-gate predicate must route through the substrate-
11031        // primitive typed dispatch" discipline extended onto the peer
11032        // outer top-level [`Caixa`] required-`&str` composition axis.
11033        let c = caixa_with_nome("");
11034        assert!(
11035            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
11036            "validate_nome must reject nome == \"\" with NomeEmpty — \
11037             the accessor and the validate gate must route through the \
11038             same substrate-primitive typed dispatch on the :nome \
11039             empty-arm",
11040        );
11041        let c = caixa_with_nome("demo");
11042        assert!(
11043            c.validate_nome().is_ok(),
11044            "validate_nome must accept nome == \"demo\" (the canonical \
11045             DNS-1123-label template baseline)",
11046        );
11047    }
11048
11049    #[test]
11050    fn nome_projects_str_by_borrow() {
11051        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
11052        // — the `&str` borrows the underlying `String` storage of the
11053        // required `nome` slot and the accessor must not allocate a
11054        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
11055        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
11056        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
11057        // by-borrow pins on the peer outer top-level [`Caixa`]
11058        // `Option<&str>`-return axes, extended onto the first outer
11059        // top-level [`Caixa`] required-`&str`-return axis — the
11060        // accessor's returned `&str` must borrow from `&self` (the
11061        // returned reference's lifetime is tied to `&self`), and
11062        // calling the accessor twice on the same [`Caixa`] must yield
11063        // the same `&str` verbatim (idempotent, no side effects on
11064        // `&self`).
11065        //
11066        // Pins against a future silent detour that returned an owned
11067        // `String` (which would type-check but silently allocate on
11068        // every call, breaking the zero-cost projection every peer
11069        // sibling accessor carries), an accidental
11070        // `.nome.to_lowercase()` detour that returned a fresh
11071        // allocation through an already-DNS-1123-lowercase-only
11072        // string (breaking a future `const fn` regression), or a
11073        // one-arm-only accessor that returned a canonicalized value
11074        // on some sentinel input (breaking the pass-through invariant
11075        // the sibling required-scalar accessors carry).
11076        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
11077            let c = caixa_with_nome(nome);
11078            let first = c.nome();
11079            let second = c.nome();
11080            assert_eq!(
11081                first, second,
11082                "Caixa::nome must be idempotent — two successive calls \
11083                 on the same &self must return the same &str",
11084            );
11085            assert_eq!(
11086                first, nome,
11087                "Caixa::nome must return :nome verbatim by borrow — \
11088                 got {first}, expected {nome}",
11089            );
11090        }
11091    }
11092
11093    #[test]
11094    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
11095        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
11096        // pinned-version scalar pin: [`Caixa::versao`] must return the
11097        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
11098        // raw `.versao` field access across every representative value
11099        // in the accept-set — the canonical `"0.1.0"` template baseline
11100        // (the same `feira init`-scaffolded default the sibling
11101        // `validate_versao_accepts_canonical_template` positive-control
11102        // gate pins), plus every canonical SemVer-2 shape the sibling
11103        // `validate_versao_accepts_canonical_forms` positive-arm sweep
11104        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
11105        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
11106        // `"10.20.30"`), plus every past-the-guard sentinel for the
11107        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
11108        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
11109        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
11110        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
11111        // `"latest"` the docker-tag-shape footgun — the sentinels pin
11112        // the accessor doesn't silently absorb the refusal cases into a
11113        // template-derived fallback like `"0.1.0"`).
11114        //
11115        // The past-the-guard sentinels pin the accessor doesn't silently
11116        // absorb the refusal cases into a template-derived fallback (a
11117        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
11118        // silently absorb the `VersaoEmpty` refusal at the accessor
11119        // boundary and the validate gate would accept a struct-literal
11120        // `Caixa { versao: "".into(), .. }` — the pin catches that at
11121        // caixa-core build time).
11122        //
11123        // Second outer top-level [`Caixa`] `&str`-return required-scalar
11124        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
11125        // scalar" projection pattern the sibling per-`Caixa`
11126        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
11127        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
11128        // (4127bb6) / per-`:children`
11129        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
11130        // / per-`:upgrade-from`
11131        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
11132        // struct `:versao`-shaped `&str`-return accessor pins on the
11133        // sibling per-typed-slot version-carrier axes, extended onto the
11134        // second outer top-level [`Caixa`] universal-axis required-
11135        // `String`-carry axis so the two universal-axis identity-
11136        // carrying scalars every `defcaixa` form supplies (`:nome` +
11137        // `:versao`) share the same "one typed dispatch per axis" pin
11138        // discipline.
11139        for versao in [
11140            "0.1.0",
11141            "0.0.0",
11142            "1.0.0",
11143            "0.2.0-rc.1",
11144            "1.0.0-alpha.0",
11145            "1.0.0+build.42",
11146            "1.0.0-rc.1+build.42",
11147            "10.20.30",
11148            "",
11149            "v0.1.0",
11150            "0.1",
11151            "^0.1",
11152            "0.1.0.0",
11153            "latest",
11154        ] {
11155            let c = caixa_with_versao(versao);
11156            assert_eq!(
11157                c.versao(),
11158                versao,
11159                "Caixa::versao must return :versao verbatim (got {}, \
11160                 expected {versao})",
11161                c.versao(),
11162            );
11163            assert_eq!(
11164                c.versao(),
11165                c.versao.as_str(),
11166                "Caixa::versao must byte-equal the raw .versao field \
11167                 access across every value in the String accept-set",
11168            );
11169        }
11170    }
11171
11172    #[test]
11173    fn validate_versao_empty_arm_routes_through_accessor() {
11174        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
11175        // must key off [`Caixa::versao`], not the raw `.versao` field
11176        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
11177        // surface the `VersaoEmpty` refusal exactly, and the canonical
11178        // `"0.1.0"` template baseline (the peer positive-arm the sibling
11179        // `validate_versao_accepts_canonical_template` gate carves out)
11180        // must pass validate. The pair jointly pins the accessor +
11181        // validate-gate composition: any future silent detour that had
11182        // the accessor return a fresh `"0.1.0"` on the empty arm
11183        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
11184        // would silently absorb the `VersaoEmpty` refusal at the
11185        // accessor boundary and the validate gate would accept a
11186        // struct-literal `Caixa { versao: "".into(), .. }` — the
11187        // composition pin catches that at caixa-core build time.
11188        //
11189        // Peer of the sibling per-`Caixa`
11190        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
11191        // composition pin on the sibling outer top-level [`Caixa`]
11192        // required-`&str` universal-axis surface — same "the validate /
11193        // shape-gate predicate must route through the substrate-
11194        // primitive typed dispatch" discipline extended onto the peer
11195        // outer top-level [`Caixa`] required-`&str` universal-axis
11196        // pinned-version composition axis, closing the second
11197        // coordinate of the "one canonical typed dispatch per per-Caixa
11198        // required-`&str` universal-axis" discipline.
11199        let c = caixa_with_versao("");
11200        assert!(
11201            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
11202            "validate_versao must reject versao == \"\" with VersaoEmpty — \
11203             the accessor and the validate gate must route through the \
11204             same substrate-primitive typed dispatch on the :versao \
11205             empty-arm",
11206        );
11207        let c = caixa_with_versao("0.1.0");
11208        assert!(
11209            c.validate_versao().is_ok(),
11210            "validate_versao must accept versao == \"0.1.0\" (the \
11211             canonical SemVer-2 template baseline)",
11212        );
11213    }
11214
11215    #[test]
11216    fn versao_projects_str_by_borrow() {
11217        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
11218        // — the `&str` borrows the underlying `String` storage of the
11219        // required `versao` slot and the accessor must not allocate a
11220        // fresh `String` on every call. Peer of the [`Caixa::nome`]
11221        // (e6b7d97) by-borrow pin on the sibling outer top-level
11222        // [`Caixa`] required-`&str`-return axis, extended onto the
11223        // second outer top-level [`Caixa`] required-`&str`-return
11224        // universal-axis pinned-version surface — the accessor's
11225        // returned `&str` must borrow from `&self` (the returned
11226        // reference's lifetime is tied to `&self`), and calling the
11227        // accessor twice on the same [`Caixa`] must yield the same
11228        // `&str` verbatim (idempotent, no side effects on `&self`).
11229        //
11230        // Pins against a future silent detour that returned an owned
11231        // `String` (which would type-check but silently allocate on
11232        // every call, breaking the zero-cost projection every peer
11233        // sibling accessor carries), an accidental
11234        // `semver::Version::parse(&self.versao).unwrap().to_string()`
11235        // detour that returned a canonicalized fresh allocation through
11236        // an already-canonical byte-string (breaking a future `const fn`
11237        // regression and silently absorbing the `VersaoInvalid` refusal
11238        // at the accessor boundary), or a one-arm-only accessor that
11239        // returned a canonicalized value on some sentinel input
11240        // (breaking the pass-through invariant the sibling required-
11241        // scalar accessors carry).
11242        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
11243            let c = caixa_with_versao(versao);
11244            let first = c.versao();
11245            let second = c.versao();
11246            assert_eq!(
11247                first, second,
11248                "Caixa::versao must be idempotent — two successive \
11249                 calls on the same &self must return the same &str",
11250            );
11251            assert_eq!(
11252                first, versao,
11253                "Caixa::versao must return :versao verbatim by borrow \
11254                 — got {first}, expected {versao}",
11255            );
11256        }
11257    }
11258
11259    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
11260        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11261        c.kind = kind;
11262        c
11263    }
11264
11265    #[test]
11266    fn kind_returns_kind_variant_verbatim_across_permutations() {
11267        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
11268        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
11269        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
11270        // the raw `.kind` field access across every variant in the
11271        // closed accept-set (`Biblioteca` — the library kind that
11272        // exports lisp forms; `Binario` — the nix-built executable kind
11273        // under `exe/`; `Servico` — the wasm-component daemon kind
11274        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
11275        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
11276        // composition kind).
11277        //
11278        // Pins against a future silent detour that re-derived the kind
11279        // from a peer axis (an accidental fallback to
11280        // `if !servicos.is_empty() { Servico } else if
11281        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
11282        // collapse that read the code-surface / mesh-slot columns into
11283        // the kind discriminator), a variant remap the operator
11284        // authors on one consumer without the other, or a stale-derive
11285        // detour that substituted [`CaixaKind::Biblioteca`] as the
11286        // default when the field held any other variant (which would
11287        // silently collapse the distinction between "author explicitly
11288        // declared `:kind Servico`" and "author declared any other
11289        // kind" every downstream renderer-dispatch site depends on).
11290        //
11291        // First outer top-level [`Caixa`] `Copy`-return required-enum-
11292        // discriminant accessor pin — opens the "outer [`Caixa`]
11293        // `Copy`-return required-discriminant" projection pattern.
11294        // Sibling in shape to the peer per-`:supervisor`
11295        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
11296        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
11297        // (921fe1b), and per-`:children`
11298        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
11299        // `Copy`-return closed-set-enum discriminant accessor pins on
11300        // the sibling nested-spec typed-slot discriminator axes,
11301        // extended here to the outer top-level [`Caixa`] universal-
11302        // axis surface.
11303        for kind in [
11304            CaixaKind::Biblioteca,
11305            CaixaKind::Binario,
11306            CaixaKind::Servico,
11307            CaixaKind::Supervisor,
11308            CaixaKind::Aplicacao,
11309        ] {
11310            let c = caixa_with_kind(kind);
11311            assert_eq!(
11312                c.kind(),
11313                kind,
11314                "Caixa::kind must return :kind verbatim (got {:?}, \
11315                 expected {kind:?})",
11316                c.kind(),
11317            );
11318            assert_eq!(
11319                c.kind(),
11320                c.kind,
11321                "Caixa::kind accessor and .kind field access must \
11322                 byte-equal — the accessor is the substrate-primitive \
11323                 typed dispatch every downstream kind-gate consumer \
11324                 must route through",
11325            );
11326        }
11327    }
11328
11329    #[test]
11330    fn require_kind_reads_through_lifted_kind_accessor() {
11331        // Two-consumer coherence pin: the [`crate::render::require_kind`]
11332        // entry-gate predicate (the canonical two-line
11333        // `require_kind(caixa, Servico)?` prelude every per-Servico /
11334        // per-Aplicacao renderer runs at its entry-point) and the
11335        // sibling [`crate::render::KindMismatch`] error carrier's
11336        // `actual:` field (which names the offending caixa's variant
11337        // in the diagnostic) must both key off the lifted accessor, so
11338        // any future rebrand on the typed slot's reader shape lands at
11339        // exactly one place. Pins the two-site coherence by exercising
11340        // every off-diagonal `(actual, expected)` pair across the
11341        // closed accept-set — the `KindMismatch { actual, expected }`
11342        // surfaced on the mismatch arm must byte-equal the pair the
11343        // accessor returns for each side.
11344        //
11345        // Peer of the sibling per-`:placement`
11346        // `validate_placement_reads_through_lifted_estrategia_accessor`
11347        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
11348        // `Copy`-return discriminant axis — same "the entry-gate
11349        // predicate and the error carrier's `actual:` field must route
11350        // through the substrate-primitive typed dispatch" discipline
11351        // extended onto the outer top-level [`Caixa`] universal-axis
11352        // discriminant surface.
11353        for expected in [
11354            CaixaKind::Biblioteca,
11355            CaixaKind::Binario,
11356            CaixaKind::Servico,
11357            CaixaKind::Supervisor,
11358            CaixaKind::Aplicacao,
11359        ] {
11360            for actual in [
11361                CaixaKind::Biblioteca,
11362                CaixaKind::Binario,
11363                CaixaKind::Servico,
11364                CaixaKind::Supervisor,
11365                CaixaKind::Aplicacao,
11366            ] {
11367                let c = caixa_with_kind(actual);
11368                let result = crate::render::require_kind(&c, expected);
11369                if expected == actual {
11370                    assert!(
11371                        result.is_ok(),
11372                        "require_kind must accept when actual == expected \
11373                         (actual={actual:?}, expected={expected:?})",
11374                    );
11375                } else {
11376                    let err = result.expect_err("require_kind must reject when actual != expected");
11377                    assert_eq!(
11378                        err.actual,
11379                        c.kind(),
11380                        "KindMismatch.actual must byte-equal Caixa::kind() \
11381                         — the error carrier's `actual:` field reads \
11382                         through the lifted accessor",
11383                    );
11384                    assert_eq!(
11385                        err.expected, expected,
11386                        "KindMismatch.expected must byte-equal the \
11387                         expected variant passed to require_kind",
11388                    );
11389                }
11390            }
11391        }
11392    }
11393
11394    #[test]
11395    fn aplicacao_view_kind_gate_routes_through_accessor() {
11396        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
11397        // must key off [`Caixa::kind`], not the raw `.kind` field
11398        // access. Structurally: a `Caixa { kind: X, .. }` for any
11399        // non-`Aplicacao` variant must fold to `None` on the
11400        // `aplicacao_view` composer (the "kind mismatch → no typed
11401        // view" contract every downstream Aplicacao consumer keys off
11402        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
11403        // `Some(_)`. The pair jointly pins the accessor + view-gate
11404        // composition: any future silent detour that had the accessor
11405        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
11406        // input would silently absorb the kind-mismatch case at the
11407        // accessor boundary and every per-Aplicacao renderer would
11408        // silently render a non-Aplicacao caixa's mesh slots — the
11409        // composition pin catches that at caixa-core build time.
11410        //
11411        // Peer of the sibling per-`Caixa`
11412        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
11413        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
11414        // composition pins on the sibling outer top-level [`Caixa`]
11415        // required-`&str` universal-axis surfaces — same "the
11416        // composer / validate gate must route through the substrate-
11417        // primitive typed dispatch" discipline extended onto the
11418        // outer top-level [`Caixa`] `Copy`-return required-
11419        // discriminant composition axis.
11420        for kind in [
11421            CaixaKind::Biblioteca,
11422            CaixaKind::Binario,
11423            CaixaKind::Servico,
11424            CaixaKind::Supervisor,
11425        ] {
11426            let c = caixa_with_kind(kind);
11427            assert!(
11428                c.aplicacao_view().is_none(),
11429                "aplicacao_view must return None on non-Aplicacao \
11430                 kind {kind:?} — the composer's kind-gate must route \
11431                 through Caixa::kind()",
11432            );
11433        }
11434        let c = caixa_with_kind(CaixaKind::Aplicacao);
11435        assert!(
11436            c.aplicacao_view().is_some(),
11437            "aplicacao_view must return Some on kind Aplicacao — \
11438             the composer's kind-gate must accept the matching arm \
11439             through Caixa::kind()",
11440        );
11441    }
11442
11443    #[test]
11444    fn supervisor_view_kind_gate_routes_through_accessor() {
11445        // Composition pin (mirror of the sibling
11446        // `aplicacao_view_kind_gate_routes_through_accessor` on the
11447        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
11448        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
11449        // field access. A `Caixa { kind: X, .. }` for any non-
11450        // `Supervisor` variant must fold to `None` on the
11451        // `supervisor_view` composer, and a `Caixa { kind:
11452        // Supervisor, .. }` must fold to `Some(_)`. Same peer
11453        // composition pin discipline on the second `_view` composer
11454        // axis.
11455        for kind in [
11456            CaixaKind::Biblioteca,
11457            CaixaKind::Binario,
11458            CaixaKind::Servico,
11459            CaixaKind::Aplicacao,
11460        ] {
11461            let c = caixa_with_kind(kind);
11462            assert!(
11463                c.supervisor_view().is_none(),
11464                "supervisor_view must return None on non-Supervisor \
11465                 kind {kind:?} — the composer's kind-gate must route \
11466                 through Caixa::kind()",
11467            );
11468        }
11469        let mut c = caixa_with_kind(CaixaKind::Supervisor);
11470        // A Supervisor caixa needs a strategy + at least one child to
11471        // fold to a Some(_) that also validates; the composer itself
11472        // requires only the kind arm, so bare kind flip is enough to
11473        // pin the `Some(_)` return, but we populate the minimum
11474        // supervisor shape so a future strengthening of the composer
11475        // to reject an empty spec doesn't false-positive this pin.
11476        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
11477        c.children = vec![crate::supervisor::ChildSpec {
11478            caixa: "child".into(),
11479            versao: "^0.1".into(),
11480            restart: crate::supervisor::RestartPolicy::Permanent,
11481        }];
11482        assert!(
11483            c.supervisor_view().is_some(),
11484            "supervisor_view must return Some on kind Supervisor — \
11485             the composer's kind-gate must accept the matching arm \
11486             through Caixa::kind()",
11487        );
11488    }
11489
11490    #[test]
11491    fn kind_projects_by_copy() {
11492        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
11493        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
11494        // `&self` (the returned value is owned, `Copy`-projected from
11495        // the underlying [`CaixaKind`] storage; two calls on the same
11496        // [`Caixa`] must yield byte-equal values). Peer of the peer
11497        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
11498        // `SupervisorSpec::estrategia` / per-`:children`
11499        // `ChildSpec::restart` `Copy`-return discriminant accessor
11500        // pins on the sibling nested-spec typed-slot discriminator
11501        // axes, extended onto the first outer top-level [`Caixa`]
11502        // required-`Copy`-return axis — pins against a future silent
11503        // detour that returned `&CaixaKind` (which would type-check
11504        // but silently constrain every consumer's callsite to a
11505        // borrow-shaped dispatch, breaking the zero-cost `Copy`
11506        // projection every peer sibling accessor carries).
11507        for kind in [
11508            CaixaKind::Biblioteca,
11509            CaixaKind::Binario,
11510            CaixaKind::Servico,
11511            CaixaKind::Supervisor,
11512            CaixaKind::Aplicacao,
11513        ] {
11514            let c = caixa_with_kind(kind);
11515            let first: CaixaKind = c.kind();
11516            let second: CaixaKind = c.kind();
11517            assert_eq!(
11518                first, second,
11519                "Caixa::kind must be idempotent — two successive \
11520                 calls on the same &self must return the same \
11521                 CaixaKind variant",
11522            );
11523            assert_eq!(
11524                first, kind,
11525                "Caixa::kind must return :kind verbatim by Copy — \
11526                 got {first:?}, expected {kind:?}",
11527            );
11528        }
11529    }
11530
11531    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
11532
11533    #[test]
11534    fn autores_returns_autores_slice_verbatim_across_permutations() {
11535        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
11536        // name-list slice pin: [`Caixa::autores`] must return the
11537        // `:autores` typed [`Vec<String>`] list verbatim as a
11538        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
11539        // access across every representative value in the accept-set —
11540        // `[]` (the "no maintainers declared" arm every existing
11541        // fixture without an `:autores` line carries), `[""]` (a past-
11542        // the-guard sentinel that pins the accessor doesn't perform a
11543        // silent `[""] → []` collapse on the empty-entry arm — validate
11544        // rejects `[""]` through `AutorEmpty` but the accessor must
11545        // ship the raw slot verbatim so a validate-time gate regression
11546        // surfaces at the caixa-helm emit boundary rather than being
11547        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
11548        // canonical single-maintainer form every `feira init` template
11549        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
11550        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
11551        // (the canonical RFC-5322 `<name> <email>` form the
11552        // `is_chart_maintainer_name_shape` predicate accepts), and
11553        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
11554        // sentinel — validate rejects through `AutorDuplicate` but the
11555        // accessor must ship the raw slot verbatim).
11556        //
11557        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
11558        // pin on the substrate primitive — opens the "outer [`Caixa`]
11559        // `&[T]` slice" projection pattern the sibling per-`Caixa`
11560        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
11561        // / `:servicos` / `:upgrade-from` / `:children` future lifts
11562        // fold on. Sibling in shape to the peer per-`:supervisor`
11563        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
11564        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
11565        // (a6e18d7), per-`:membros`
11566        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
11567        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
11568        // (0dcc926), and per-`:upgrade-from :instructions`
11569        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
11570        // `&[T]`-return slice accessor pins on the sibling per-M2 /
11571        // per-M3 typed-slot list axes, extended onto the outer top-
11572        // level [`Caixa`] universal-axis surface. Pins against a future
11573        // silent detour that returned an owned `Vec<String>` (which
11574        // would type-check but silently clone on every accessor call,
11575        // breaking the zero-cost projection every peer sibling slice
11576        // accessor carries), a `[""] → []` collapse (which would
11577        // silently absorb the `AutorEmpty` refusal case at the accessor
11578        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
11579        // would silently absorb the `AutorDuplicate` refusal case at
11580        // the accessor boundary and the caixa-helm `maintainers:` fold
11581        // would silently render a dedupped list on a struct-literal
11582        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
11583        for autores in [
11584            vec![],
11585            vec![""],
11586            vec!["pleme-io"],
11587            vec!["alice", "bob"],
11588            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
11589            vec!["pleme-io", "pleme-io"],
11590        ] {
11591            let c = caixa_with_autores(autores.clone());
11592            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
11593            assert_eq!(
11594                c.autores(),
11595                expected.as_slice(),
11596                "Caixa::autores must return :autores verbatim (got {:?}, \
11597                 expected {expected:?})",
11598                c.autores(),
11599            );
11600            assert_eq!(
11601                c.autores(),
11602                c.autores.as_slice(),
11603                "Caixa::autores must byte-equal the raw \
11604                 `self.autores.as_slice()` field access across every \
11605                 value in the Vec<String> accept-set",
11606            );
11607        }
11608    }
11609
11610    #[test]
11611    fn validate_autores_empty_entry_arm_routes_through_accessor() {
11612        // Composition pin: [`Caixa::validate_autores`]'s per-entry
11613        // empty-arm gate must key off [`Caixa::autores`], not the raw
11614        // `&self.autores` field-borrow walk. Structurally: a
11615        // `Caixa { autores: vec!["".into()], .. }` must surface the
11616        // `AutorEmpty` refusal exactly, and a
11617        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
11618        // canonical single-maintainer form) must pass validate. The
11619        // pair jointly pins the accessor + validate-gate composition:
11620        // any future silent detour that had the accessor return an
11621        // empty slice on the `[""]` arm (a
11622        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
11623        // would silently absorb the `AutorEmpty` refusal at the
11624        // accessor boundary and the validate gate would accept a
11625        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
11626        // the composition pin catches that at caixa-core build time.
11627        //
11628        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
11629        // accessor-composition pin
11630        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
11631        // sibling `Option<&str>`-composition axis and the
11632        // per-`:politicas :circuit-breaker`
11633        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
11634        // accessor-composition pin
11635        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
11636        // on the sibling required-`u32`-composition axis — same "the
11637        // validate / shape-gate predicate must route through the
11638        // substrate-primitive typed dispatch" discipline extended onto
11639        // the outer top-level [`Caixa`] universal-axis `&[T]`-
11640        // composition surface.
11641        let c = caixa_with_autores(vec![""]);
11642        assert!(
11643            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
11644            "validate_autores must reject autores == vec![\"\"] with \
11645             AutorEmpty — the accessor and the validate gate must \
11646             route through the same substrate-primitive typed dispatch \
11647             on the :autores per-entry empty arm",
11648        );
11649        let c = caixa_with_autores(vec!["pleme-io"]);
11650        assert!(
11651            c.validate_autores().is_ok(),
11652            "validate_autores must accept autores == vec![\"pleme-io\"] \
11653             (the canonical single-maintainer shape every `feira init` \
11654             template scaffolds)",
11655        );
11656    }
11657
11658    #[test]
11659    fn autores_projects_slice_by_borrow() {
11660        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
11661        // borrow — the returned slice borrows the underlying
11662        // `Vec<String>` storage of the `:autores` slot and the
11663        // accessor must not clone the backing `Vec` on every call.
11664        // Peer of the per-`:membros`
11665        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
11666        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
11667        // (0dcc926) / per-`:placement`
11668        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
11669        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
11670        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
11671        // typed-slot `&[T]`-return axes, extended onto the outer top-
11672        // level [`Caixa`] universal-axis `&[String]` shape — the
11673        // accessor's returned slice must borrow from `&self` (the
11674        // returned reference's lifetime is tied to `&self`), and
11675        // calling the accessor twice on the same [`Caixa`] must yield
11676        // slices that are pointer-equal (the underlying byte-buffer is
11677        // the storage `Vec`'s allocation, not a fresh copy) as well as
11678        // value-equal (idempotent, no side effects on `&self`).
11679        //
11680        // Pins against a future silent detour that returned an owned
11681        // `Vec<String>` (which would type-check but silently clone on
11682        // every call, breaking the zero-cost projection every peer
11683        // sibling slice accessor carries), a `&Vec<String>` return
11684        // (which would leak the backing `Vec`'s grow/push/reserve
11685        // surface no downstream consumer reaches for), or a one-arm-
11686        // only accessor that returned a saturating value on some
11687        // sentinel input (breaking the pass-through invariant the
11688        // sibling slice accessors carry).
11689        for autores in [
11690            vec![],
11691            vec!["pleme-io"],
11692            vec!["alice", "bob"],
11693            vec!["pleme-io", "pleme-io"],
11694        ] {
11695            let c = caixa_with_autores(autores.clone());
11696            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
11697            let first = c.autores();
11698            let second = c.autores();
11699            assert_eq!(
11700                first, second,
11701                "Caixa::autores must be idempotent — two successive \
11702                 calls on the same &self must return the same \
11703                 &[String]",
11704            );
11705            assert_eq!(
11706                first.as_ptr(),
11707                second.as_ptr(),
11708                "Caixa::autores must borrow the underlying Vec<String> \
11709                 storage — two successive calls must return slices \
11710                 with the same backing pointer (a fresh Vec<String> \
11711                 clone would change the pointer on every call)",
11712            );
11713            assert_eq!(
11714                first,
11715                expected.as_slice(),
11716                "Caixa::autores must return :autores verbatim by \
11717                 borrow — got {first:?}, expected {expected:?}",
11718            );
11719        }
11720    }
11721
11722    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
11723
11724    #[test]
11725    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
11726        // The canonical per-`Caixa` `:etiquetas` universal-axis
11727        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
11728        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
11729        // as a `&[String]`, byte-equal to the raw
11730        // `self.etiquetas.as_slice()` access across every representative
11731        // value in the accept-set — `[]` (the "no tags declared" arm
11732        // every existing fixture without an `:etiquetas` line carries),
11733        // `[""]` (a past-the-guard sentinel that pins the accessor
11734        // doesn't perform a silent `[""] → []` collapse on the empty-
11735        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
11736        // but the accessor must ship the raw slot verbatim so a
11737        // validate-time gate regression surfaces at the caixa-helm emit
11738        // boundary rather than being silently absorbed into a keyword-
11739        // drop), `["demo"]` (the canonical single-tag form every
11740        // `feira init` template scaffolds), `["example", "aplicacao",
11741        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
11742        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
11743        // (a past-the-guard duplicate sentinel — validate rejects
11744        // through `EtiquetaDuplicate` but the accessor must ship the
11745        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
11746        // at chart-render time isn't silently promoted into the
11747        // accessor boundary and struct-literal
11748        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
11749        // fixtures continue to expose the duplicate at the accessor).
11750        //
11751        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
11752        // pin on the substrate primitive — folds on the "outer
11753        // [`Caixa`] `&[T]` slice" projection pattern
11754        // `autores_returns_autores_slice_verbatim_across_permutations`
11755        // (b5d813f) opened, sibling in shape and idiom. Pins against a
11756        // future silent detour that returned an owned `Vec<String>`
11757        // (which would type-check but silently clone on every accessor
11758        // call, breaking the zero-cost projection every peer sibling
11759        // slice accessor carries), a `[""] → []` collapse (which would
11760        // silently absorb the `EtiquetaEmpty` refusal case at the
11761        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
11762        // (which would silently absorb the `EtiquetaDuplicate` refusal
11763        // case at the accessor boundary — the caixa-helm chart-render
11764        // `BTreeSet::collect` dedup is downstream of the accessor and
11765        // must not be silently promoted into it).
11766        for etiquetas in [
11767            vec![],
11768            vec![""],
11769            vec!["demo"],
11770            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
11771            vec!["demo", "demo"],
11772        ] {
11773            let c = caixa_with_etiquetas(etiquetas.clone());
11774            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
11775            assert_eq!(
11776                c.etiquetas(),
11777                expected.as_slice(),
11778                "Caixa::etiquetas must return :etiquetas verbatim (got \
11779                 {:?}, expected {expected:?})",
11780                c.etiquetas(),
11781            );
11782            assert_eq!(
11783                c.etiquetas(),
11784                c.etiquetas.as_slice(),
11785                "Caixa::etiquetas must byte-equal the raw \
11786                 `self.etiquetas.as_slice()` field access across every \
11787                 value in the Vec<String> accept-set",
11788            );
11789        }
11790    }
11791
11792    #[test]
11793    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
11794        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
11795        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
11796        // `&self.etiquetas` field-borrow walk. Structurally: a
11797        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
11798        // `EtiquetaEmpty` refusal exactly, and a
11799        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
11800        // single-tag form) must pass validate. The pair jointly pins
11801        // the accessor + validate-gate composition: any future silent
11802        // detour that had the accessor return an empty slice on the
11803        // `[""]` arm (a
11804        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
11805        // silently absorb the `EtiquetaEmpty` refusal at the accessor
11806        // boundary and the validate gate would accept a struct-literal
11807        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
11808        // pin catches that at caixa-core build time.
11809        //
11810        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
11811        // through_accessor` (b5d813f) accessor-composition pin on the
11812        // sibling `&[T]`-composition axis — same "the validate / shape-
11813        // gate predicate must route through the substrate-primitive
11814        // typed dispatch" discipline extended onto the sibling outer
11815        // top-level [`Caixa`] `&[T]`-composition surface.
11816        let c = caixa_with_etiquetas(vec![""]);
11817        assert!(
11818            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
11819            "validate_etiquetas must reject etiquetas == vec![\"\"] \
11820             with EtiquetaEmpty — the accessor and the validate gate \
11821             must route through the same substrate-primitive typed \
11822             dispatch on the :etiquetas per-entry empty arm",
11823        );
11824        let c = caixa_with_etiquetas(vec!["demo"]);
11825        assert!(
11826            c.validate_etiquetas().is_ok(),
11827            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
11828             (the canonical single-tag shape every `feira init` \
11829             template scaffolds)",
11830        );
11831    }
11832
11833    #[test]
11834    fn etiquetas_projects_slice_by_borrow() {
11835        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
11836        // by borrow — the returned slice borrows the underlying
11837        // `Vec<String>` storage of the `:etiquetas` slot and the
11838        // accessor must not clone the backing `Vec` on every call.
11839        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
11840        // (b5d813f) by-borrow pin on the sibling outer top-level
11841        // [`Caixa`] `&[String]`-return axis — the accessor's returned
11842        // slice must borrow from `&self` (the returned reference's
11843        // lifetime is tied to `&self`), and calling the accessor twice
11844        // on the same [`Caixa`] must yield slices that are pointer-
11845        // equal (the underlying byte-buffer is the storage `Vec`'s
11846        // allocation, not a fresh copy) as well as value-equal
11847        // (idempotent, no side effects on `&self`).
11848        //
11849        // Pins against a future silent detour that returned an owned
11850        // `Vec<String>` (which would type-check but silently clone on
11851        // every call, breaking the zero-cost projection every peer
11852        // sibling slice accessor carries), a `&Vec<String>` return
11853        // (which would leak the backing `Vec`'s grow/push/reserve
11854        // surface no downstream consumer reaches for), or a one-arm-
11855        // only accessor that returned a saturating value on some
11856        // sentinel input (breaking the pass-through invariant the
11857        // sibling slice accessors carry).
11858        for etiquetas in [
11859            vec![],
11860            vec!["demo"],
11861            vec!["example", "aplicacao", "mesh"],
11862            vec!["demo", "demo"],
11863        ] {
11864            let c = caixa_with_etiquetas(etiquetas.clone());
11865            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
11866            let first = c.etiquetas();
11867            let second = c.etiquetas();
11868            assert_eq!(
11869                first, second,
11870                "Caixa::etiquetas must be idempotent — two successive \
11871                 calls on the same &self must return the same \
11872                 &[String]",
11873            );
11874            assert_eq!(
11875                first.as_ptr(),
11876                second.as_ptr(),
11877                "Caixa::etiquetas must borrow the underlying \
11878                 Vec<String> storage — two successive calls must \
11879                 return slices with the same backing pointer (a fresh \
11880                 Vec<String> clone would change the pointer on every \
11881                 call)",
11882            );
11883            assert_eq!(
11884                first,
11885                expected.as_slice(),
11886                "Caixa::etiquetas must return :etiquetas verbatim by \
11887                 borrow — got {first:?}, expected {expected:?}",
11888            );
11889        }
11890    }
11891
11892    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
11893
11894    #[test]
11895    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
11896        // The canonical per-`Caixa` `:bibliotecas` universal-axis
11897        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
11898        // must return the `:bibliotecas` typed [`Vec<String>`] list
11899        // verbatim as a `&[String]`, byte-equal to the raw
11900        // `self.bibliotecas.as_slice()` access across every
11901        // representative value in the accept-set — `[]` (the "no
11902        // libraries declared" arm every `:kind` other than `Biblioteca`
11903        // + every `Biblioteca` relying on the canonical
11904        // `lib/<nome>.lisp` implicit-default path carries; the
11905        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
11906        // fires exactly on this empty-slot + `Biblioteca`-kind
11907        // combination), `[""]` (a past-the-guard sentinel that pins
11908        // the accessor doesn't perform a silent `[""] → []` collapse
11909        // on the empty-entry arm — validate rejects `[""]` through
11910        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
11911        // must ship the raw slot verbatim so a validate-time gate
11912        // regression surfaces at the `feira build` phase-1 parse
11913        // boundary rather than being silently absorbed into a
11914        // library-drop), `["lib/demo.lisp"]` (the canonical single-
11915        // entry form `Caixa::template` scaffolds and every `feira init`
11916        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
11917        // (the canonical multi-library form the
11918        // `validate_code_paths_accepts_explicit_relative_paths_on_
11919        // every_slot` fixture emits), and `["lib/foo.lisp",
11920        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
11921        // validate rejects through `CodePathDuplicate { slot:
11922        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
11923        // but the accessor must ship the raw slot verbatim so the
11924        // `feira build` `for entry in caixa.bibliotecas()` parse walk
11925        // sees the duplicate at the accessor boundary and struct-
11926        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
11927        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
11928        // the duplicate at the accessor).
11929        //
11930        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
11931        // pin on the substrate primitive — folds on the "outer
11932        // [`Caixa`] `&[T]` slice" projection pattern
11933        // `autores_returns_autores_slice_verbatim_across_permutations`
11934        // (b5d813f) opened and
11935        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
11936        // (78c7d3c) folded on, sibling in shape and idiom. Pins
11937        // against a future silent detour that returned an owned
11938        // `Vec<String>` (which would type-check but silently clone on
11939        // every accessor call, breaking the zero-cost projection
11940        // every peer sibling slice accessor carries), a `[""] → []`
11941        // collapse (which would silently absorb the `CodePathEmpty`
11942        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
11943        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
11944        // would silently absorb the `CodePathDuplicate` refusal case
11945        // at the accessor boundary — the per-slot set-not-multiset
11946        // gate is downstream of the accessor and must not be silently
11947        // promoted into it).
11948        for bibliotecas in [
11949            vec![],
11950            vec![""],
11951            vec!["lib/demo.lisp"],
11952            vec!["lib/demo.lisp", "lib/helpers.lisp"],
11953            vec!["lib/foo.lisp", "lib/foo.lisp"],
11954        ] {
11955            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
11956            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
11957            assert_eq!(
11958                c.bibliotecas(),
11959                expected.as_slice(),
11960                "Caixa::bibliotecas must return :bibliotecas verbatim \
11961                 (got {:?}, expected {expected:?})",
11962                c.bibliotecas(),
11963            );
11964            assert_eq!(
11965                c.bibliotecas(),
11966                c.bibliotecas.as_slice(),
11967                "Caixa::bibliotecas must byte-equal the raw \
11968                 `self.bibliotecas.as_slice()` field access across \
11969                 every value in the Vec<String> accept-set",
11970            );
11971        }
11972    }
11973
11974    #[test]
11975    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
11976        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
11977        // empty-arm gate on the `:bibliotecas` slot must key off
11978        // [`Caixa::bibliotecas`], not a divergent raw
11979        // `&self.bibliotecas` field-borrow walk. Structurally: a
11980        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
11981        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
11982        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
11983        // into()], .. }` (the canonical single-library form
11984        // `Caixa::template` scaffolds) must pass validate. The pair
11985        // jointly pins the accessor + validate-gate composition: any
11986        // future silent detour that had the accessor return an empty
11987        // slice on the `[""]` arm (a `.iter().filter(|s|
11988        // !s.is_empty()).collect()` collapse) would silently absorb
11989        // the `CodePathEmpty` refusal at the accessor boundary and
11990        // the validate gate would accept a struct-literal
11991        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
11992        // composition pin catches that at caixa-core build time.
11993        //
11994        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
11995        // through_accessor` (b5d813f) and
11996        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
11997        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
11998        // composition axes — same "the validate / shape-gate
11999        // predicate must route through the substrate-primitive typed
12000        // dispatch" discipline extended onto the sibling outer top-
12001        // level [`Caixa`] `&[T]`-composition surface. Nominally the
12002        // in-tree `validate_code_paths` production body still keys
12003        // off the internal `[(":bibliotecas", &self.bibliotecas,
12004        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12005        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12006        // (the tuple's homogeneous slice-typed shape blocks a per-
12007        // element accessor swap in isolation — a future companion
12008        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
12009        // `&[T]` slice-accessor axis closes that tuple onto the
12010        // triple of typed dispatches as a unit); the composition pin
12011        // catches any future accessor-side silent filter drop against
12012        // that eventual tuple-closure regardless of whether the
12013        // `:bibliotecas` slot is threaded through the accessor or the
12014        // raw field access at the tuple's construction site.
12015        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
12016        assert!(
12017            matches!(
12018                c.validate_code_paths(),
12019                Err(ManifestError::CodePathEmpty {
12020                    slot: ":bibliotecas"
12021                })
12022            ),
12023            "validate_code_paths must reject bibliotecas == vec![\"\"] \
12024             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
12025             accessor and the validate gate must route through the \
12026             same substrate-primitive typed dispatch on the \
12027             :bibliotecas per-entry empty arm",
12028        );
12029        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
12030        assert!(
12031            c.validate_code_paths().is_ok(),
12032            "validate_code_paths must accept bibliotecas == \
12033             vec![\"lib/demo.lisp\"] (the canonical single-library \
12034             shape every `feira init` template scaffolds)",
12035        );
12036    }
12037
12038    #[test]
12039    fn bibliotecas_projects_slice_by_borrow() {
12040        // The by-borrow pin: [`Caixa::bibliotecas`] returns
12041        // `&[String]` by borrow — the returned slice borrows the
12042        // underlying `Vec<String>` storage of the `:bibliotecas` slot
12043        // and the accessor must not clone the backing `Vec` on every
12044        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12045        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
12046        // by-borrow pins on the sibling outer top-level [`Caixa`]
12047        // `&[String]`-return axes — the accessor's returned slice
12048        // must borrow from `&self` (the returned reference's lifetime
12049        // is tied to `&self`), and calling the accessor twice on the
12050        // same [`Caixa`] must yield slices that are pointer-equal
12051        // (the underlying byte-buffer is the storage `Vec`'s
12052        // allocation, not a fresh copy) as well as value-equal
12053        // (idempotent, no side effects on `&self`).
12054        //
12055        // Pins against a future silent detour that returned an owned
12056        // `Vec<String>` (which would type-check but silently clone on
12057        // every call, breaking the zero-cost projection every peer
12058        // sibling slice accessor carries), a `&Vec<String>` return
12059        // (which would leak the backing `Vec`'s grow/push/reserve
12060        // surface no downstream consumer reaches for), or a one-arm-
12061        // only accessor that returned a saturating value on some
12062        // sentinel input (breaking the pass-through invariant the
12063        // sibling slice accessors carry).
12064        for bibliotecas in [
12065            vec![],
12066            vec!["lib/demo.lisp"],
12067            vec!["lib/demo.lisp", "lib/helpers.lisp"],
12068            vec!["lib/foo.lisp", "lib/foo.lisp"],
12069        ] {
12070            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
12071            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
12072            let first = c.bibliotecas();
12073            let second = c.bibliotecas();
12074            assert_eq!(
12075                first, second,
12076                "Caixa::bibliotecas must be idempotent — two \
12077                 successive calls on the same &self must return the \
12078                 same &[String]",
12079            );
12080            assert_eq!(
12081                first.as_ptr(),
12082                second.as_ptr(),
12083                "Caixa::bibliotecas must borrow the underlying \
12084                 Vec<String> storage — two successive calls must \
12085                 return slices with the same backing pointer (a \
12086                 fresh Vec<String> clone would change the pointer on \
12087                 every call)",
12088            );
12089            assert_eq!(
12090                first,
12091                expected.as_slice(),
12092                "Caixa::bibliotecas must return :bibliotecas verbatim \
12093                 by borrow — got {first:?}, expected {expected:?}",
12094            );
12095        }
12096    }
12097
12098    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
12099
12100    #[test]
12101    fn exe_returns_exe_slice_verbatim_across_permutations() {
12102        // The canonical per-`Caixa` `:exe` universal-axis
12103        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
12104        // must return the `:exe` typed [`Vec<String>`] list verbatim as
12105        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
12106        // access across every representative value in the accept-set —
12107        // `[]` (the "no executable declared" arm every `:kind` other
12108        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
12109        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
12110        // + `Binario`-kind combination), `[""]` (a past-the-guard
12111        // sentinel that pins the accessor doesn't perform a silent
12112        // `[""] → []` collapse on the empty-entry arm — validate rejects
12113        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
12114        // accessor must ship the raw slot verbatim so a validate-time
12115        // gate regression surfaces at the layout / `feira nix` boundary
12116        // rather than being silently absorbed into an executable-drop),
12117        // `["exe/cli"]` (the canonical single-entry Binario form every
12118        // in-tree `caixa_with_code_paths` positive control uses),
12119        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
12120        // form the `validate_code_paths_accepts_explicit_relative_paths_
12121        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
12122        // (a past-the-guard duplicate sentinel — validate rejects
12123        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
12124        // set-not-multiset gate, but the accessor must ship the raw
12125        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
12126        // into(), "exe/cli".into()], .. }` fixtures continue to expose
12127        // the duplicate at the accessor).
12128        //
12129        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
12130        // pin on the substrate primitive — folds on the "outer
12131        // [`Caixa`] `&[T]` slice" projection pattern
12132        // `autores_returns_autores_slice_verbatim_across_permutations`
12133        // (b5d813f) opened,
12134        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12135        // (78c7d3c) folded on, and
12136        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12137        // (8a36c23) closed the universal-axis text-tag family of.
12138        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
12139        // the sibling `:servicos` future lift closes onto. Pins against
12140        // a future silent detour that returned an owned `Vec<String>`
12141        // (which would type-check but silently clone on every accessor
12142        // call, breaking the zero-cost projection every peer sibling
12143        // slice accessor carries), a `[""] → []` collapse (which would
12144        // silently absorb the `CodePathEmpty` refusal case at the
12145        // accessor boundary), or an `["exe/cli", "exe/cli"] →
12146        // ["exe/cli"]` dedup collapse (which would silently absorb the
12147        // `CodePathDuplicate` refusal case at the accessor boundary —
12148        // the per-slot set-not-multiset gate is downstream of the
12149        // accessor and must not be silently promoted into it).
12150        for exe in [
12151            vec![],
12152            vec![""],
12153            vec!["exe/cli"],
12154            vec!["exe/cli", "exe/serve"],
12155            vec!["exe/cli", "exe/cli"],
12156        ] {
12157            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
12158            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
12159            assert_eq!(
12160                c.exe(),
12161                expected.as_slice(),
12162                "Caixa::exe must return :exe verbatim (got {:?}, \
12163                 expected {expected:?})",
12164                c.exe(),
12165            );
12166            assert_eq!(
12167                c.exe(),
12168                c.exe.as_slice(),
12169                "Caixa::exe must byte-equal the raw \
12170                 `self.exe.as_slice()` field access across every value \
12171                 in the Vec<String> accept-set",
12172            );
12173        }
12174    }
12175
12176    #[test]
12177    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
12178        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12179        // empty-arm gate on the `:exe` slot must key off
12180        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
12181        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
12182        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
12183        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
12184        // (the canonical single-executable form every in-tree
12185        // `caixa_with_code_paths` positive control uses) must pass
12186        // validate. The pair jointly pins the accessor + validate-gate
12187        // composition: any future silent detour that had the accessor
12188        // return an empty slice on the `[""]` arm (a
12189        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
12190        // silently absorb the `CodePathEmpty` refusal at the accessor
12191        // boundary and the validate gate would accept a struct-literal
12192        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
12193        // catches that at caixa-core build time.
12194        //
12195        // Peer of the per-`Caixa`
12196        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12197        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
12198        // (b5d813f), and
12199        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12200        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12201        // composition axes — same "the validate / shape-gate predicate
12202        // must route through the substrate-primitive typed dispatch"
12203        // discipline extended onto the sibling outer top-level [`Caixa`]
12204        // `&[T]`-composition surface. Nominally the in-tree
12205        // `validate_code_paths` production body still keys off the
12206        // internal `[(":bibliotecas", &self.bibliotecas,
12207        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12208        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12209        // (the tuple's homogeneous slice-typed shape blocks a per-
12210        // element accessor swap in isolation — a future companion lift
12211        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
12212        // accessor axis closes that tuple onto the triple of typed
12213        // dispatches as a unit); the composition pin catches any future
12214        // accessor-side silent filter drop against that eventual tuple-
12215        // closure regardless of whether the `:exe` slot is threaded
12216        // through the accessor or the raw field access at the tuple's
12217        // construction site.
12218        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
12219        assert!(
12220            matches!(
12221                c.validate_code_paths(),
12222                Err(ManifestError::CodePathEmpty { slot: ":exe" })
12223            ),
12224            "validate_code_paths must reject exe == vec![\"\"] \
12225             with CodePathEmpty {{ slot: \":exe\" }} — the \
12226             accessor and the validate gate must route through the \
12227             same substrate-primitive typed dispatch on the \
12228             :exe per-entry empty arm",
12229        );
12230        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
12231        assert!(
12232            c.validate_code_paths().is_ok(),
12233            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
12234             (the canonical single-executable shape every in-tree \
12235             `caixa_with_code_paths` positive control uses)",
12236        );
12237    }
12238
12239    #[test]
12240    fn exe_projects_slice_by_borrow() {
12241        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
12242        // borrow — the returned slice borrows the underlying
12243        // `Vec<String>` storage of the `:exe` slot and the accessor
12244        // must not clone the backing `Vec` on every call. Peer of the
12245        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
12246        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
12247        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
12248        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
12249        // return axes — the accessor's returned slice must borrow from
12250        // `&self` (the returned reference's lifetime is tied to
12251        // `&self`), and calling the accessor twice on the same
12252        // [`Caixa`] must yield slices that are pointer-equal (the
12253        // underlying byte-buffer is the storage `Vec`'s allocation,
12254        // not a fresh copy) as well as value-equal (idempotent, no
12255        // side effects on `&self`).
12256        //
12257        // Pins against a future silent detour that returned an owned
12258        // `Vec<String>` (which would type-check but silently clone on
12259        // every call, breaking the zero-cost projection every peer
12260        // sibling slice accessor carries), a `&Vec<String>` return
12261        // (which would leak the backing `Vec`'s grow/push/reserve
12262        // surface no downstream consumer reaches for), or a one-arm-
12263        // only accessor that returned a saturating value on some
12264        // sentinel input (breaking the pass-through invariant the
12265        // sibling slice accessors carry).
12266        for exe in [
12267            vec![],
12268            vec!["exe/cli"],
12269            vec!["exe/cli", "exe/serve"],
12270            vec!["exe/cli", "exe/cli"],
12271        ] {
12272            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
12273            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
12274            let first = c.exe();
12275            let second = c.exe();
12276            assert_eq!(
12277                first, second,
12278                "Caixa::exe must be idempotent — two successive calls \
12279                 on the same &self must return the same &[String]",
12280            );
12281            assert_eq!(
12282                first.as_ptr(),
12283                second.as_ptr(),
12284                "Caixa::exe must borrow the underlying Vec<String> \
12285                 storage — two successive calls must return slices \
12286                 with the same backing pointer (a fresh Vec<String> \
12287                 clone would change the pointer on every call)",
12288            );
12289            assert_eq!(
12290                first,
12291                expected.as_slice(),
12292                "Caixa::exe must return :exe verbatim by borrow — \
12293                 got {first:?}, expected {expected:?}",
12294            );
12295        }
12296    }
12297
12298    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
12299
12300    #[test]
12301    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
12302        // The canonical per-`Caixa` `:servicos` universal-axis
12303        // ComputeUnit-CR-YAML-entry-path-list slice pin:
12304        // [`Caixa::servicos`] must return the `:servicos` typed
12305        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
12306        // the raw `self.servicos.as_slice()` access across every
12307        // representative value in the accept-set — `[]` (the "no
12308        // ComputeUnit-CR declared" arm every `:kind` other than
12309        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
12310        // `ServicoWithoutServicos` arm-gate fires exactly on this
12311        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
12312        // guard sentinel that pins the accessor doesn't perform a
12313        // silent `[""] → []` collapse on the empty-entry arm — validate
12314        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
12315        // but the accessor must ship the raw slot verbatim so a
12316        // validate-time gate regression surfaces at the layout /
12317        // per-Servico renderer boundary rather than being silently
12318        // absorbed into a component-drop),
12319        // `["servicos/demo.computeunit.yaml"]` (the canonical
12320        // singleton V0-shape every in-tree `caixa_with_code_paths`
12321        // positive control uses; the same shape
12322        // [`crate::require_single_servico`] admits),
12323        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
12324        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
12325        // singularity gate rejects through `ServicoCountMismatch
12326        // { count: 2 }` but the accessor must ship the raw slot
12327        // verbatim so struct-literal `Caixa { servicos: vec![...,
12328        // ...], .. }` fixtures continue to expose the count at the
12329        // accessor), and `["servicos/a.computeunit.yaml",
12330        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
12331        // sentinel — validate rejects through
12332        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
12333        // set-not-multiset gate, but the accessor must ship the raw
12334        // slot verbatim so struct-literal fixtures continue to expose
12335        // the duplicate at the accessor).
12336        //
12337        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
12338        // slice accessor pin on the substrate primitive — folds on the
12339        // "outer [`Caixa`] `&[T]` slice" projection pattern
12340        // `autores_returns_autores_slice_verbatim_across_permutations`
12341        // (b5d813f) opened,
12342        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12343        // (78c7d3c) folded on,
12344        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12345        // (8a36c23) closed the universal-axis text-tag family of, and
12346        // `exe_returns_exe_slice_verbatim_across_permutations`
12347        // (65d9527) opened the foreign-code-slot sub-family of. Closes
12348        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
12349        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
12350        // `:servicos`) now each carries a substrate-canonical slice
12351        // accessor. Pins against a future silent detour that returned
12352        // an owned `Vec<String>` (which would type-check but silently
12353        // clone on every accessor call, breaking the zero-cost
12354        // projection every peer sibling slice accessor carries), a
12355        // `[""] → []` collapse (which would silently absorb the
12356        // `CodePathEmpty` refusal case at the accessor boundary), an
12357        // `[a, a] → [a]` dedup collapse (which would silently absorb
12358        // the `CodePathDuplicate` refusal case at the accessor
12359        // boundary — the per-slot set-not-multiset gate is downstream
12360        // of the accessor and must not be silently promoted into it),
12361        // or a `[a, b] → [a]` singleton collapse (which would silently
12362        // absorb the V0 `ServicoCountMismatch` refusal case at the
12363        // accessor boundary — the V0 singularity gate is downstream of
12364        // the accessor and must not be silently promoted into it).
12365        for servicos in [
12366            vec![],
12367            vec![""],
12368            vec!["servicos/demo.computeunit.yaml"],
12369            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
12370            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
12371        ] {
12372            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
12373            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
12374            assert_eq!(
12375                c.servicos(),
12376                expected.as_slice(),
12377                "Caixa::servicos must return :servicos verbatim (got \
12378                 {:?}, expected {expected:?})",
12379                c.servicos(),
12380            );
12381            assert_eq!(
12382                c.servicos(),
12383                c.servicos.as_slice(),
12384                "Caixa::servicos must byte-equal the raw \
12385                 `self.servicos.as_slice()` field access across every \
12386                 value in the Vec<String> accept-set",
12387            );
12388        }
12389    }
12390
12391    #[test]
12392    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
12393        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12394        // empty-arm gate on the `:servicos` slot must key off
12395        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
12396        // field-borrow walk. Structurally: a `Caixa { servicos:
12397        // vec!["".into()], .. }` must surface the `CodePathEmpty
12398        // { slot: ":servicos" }` refusal exactly, and a `Caixa
12399        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
12400        // .. }` (the canonical singleton V0-shape every in-tree
12401        // `caixa_with_code_paths` positive control uses) must pass
12402        // validate. The pair jointly pins the accessor + validate-gate
12403        // composition: any future silent detour that had the accessor
12404        // return an empty slice on the `[""]` arm (a `.iter().filter
12405        // (|s| !s.is_empty()).collect()` collapse) would silently
12406        // absorb the `CodePathEmpty` refusal at the accessor boundary
12407        // and the validate gate would accept a struct-literal
12408        // `Caixa { servicos: vec!["".into()], .. }` — the composition
12409        // pin catches that at caixa-core build time.
12410        //
12411        // Peer of the per-`Caixa`
12412        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12413        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
12414        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
12415        // (b5d813f), and
12416        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12417        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12418        // composition axes — same "the validate / shape-gate predicate
12419        // must route through the substrate-primitive typed dispatch"
12420        // discipline extended onto the sibling outer top-level
12421        // [`Caixa`] `&[T]`-composition surface, closing the trio of
12422        // code-surface accessor-composition pins on the same axis.
12423        // Nominally the in-tree `validate_code_paths` production body
12424        // still keys off the internal
12425        // `[(":bibliotecas", &self.bibliotecas,
12426        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12427        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12428        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
12429        // per-element accessor swap in isolation — a future companion
12430        // lift promotes the tuple's element type to `&[String]` and
12431        // threads the triple of typed dispatches through as a unit);
12432        // the composition pin catches any future accessor-side silent
12433        // filter drop against that eventual tuple-closure regardless
12434        // of whether the `:servicos` slot is threaded through the
12435        // accessor or the raw field access at the tuple's construction
12436        // site.
12437        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
12438        assert!(
12439            matches!(
12440                c.validate_code_paths(),
12441                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
12442            ),
12443            "validate_code_paths must reject servicos == vec![\"\"] \
12444             with CodePathEmpty {{ slot: \":servicos\" }} — the \
12445             accessor and the validate gate must route through the \
12446             same substrate-primitive typed dispatch on the \
12447             :servicos per-entry empty arm",
12448        );
12449        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
12450        assert!(
12451            c.validate_code_paths().is_ok(),
12452            "validate_code_paths must accept servicos == \
12453             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
12454             singleton V0-shape every in-tree `caixa_with_code_paths` \
12455             positive control uses)",
12456        );
12457    }
12458
12459    #[test]
12460    fn servicos_projects_slice_by_borrow() {
12461        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
12462        // borrow — the returned slice borrows the underlying
12463        // `Vec<String>` storage of the `:servicos` slot and the
12464        // accessor must not clone the backing `Vec` on every call.
12465        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12466        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
12467        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
12468        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
12469        // the sibling outer top-level [`Caixa`] `&[String]`-return
12470        // axes — the accessor's returned slice must borrow from
12471        // `&self` (the returned reference's lifetime is tied to
12472        // `&self`), and calling the accessor twice on the same
12473        // [`Caixa`] must yield slices that are pointer-equal (the
12474        // underlying byte-buffer is the storage `Vec`'s allocation,
12475        // not a fresh copy) as well as value-equal (idempotent, no
12476        // side effects on `&self`).
12477        //
12478        // Pins against a future silent detour that returned an owned
12479        // `Vec<String>` (which would type-check but silently clone on
12480        // every call, breaking the zero-cost projection every peer
12481        // sibling slice accessor carries), a `&Vec<String>` return
12482        // (which would leak the backing `Vec`'s grow/push/reserve
12483        // surface no downstream consumer reaches for), or a one-arm-
12484        // only accessor that returned a saturating value on some
12485        // sentinel input (breaking the pass-through invariant the
12486        // sibling slice accessors carry).
12487        for servicos in [
12488            vec![],
12489            vec!["servicos/demo.computeunit.yaml"],
12490            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
12491            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
12492        ] {
12493            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
12494            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
12495            let first = c.servicos();
12496            let second = c.servicos();
12497            assert_eq!(
12498                first, second,
12499                "Caixa::servicos must be idempotent — two successive \
12500                 calls on the same &self must return the same &[String]",
12501            );
12502            assert_eq!(
12503                first.as_ptr(),
12504                second.as_ptr(),
12505                "Caixa::servicos must borrow the underlying \
12506                 Vec<String> storage — two successive calls must \
12507                 return slices with the same backing pointer (a fresh \
12508                 Vec<String> clone would change the pointer on every \
12509                 call)",
12510            );
12511            assert_eq!(
12512                first,
12513                expected.as_slice(),
12514                "Caixa::servicos must return :servicos verbatim by \
12515                 borrow — got {first:?}, expected {expected:?}",
12516            );
12517        }
12518    }
12519
12520    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
12521
12522    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
12523        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12524        c.deps = deps;
12525        c
12526    }
12527
12528    #[test]
12529    fn deps_returns_deps_slice_verbatim_across_permutations() {
12530        // The canonical per-`Caixa` `:deps` universal-axis runtime-
12531        // dependency-declaration-list slice pin: [`Caixa::deps`] must
12532        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
12533        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
12534        // access across every representative value in the accept-set —
12535        // `[]` (the "no runtime deps declared" arm every existing
12536        // fixture without a `:deps` line carries; the
12537        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
12538        // single-entry list (the shape most consumer caixas carry), a
12539        // canonical two-entry list (the multi-dep runtime closure), and
12540        // two past-the-guard sentinels — a `[""]`-`:nome` entry
12541        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
12542        // `NomeInvalid` but the accessor must ship the raw slot
12543        // verbatim) and a `[a, a]` duplicate (validate rejects through
12544        // `DuplicateNome { list: ":deps" }` but the accessor must ship
12545        // the raw slot verbatim so struct-literal fixtures continue to
12546        // expose the duplicate at the accessor).
12547        //
12548        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
12549        // pin on the substrate primitive — opens the outer-`Caixa`
12550        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
12551        // future lift closes on. Peer of the closed outer-`Caixa`
12552        // foreign-code-slot `&[String]` sub-family
12553        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12554        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
12555        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
12556        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
12557        // (`autores_returns_autores_slice_verbatim_across_permutations`
12558        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12559        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
12560        // projection pattern onto a novel element-type axis (`Dep`
12561        // composite vs the prior sibling family's `String` scalar).
12562        // Pins against a future silent detour that returned an owned
12563        // `Vec<Dep>` (which would type-check but silently clone on every
12564        // accessor call, breaking the zero-cost projection every peer
12565        // sibling slice accessor carries), a `[""] → []` collapse (which
12566        // would silently absorb the `NomeEmpty` refusal case at the
12567        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
12568        // would silently absorb the `DuplicateNome` refusal case at the
12569        // accessor boundary).
12570        for deps in [
12571            vec![],
12572            vec![Dep::simple("", "^0.1")],
12573            vec![Dep::simple("caixa-teia", "^0.1")],
12574            vec![
12575                Dep::simple("caixa-teia", "^0.1"),
12576                Dep::simple("caixa-core", "^0.1"),
12577            ],
12578            vec![
12579                Dep::simple("caixa-teia", "^0.1"),
12580                Dep::simple("caixa-teia", "^0.2"),
12581            ],
12582        ] {
12583            let c = caixa_with_deps(deps.clone());
12584            assert_eq!(
12585                c.deps(),
12586                deps.as_slice(),
12587                "Caixa::deps must return :deps verbatim (got {:?}, \
12588                 expected {deps:?})",
12589                c.deps(),
12590            );
12591            assert_eq!(
12592                c.deps(),
12593                c.deps.as_slice(),
12594                "Caixa::deps must element-equal the raw \
12595                 `self.deps.as_slice()` field access across every \
12596                 value in the Vec<Dep> accept-set",
12597            );
12598        }
12599    }
12600
12601    #[test]
12602    fn validate_deps_duplicate_arm_routes_through_accessor() {
12603        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
12604        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
12605        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
12606        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
12607        // "^0.2")], .. }` must surface the `DuplicateNome { list:
12608        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
12609        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
12610        // form) must pass validate. The pair jointly pins the accessor +
12611        // validate-gate composition: any future silent detour that had
12612        // the accessor return a dedupped slice on the `[a, a]` arm (a
12613        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
12614        // would silently absorb the `DuplicateNome` refusal at the
12615        // accessor boundary and the validate gate would accept a
12616        // struct-literal `Caixa` carrying the drift — the composition
12617        // pin catches that at caixa-core build time.
12618        //
12619        // Peer of the per-`Caixa`
12620        // `validate_autores_empty_entry_arm_routes_through_accessor`
12621        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12622        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12623        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
12624        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
12625        // (611f78b) accessor-composition pins on the sibling `&[T]`-
12626        // composition axes — same "the validate gate must route through
12627        // the substrate-primitive typed dispatch" discipline extended
12628        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
12629        // composition surface, opening the outer-`Caixa` dependency-slot
12630        // arm of the composition-pin family.
12631        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
12632        let err = c.validate_deps().unwrap_err();
12633        assert!(
12634            matches!(
12635                err,
12636                DepError::DuplicateNome { ref nome, list } if nome == "d"
12637                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
12638            ),
12639            "validate_deps must reject deps == \
12640             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
12641             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
12642             accessor and the validate gate must route through the \
12643             same substrate-primitive typed dispatch on the :deps \
12644             within-list duplicate arm (got {err:?})",
12645        );
12646        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
12647        assert!(
12648            c.validate_deps().is_ok(),
12649            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
12650             (the canonical single-entry form)",
12651        );
12652    }
12653
12654    #[test]
12655    fn deps_projects_slice_by_borrow() {
12656        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
12657        // — the returned slice borrows the underlying `Vec<Dep>` storage
12658        // of the `:deps` slot and the accessor must not clone the
12659        // backing `Vec` on every call. Peer of the per-`Caixa`
12660        // `autores_projects_slice_by_borrow` (b5d813f),
12661        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
12662        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
12663        // `exe_projects_slice_by_borrow` (65d9527), and
12664        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
12665        // on the sibling outer top-level [`Caixa`] `&[String]`-return
12666        // axes — the accessor's returned slice must borrow from `&self`
12667        // (the returned reference's lifetime is tied to `&self`), and
12668        // calling the accessor twice on the same [`Caixa`] must yield
12669        // slices that are pointer-equal (the underlying byte-buffer is
12670        // the storage `Vec`'s allocation, not a fresh copy) as well as
12671        // value-equal (idempotent, no side effects on `&self`).
12672        //
12673        // Pins against a future silent detour that returned an owned
12674        // `Vec<Dep>` (which would type-check but silently clone on
12675        // every call), a `&Vec<Dep>` return (which would leak the
12676        // backing `Vec`'s grow/push/reserve surface no downstream
12677        // consumer reaches for), or a one-arm-only accessor that
12678        // returned a saturating value on some sentinel input.
12679        for deps in [
12680            vec![],
12681            vec![Dep::simple("caixa-teia", "^0.1")],
12682            vec![
12683                Dep::simple("caixa-teia", "^0.1"),
12684                Dep::simple("caixa-core", "^0.1"),
12685            ],
12686        ] {
12687            let c = caixa_with_deps(deps.clone());
12688            let first = c.deps();
12689            let second = c.deps();
12690            assert_eq!(
12691                first, second,
12692                "Caixa::deps must be idempotent — two successive calls \
12693                 on the same &self must return the same &[Dep]",
12694            );
12695            assert_eq!(
12696                first.as_ptr(),
12697                second.as_ptr(),
12698                "Caixa::deps must borrow the underlying Vec<Dep> \
12699                 storage — two successive calls must return slices \
12700                 with the same backing pointer (a fresh Vec<Dep> clone \
12701                 would change the pointer on every call)",
12702            );
12703            assert_eq!(
12704                first,
12705                deps.as_slice(),
12706                "Caixa::deps must return :deps verbatim by borrow — \
12707                 got {first:?}, expected {deps:?}",
12708            );
12709        }
12710    }
12711
12712    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
12713
12714    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
12715        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12716        c.deps_dev = deps_dev;
12717        c
12718    }
12719
12720    #[test]
12721    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
12722        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
12723        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
12724        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
12725        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
12726        // access across every representative value in the accept-set —
12727        // `[]` (the "no dev deps declared" arm every existing fixture
12728        // without a `:deps-dev` line carries; the [`Caixa::template`]
12729        // scaffold emits `:deps-dev ()`), a canonical single-entry list
12730        // (the shape most consumer caixas carry — a `tatara-check` dev
12731        // pin), a canonical two-entry list (the multi-dev-dep closure),
12732        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
12733        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
12734        // `NomeInvalid` but the accessor must ship the raw slot
12735        // verbatim) and a `[a, a]` duplicate (validate rejects through
12736        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
12737        // ship the raw slot verbatim so struct-literal fixtures continue
12738        // to expose the duplicate at the accessor).
12739        //
12740        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
12741        // pin on the substrate primitive — closes the outer-`Caixa`
12742        // dependency-slot `&[Dep]` sub-family the sibling
12743        // `deps_returns_deps_slice_verbatim_across_permutations`
12744        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
12745        // slice" projection pattern onto the sibling dev-dep axis —
12746        // pins against a future silent detour that returned an owned
12747        // `Vec<Dep>` (which would type-check but silently clone on every
12748        // accessor call, breaking the zero-cost projection every peer
12749        // sibling slice accessor carries), a `[""] → []` collapse (which
12750        // would silently absorb the `NomeEmpty` refusal case at the
12751        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
12752        // would silently absorb the `DuplicateNome` refusal case at the
12753        // accessor boundary).
12754        for deps_dev in [
12755            vec![],
12756            vec![Dep::simple("", "^0.1")],
12757            vec![Dep::simple("tatara-check", "^0.1")],
12758            vec![
12759                Dep::simple("tatara-check", "^0.1"),
12760                Dep::simple("caixa-lint", "^0.1"),
12761            ],
12762            vec![
12763                Dep::simple("tatara-check", "^0.1"),
12764                Dep::simple("tatara-check", "^0.2"),
12765            ],
12766        ] {
12767            let c = caixa_with_deps_dev(deps_dev.clone());
12768            assert_eq!(
12769                c.deps_dev(),
12770                deps_dev.as_slice(),
12771                "Caixa::deps_dev must return :deps-dev verbatim (got \
12772                 {:?}, expected {deps_dev:?})",
12773                c.deps_dev(),
12774            );
12775            assert_eq!(
12776                c.deps_dev(),
12777                c.deps_dev.as_slice(),
12778                "Caixa::deps_dev must element-equal the raw \
12779                 `self.deps_dev.as_slice()` field access across every \
12780                 value in the Vec<Dep> accept-set",
12781            );
12782        }
12783    }
12784
12785    #[test]
12786    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
12787        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
12788        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
12789        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
12790        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
12791        // Dep::simple("d", "^0.2")], .. }` must surface the
12792        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
12793        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
12794        // canonical single-entry form) must pass validate. The pair
12795        // jointly pins the accessor + validate-gate composition: any
12796        // future silent detour that had the accessor return a dedupped
12797        // slice on the `[a, a]` arm (a
12798        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
12799        // would silently absorb the `DuplicateNome` refusal at the
12800        // accessor boundary and the validate gate would accept a
12801        // struct-literal `Caixa` carrying the drift — the composition
12802        // pin catches that at caixa-core build time.
12803        //
12804        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
12805        // (ad34b4e) on the sibling `:deps` axis — same "the validate
12806        // gate must route through the substrate-primitive typed
12807        // dispatch" discipline folded onto the sibling `:deps-dev`
12808        // axis, closing the two-list dep-graph composition-pin family.
12809        // The `:deps-dev` diagnostic must carry the
12810        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
12811        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
12812        // offending list unambiguously.
12813        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
12814        let err = c.validate_deps().unwrap_err();
12815        assert!(
12816            matches!(
12817                err,
12818                DepError::DuplicateNome { ref nome, list } if nome == "d"
12819                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
12820            ),
12821            "validate_deps must reject deps_dev == \
12822             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
12823             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
12824             accessor and the validate gate must route through the \
12825             same substrate-primitive typed dispatch on the :deps-dev \
12826             within-list duplicate arm (got {err:?})",
12827        );
12828        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
12829        assert!(
12830            c.validate_deps().is_ok(),
12831            "validate_deps must accept deps_dev == \
12832             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
12833        );
12834    }
12835
12836    #[test]
12837    fn deps_dev_projects_slice_by_borrow() {
12838        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
12839        // borrow — the returned slice borrows the underlying `Vec<Dep>`
12840        // storage of the `:deps-dev` slot and the accessor must not
12841        // clone the backing `Vec` on every call. Peer of
12842        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
12843        // `:deps` axis, and of the per-`Caixa`
12844        // `autores_projects_slice_by_borrow` (b5d813f),
12845        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
12846        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
12847        // `exe_projects_slice_by_borrow` (65d9527), and
12848        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
12849        // on the sibling outer top-level [`Caixa`] `&[String]`-return
12850        // axes — the accessor's returned slice must borrow from `&self`
12851        // (the returned reference's lifetime is tied to `&self`), and
12852        // calling the accessor twice on the same [`Caixa`] must yield
12853        // slices that are pointer-equal (the underlying byte-buffer is
12854        // the storage `Vec`'s allocation, not a fresh copy) as well as
12855        // value-equal (idempotent, no side effects on `&self`).
12856        //
12857        // Pins against a future silent detour that returned an owned
12858        // `Vec<Dep>` (which would type-check but silently clone on
12859        // every call), a `&Vec<Dep>` return (which would leak the
12860        // backing `Vec`'s grow/push/reserve surface no downstream
12861        // consumer reaches for), or a one-arm-only accessor that
12862        // returned a saturating value on some sentinel input.
12863        for deps_dev in [
12864            vec![],
12865            vec![Dep::simple("tatara-check", "^0.1")],
12866            vec![
12867                Dep::simple("tatara-check", "^0.1"),
12868                Dep::simple("caixa-lint", "^0.1"),
12869            ],
12870        ] {
12871            let c = caixa_with_deps_dev(deps_dev.clone());
12872            let first = c.deps_dev();
12873            let second = c.deps_dev();
12874            assert_eq!(
12875                first, second,
12876                "Caixa::deps_dev must be idempotent — two successive \
12877                 calls on the same &self must return the same &[Dep]",
12878            );
12879            assert_eq!(
12880                first.as_ptr(),
12881                second.as_ptr(),
12882                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
12883                 storage — two successive calls must return slices \
12884                 with the same backing pointer (a fresh Vec<Dep> clone \
12885                 would change the pointer on every call)",
12886            );
12887            assert_eq!(
12888                first,
12889                deps_dev.as_slice(),
12890                "Caixa::deps_dev must return :deps-dev verbatim by \
12891                 borrow — got {first:?}, expected {deps_dev:?}",
12892            );
12893        }
12894    }
12895
12896    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
12897
12898    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
12899        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12900        c.limits = limits;
12901        c
12902    }
12903
12904    #[test]
12905    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
12906        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
12907        // composite optional-composite-reference-shape pin:
12908        // [`Caixa::limits`] must return the `:limits` typed
12909        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
12910        // reference over the same backing storage the raw
12911        // `self.limits.as_ref()` field access borrows from, byte-equal
12912        // across every representative fixture in the accept-set — the
12913        // author-omitted `None` shape (the "engine-default applies"
12914        // partition every downstream Servico M2 overlay emitter treats
12915        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
12916        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
12917        // per-axis cap is `None`, so the peer M2 overlay emitter's
12918        // `.is_empty()`-gated projection still emits nothing but the
12919        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
12920        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
12921        // fixture (only `:memory` set — the canonical shape most
12922        // memory-heavy Servicos carry), and a fully-populated composite
12923        // (every per-axis cap set — the canonical shape a
12924        // sandboxed-by-default Servico carries).
12925        //
12926        // Pins against a future silent detour that returned a fresh-
12927        // cloned [`LimitsSpec`] copy (which would type-check via the
12928        // `Clone` impl but silently break every downstream caller that
12929        // relied on the reference sharing the composite's backing
12930        // identity), a reference to an operator-resolved overlay (the
12931        // future per-cluster `:limits-overrides` slot — its resolution
12932        // must land at exactly this accessor body, not silently divert
12933        // the raw slot away from a second consumer), a
12934        // `None` → `Some(LimitsSpec::default)` cluster-default
12935        // projection (which would collapse the load-bearing
12936        // "author-omitted `:limits` ⇒ engine-default applies" partition
12937        // the peer [`crate::render::servico_m2_overlay`] emitter and
12938        // the peer [`Caixa::declared_servico_slots`] enumerator both
12939        // read), or an axis-shuffled projection (a future detour that
12940        // swapped `memory` and `fuel` through the accessor would
12941        // silently split the paired [`crate::StandardLayout::verify`]
12942        // per-`:limits` shape gate's traversal input from the peer
12943        // `servico_m2_overlay` emitter's projection input).
12944        //
12945        // First outer top-level [`Caixa`] `Option<&Composite>`-return
12946        // composite-reference accessor pin on the substrate primitive
12947        // — opens the outer-`Caixa` `Option<&Composite>` composite-
12948        // reference projection pattern the sibling `:behavior`
12949        // [`crate::BehaviorSpec`] / `:politicas`
12950        // [`crate::aplicacao::MeshPolicy`] / `:placement`
12951        // [`crate::aplicacao::Placement`] / `:entrada`
12952        // [`crate::aplicacao::Entrada`] future outer-composite lifts
12953        // fold on. Peer of the closed M3 outer-composite family the
12954        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
12955        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
12956        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
12957        // reference accessor pins already carry on the outer
12958        // [`crate::AplicacaoSpec`] altitude — extends the outer-
12959        // accessor byte-equal-projection discipline onto the outer
12960        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
12961        use crate::LimitsSpec;
12962        use std::time::Duration;
12963        let fixtures: Vec<Option<LimitsSpec>> = vec![
12964            None,
12965            Some(LimitsSpec::default()),
12966            Some(LimitsSpec {
12967                memory: Some(64 * 1024 * 1024),
12968                ..Default::default()
12969            }),
12970            Some(LimitsSpec {
12971                memory: Some(64 * 1024 * 1024),
12972                fuel: Some(1_000_000),
12973                wall_clock: Some(Duration::from_secs(30)),
12974                cpu: Some(500),
12975            }),
12976        ];
12977        for limits in fixtures {
12978            let c = caixa_with_limits(limits.clone());
12979            assert_eq!(
12980                c.limits(),
12981                limits.as_ref(),
12982                "Caixa::limits must return :limits verbatim (got {:?}, \
12983                 expected {:?})",
12984                c.limits(),
12985                limits.as_ref(),
12986            );
12987            match (c.limits(), c.limits.as_ref()) {
12988                (Some(a), Some(b)) => assert!(
12989                    std::ptr::eq(a, b),
12990                    "Caixa::limits accessor and self.limits.as_ref() \
12991                     field access must borrow the same backing storage \
12992                     — the accessor is the substrate-primitive typed \
12993                     dispatch every downstream Servico-M2-overlay \
12994                     composite consumer must route through, and a \
12995                     reference-identity split would silently break \
12996                     every consumer that relied on the borrow sharing \
12997                     the composite's storage",
12998                ),
12999                (None, None) => {}
13000                _ => panic!(
13001                    "Caixa::limits presence bit must byte-equal \
13002                     self.limits.is_some() — a presence-bit drift would \
13003                     silently split the paired StandardLayout::verify \
13004                     per-`:limits` shape gate's traversal head from \
13005                     the peer render::servico_m2_overlay M2 overlay \
13006                     emitter's traversal head from the peer \
13007                     Caixa::declared_servico_slots M2 declared-slot \
13008                     enumerator's presence probe",
13009                ),
13010            }
13011            assert_eq!(
13012                c.limits().is_some(),
13013                c.limits.is_some(),
13014                "Caixa::limits().is_some() must byte-equal \
13015                 self.limits.is_some() — a presence-bit drift would \
13016                 silently split every downstream Option<&LimitsSpec> \
13017                 consumer's partition on the engine-default arm",
13018            );
13019        }
13020    }
13021
13022    #[test]
13023    fn declared_servico_slots_limits_arm_routes_through_accessor() {
13024        // Composition pin: [`Caixa::declared_servico_slots`]'s
13025        // `:limits` presence-probe arm must key off [`Caixa::limits`],
13026        // not the raw `self.limits.is_some()` field-probe. Structurally:
13027        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
13028        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
13029        // (the presence bit is `Some`, so the M2 kind-coherence gate
13030        // must surface the slot as "declared" even when every per-axis
13031        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
13032        // push the label (the "author omitted the slot entirely"
13033        // partition). The pair jointly pins the accessor + declared-
13034        // slot enumerator composition: any future silent detour that
13035        // had the accessor collapse `Some(LimitsSpec::default())` to
13036        // `None` (a `.filter(|l| !l.is_empty())` projection) would
13037        // silently absorb the "declared but empty" arm at the
13038        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
13039        // kind-coherence gate would silently accept a
13040        // struct-literal `Caixa` carrying the drift.
13041        //
13042        // Peer of the sibling per-`Caixa`
13043        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
13044        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
13045        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
13046        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
13047        // enumerator gate must route through the substrate-primitive
13048        // typed dispatch" discipline extended onto the outer top-level
13049        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
13050        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
13051        // composition-pin family.
13052        use crate::LimitsSpec;
13053        let c = caixa_with_limits(Some(LimitsSpec::default()));
13054        let slots = c.declared_servico_slots();
13055        assert!(
13056            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
13057            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
13058             when `:limits` is Some (even for LimitsSpec::default()) \
13059             — the accessor and the enumerator gate must route through \
13060             the same substrate-primitive typed dispatch on the outer \
13061             :limits presence bit (got slots={slots:?})",
13062        );
13063        let c = caixa_with_limits(None);
13064        let slots = c.declared_servico_slots();
13065        assert!(
13066            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
13067            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
13068             when `:limits` is None — the author-omitted arm must \
13069             route through the accessor's None-return unchanged (got \
13070             slots={slots:?})",
13071        );
13072    }
13073
13074    #[test]
13075    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
13076        // Composition pin: [`crate::render::servico_m2_overlay`]'s
13077        // per-`:limits` M2 overlay emit arm must key off
13078        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
13079        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
13080        // Some(64 MiB), .. default }), .. }` must surface the
13081        // `M2_KEY_LIMITS` key with the per-axis
13082        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
13083        // limits: Some(LimitsSpec::default()), .. }` must omit the
13084        // key entirely (the `.is_empty()`-gated inner arm elides an
13085        // empty composite even when the outer presence bit is `Some`),
13086        // and a `Caixa { limits: None, .. }` must also omit the key
13087        // (the "author omitted the slot entirely" partition). The
13088        // three-fixture family jointly pins the accessor + M2 overlay
13089        // emitter composition: any future silent detour that had the
13090        // accessor return a fresh-cloned copy on the `Some` arm (a
13091        // `LimitsSpec::clone()` projection) would silently break the
13092        // reference-identity pin the peer per-axis
13093        // `serde_yaml::to_value(limits)` projection reads from.
13094        use crate::LimitsSpec;
13095        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
13096        let c = caixa_with_limits(Some(LimitsSpec {
13097            memory: Some(64 * 1024 * 1024),
13098            ..Default::default()
13099        }));
13100        let overlay = servico_m2_overlay(&c).unwrap();
13101        assert!(
13102            overlay.contains_key(M2_KEY_LIMITS),
13103            "servico_m2_overlay must surface M2_KEY_LIMITS when \
13104             `:limits` carries a non-empty composite — the accessor \
13105             and the M2 overlay emitter must route through the same \
13106             substrate-primitive typed dispatch on the outer :limits \
13107             composite (got overlay={overlay:?})",
13108        );
13109        let c = caixa_with_limits(Some(LimitsSpec::default()));
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 Some(LimitsSpec::default()) — the empty \
13115             composite's `.is_empty()`-gated inner arm must elide \
13116             the key regardless of the outer presence bit (got \
13117             overlay={overlay:?})",
13118        );
13119        let c = caixa_with_limits(None);
13120        let overlay = servico_m2_overlay(&c).unwrap();
13121        assert!(
13122            !overlay.contains_key(M2_KEY_LIMITS),
13123            "servico_m2_overlay must omit M2_KEY_LIMITS when \
13124             `:limits` is None — the author-omitted arm must route \
13125             through the accessor's None-return unchanged (got \
13126             overlay={overlay:?})",
13127        );
13128    }
13129
13130    #[test]
13131    fn limits_projects_option_ref_by_borrow() {
13132        // The by-borrow pin: [`Caixa::limits`] returns
13133        // `Option<&LimitsSpec>` by borrow — the returned reference
13134        // borrows the underlying `Option<LimitsSpec>` storage of the
13135        // `:limits` slot and the accessor must not clone the backing
13136        // composite on every call. Peer of the sibling
13137        // `deps_projects_slice_by_borrow` (ad34b4e) /
13138        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
13139        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
13140        // extended here to the outer [`Caixa`] `Option<&Composite>`-
13141        // return axis: the accessor's returned reference must borrow
13142        // from `&self` (the returned reference's lifetime is tied to
13143        // `&self`), and calling the accessor twice on the same
13144        // [`Caixa`] must yield references that are pointer-equal (the
13145        // underlying byte-buffer is the storage `LimitsSpec`'s
13146        // allocation, not a fresh copy) as well as value-equal
13147        // (idempotent, no side effects on `&self`).
13148        //
13149        // Pins against a future silent detour that returned an owned
13150        // `LimitsSpec` (which would type-check via the `Clone` impl
13151        // but silently clone on every call), a `&LimitsSpec` panic-
13152        // return on the `None` arm (which would collapse the load-
13153        // bearing `Option` presence-bit into a runtime panic), or a
13154        // one-arm-only accessor that returned a saturating composite
13155        // on some sentinel input.
13156        use crate::LimitsSpec;
13157        use std::time::Duration;
13158        for limits in [
13159            Some(LimitsSpec::default()),
13160            Some(LimitsSpec {
13161                memory: Some(64 * 1024 * 1024),
13162                fuel: Some(1_000_000),
13163                wall_clock: Some(Duration::from_secs(30)),
13164                cpu: Some(500),
13165            }),
13166        ] {
13167            let c = caixa_with_limits(limits.clone());
13168            let first = c.limits().unwrap();
13169            let second = c.limits().unwrap();
13170            assert_eq!(
13171                first, second,
13172                "Caixa::limits must be idempotent — two successive \
13173                 calls on the same &self must return the same \
13174                 &LimitsSpec",
13175            );
13176            assert!(
13177                std::ptr::eq(first, second),
13178                "Caixa::limits must borrow the underlying \
13179                 Option<LimitsSpec> storage — two successive calls \
13180                 must return references with the same backing pointer \
13181                 (a fresh LimitsSpec clone would change the pointer \
13182                 on every call)",
13183            );
13184            assert_eq!(
13185                Some(first),
13186                limits.as_ref(),
13187                "Caixa::limits must return :limits verbatim by borrow \
13188                 — got {first:?}, expected {:?}",
13189                limits.as_ref(),
13190            );
13191        }
13192        let c = caixa_with_limits(None);
13193        assert!(
13194            c.limits().is_none(),
13195            "Caixa::limits must return None when :limits is absent — \
13196             the author-omitted arm must project through the \
13197             accessor's Option::None unchanged",
13198        );
13199    }
13200
13201    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
13202
13203    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
13204        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13205        c.behavior = behavior;
13206        c
13207    }
13208
13209    #[test]
13210    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
13211        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
13212        // composite optional-composite-reference-shape pin:
13213        // [`Caixa::behavior`] must return the `:behavior` typed
13214        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
13215        // reference over the same backing storage the raw
13216        // `self.behavior.as_ref()` field access borrows from, byte-equal
13217        // across every representative fixture in the accept-set — the
13218        // author-omitted `None` shape (the "runtime-default applies"
13219        // partition every downstream Servico M2 overlay emitter treats
13220        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
13221        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
13222        // every per-callback path is `None`, so the peer M2 overlay
13223        // emitter's `.is_empty()`-gated projection still emits nothing
13224        // but the outer presence-bit is `Some`, so
13225        // [`Caixa::declared_servico_slots`] still pushes the
13226        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
13227        // (only `:on-state-change` set — the canonical shape a caixa
13228        // that only wires the hot-upgrade migration path carries), and
13229        // a fully-populated composite (every per-callback path set —
13230        // the canonical shape a fully-instrumented gen_server-shaped
13231        // Servico carries).
13232        //
13233        // Peer of the sibling
13234        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13235        // (b2bd9d7) opening fixture-family + reference-identity +
13236        // presence-bit tetrad pin on the outer top-level [`Caixa`]
13237        // `Option<&Composite>`-return sub-family — extended here to the
13238        // second axis of that sub-family so both of the currently-lifted
13239        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
13240        // `:behavior`) carry the same "byte-equal, borrow-shared,
13241        // presence-bit-preserved" outer-accessor discipline.
13242        //
13243        // Pins against a future silent detour that returned a fresh-
13244        // cloned [`crate::BehaviorSpec`] copy (which would type-check
13245        // via the `Clone` impl but silently break every downstream
13246        // caller that relied on the reference sharing the composite's
13247        // backing identity), a reference to an operator-resolved
13248        // overlay (a future per-cluster `:behavior-overrides` slot —
13249        // its resolution must land at exactly this accessor body, not
13250        // silently divert the raw slot away from a second consumer), a
13251        // `None` → `Some(BehaviorSpec::default)` cluster-default
13252        // projection (which would collapse the load-bearing
13253        // "author-omitted `:behavior` ⇒ runtime-default applies"
13254        // partition the peer [`crate::render::servico_m2_overlay`]
13255        // emitter, the peer [`Caixa::declared_servico_slots`]
13256        // enumerator, and the cross-slot
13257        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
13258        // gate all read), or a callback-shuffled projection (a future
13259        // detour that swapped `on_init` and `on_terminate` through the
13260        // accessor would silently split the paired
13261        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
13262        // traversal input from the peer `servico_m2_overlay` emitter's
13263        // projection input from the cross-slot `:state-change`
13264        // composition gate's traversal input).
13265        use crate::BehaviorSpec;
13266        use std::path::PathBuf;
13267        let fixtures: Vec<Option<BehaviorSpec>> = vec![
13268            None,
13269            Some(BehaviorSpec::default()),
13270            Some(BehaviorSpec {
13271                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13272                ..Default::default()
13273            }),
13274            Some(BehaviorSpec {
13275                on_init: Some(PathBuf::from("lib/init.lisp")),
13276                on_call: Some(PathBuf::from("lib/handlers.lisp")),
13277                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
13278                on_info: Some(PathBuf::from("lib/handlers.lisp")),
13279                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13280                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
13281            }),
13282        ];
13283        for behavior in fixtures {
13284            let c = caixa_with_behavior(behavior.clone());
13285            assert_eq!(
13286                c.behavior(),
13287                behavior.as_ref(),
13288                "Caixa::behavior must return :behavior verbatim (got \
13289                 {:?}, expected {:?})",
13290                c.behavior(),
13291                behavior.as_ref(),
13292            );
13293            match (c.behavior(), c.behavior.as_ref()) {
13294                (Some(a), Some(b)) => assert!(
13295                    std::ptr::eq(a, b),
13296                    "Caixa::behavior accessor and self.behavior.as_ref() \
13297                     field access must borrow the same backing storage \
13298                     — the accessor is the substrate-primitive typed \
13299                     dispatch every downstream Servico-M2-overlay \
13300                     composite consumer must route through, and a \
13301                     reference-identity split would silently break \
13302                     every consumer that relied on the borrow sharing \
13303                     the composite's storage",
13304                ),
13305                (None, None) => {}
13306                _ => panic!(
13307                    "Caixa::behavior presence bit must byte-equal \
13308                     self.behavior.is_some() — a presence-bit drift \
13309                     would silently split the paired \
13310                     StandardLayout::verify per-`:behavior` shape \
13311                     gate's traversal head from the peer \
13312                     render::servico_m2_overlay M2 overlay emitter's \
13313                     traversal head from the cross-slot \
13314                     validate_upgrade_from_against_behavior \
13315                     composition gate's traversal head from the peer \
13316                     Caixa::declared_servico_slots M2 declared-slot \
13317                     enumerator's presence probe",
13318                ),
13319            }
13320            assert_eq!(
13321                c.behavior().is_some(),
13322                c.behavior.is_some(),
13323                "Caixa::behavior().is_some() must byte-equal \
13324                 self.behavior.is_some() — a presence-bit drift would \
13325                 silently split every downstream Option<&BehaviorSpec> \
13326                 consumer's partition on the runtime-default arm",
13327            );
13328        }
13329    }
13330
13331    #[test]
13332    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
13333        // Composition pin: [`Caixa::declared_servico_slots`]'s
13334        // `:behavior` presence-probe arm must key off
13335        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
13336        // field-probe. Structurally: a `Caixa { behavior:
13337        // Some(BehaviorSpec::default()), .. }` must still push
13338        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
13339        // presence bit is `Some`, so the M2 kind-coherence gate must
13340        // surface the slot as "declared" even when every per-callback
13341        // path is unset), and a `Caixa { behavior: None, .. }` must
13342        // NOT push the label (the "author omitted the slot entirely"
13343        // partition). The pair jointly pins the accessor + declared-
13344        // slot enumerator composition: any future silent detour that
13345        // had the accessor collapse `Some(BehaviorSpec::default())`
13346        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
13347        // silently absorb the "declared but empty" arm at the
13348        // accessor boundary and the
13349        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
13350        // kind-coherence gate would silently accept a struct-literal
13351        // `Caixa` carrying the drift.
13352        //
13353        // Peer of the sibling
13354        // `declared_servico_slots_limits_arm_routes_through_accessor`
13355        // (b2bd9d7) composition pin on the sibling `:limits` outer-
13356        // `Option<&LimitsSpec>` arm of the same
13357        // [`Caixa::declared_servico_slots`] M2 declared-slot
13358        // enumerator's traversal — same "the enumerator gate must
13359        // route through the substrate-primitive typed dispatch"
13360        // discipline extended onto the outer top-level [`Caixa`]
13361        // `Option<&BehaviorSpec>`-composition surface.
13362        use crate::BehaviorSpec;
13363        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
13364        let slots = c.declared_servico_slots();
13365        assert!(
13366            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
13367            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
13368             when `:behavior` is Some (even for BehaviorSpec::default()) \
13369             — the accessor and the enumerator gate must route through \
13370             the same substrate-primitive typed dispatch on the outer \
13371             :behavior presence bit (got slots={slots:?})",
13372        );
13373        let c = caixa_with_behavior(None);
13374        let slots = c.declared_servico_slots();
13375        assert!(
13376            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
13377            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
13378             when `:behavior` is None — the author-omitted arm must \
13379             route through the accessor's None-return unchanged (got \
13380             slots={slots:?})",
13381        );
13382    }
13383
13384    #[test]
13385    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
13386        // Composition pin: [`crate::render::servico_m2_overlay`]'s
13387        // per-`:behavior` M2 overlay emit arm must key off
13388        // [`Caixa::behavior`], not the raw `&caixa.behavior`
13389        // field-borrow. Structurally: a `Caixa { behavior:
13390        // Some(BehaviorSpec { on_state_change: Some(...), .. default
13391        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
13392        // per-callback `onStateChange` sub-mapping in the overlay, a
13393        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
13394        // must omit the key entirely (the `.is_empty()`-gated inner
13395        // arm elides an empty composite even when the outer presence
13396        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
13397        // also omit the key (the "author omitted the slot entirely"
13398        // partition). The three-fixture family jointly pins the
13399        // accessor + M2 overlay emitter composition: any future
13400        // silent detour that had the accessor return a fresh-cloned
13401        // copy on the `Some` arm (a `BehaviorSpec::clone()`
13402        // projection) would silently break the reference-identity
13403        // pin the peer per-callback `serde_yaml::to_value(behavior)`
13404        // projection reads from.
13405        //
13406        // Peer of the sibling
13407        // `servico_m2_overlay_limits_arm_routes_through_accessor`
13408        // (b2bd9d7) composition pin on the sibling `:limits` outer-
13409        // `Option<&LimitsSpec>` arm of the same
13410        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
13411        // traversal — same "the emitter must route through the
13412        // substrate-primitive typed dispatch on the outer composite"
13413        // discipline extended onto the outer top-level [`Caixa`]
13414        // `Option<&BehaviorSpec>`-composition surface.
13415        use crate::BehaviorSpec;
13416        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
13417        use std::path::PathBuf;
13418        let c = caixa_with_behavior(Some(BehaviorSpec {
13419            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13420            ..Default::default()
13421        }));
13422        let overlay = servico_m2_overlay(&c).unwrap();
13423        assert!(
13424            overlay.contains_key(M2_KEY_BEHAVIOR),
13425            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
13426             `:behavior` carries a non-empty composite — the accessor \
13427             and the M2 overlay emitter must route through the same \
13428             substrate-primitive typed dispatch on the outer :behavior \
13429             composite (got overlay={overlay:?})",
13430        );
13431        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
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 Some(BehaviorSpec::default()) — the empty \
13437             composite's `.is_empty()`-gated inner arm must elide the \
13438             key regardless of the outer presence bit (got \
13439             overlay={overlay:?})",
13440        );
13441        let c = caixa_with_behavior(None);
13442        let overlay = servico_m2_overlay(&c).unwrap();
13443        assert!(
13444            !overlay.contains_key(M2_KEY_BEHAVIOR),
13445            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
13446             `:behavior` is None — the author-omitted arm must route \
13447             through the accessor's None-return unchanged (got \
13448             overlay={overlay:?})",
13449        );
13450    }
13451
13452    #[test]
13453    fn behavior_projects_option_ref_by_borrow() {
13454        // The by-borrow pin: [`Caixa::behavior`] returns
13455        // `Option<&BehaviorSpec>` by borrow — the returned reference
13456        // borrows the underlying `Option<BehaviorSpec>` storage of the
13457        // `:behavior` slot and the accessor must not clone the backing
13458        // composite on every call. Peer of the sibling
13459        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
13460        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
13461        // return sub-family — extended here to the second axis of the
13462        // same sub-family: the accessor's returned reference must
13463        // borrow from `&self` (the returned reference's lifetime is
13464        // tied to `&self`), and calling the accessor twice on the same
13465        // [`Caixa`] must yield references that are pointer-equal (the
13466        // underlying byte-buffer is the storage `BehaviorSpec`'s
13467        // allocation, not a fresh copy) as well as value-equal
13468        // (idempotent, no side effects on `&self`).
13469        //
13470        // Pins against a future silent detour that returned an owned
13471        // `BehaviorSpec` (which would type-check via the `Clone` impl
13472        // but silently clone on every call), a `&BehaviorSpec` panic-
13473        // return on the `None` arm (which would collapse the load-
13474        // bearing `Option` presence-bit into a runtime panic), or a
13475        // one-arm-only accessor that returned a saturating composite
13476        // on some sentinel input.
13477        use crate::BehaviorSpec;
13478        use std::path::PathBuf;
13479        for behavior in [
13480            Some(BehaviorSpec::default()),
13481            Some(BehaviorSpec {
13482                on_init: Some(PathBuf::from("lib/init.lisp")),
13483                on_call: Some(PathBuf::from("lib/handlers.lisp")),
13484                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
13485                on_info: Some(PathBuf::from("lib/handlers.lisp")),
13486                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13487                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
13488            }),
13489        ] {
13490            let c = caixa_with_behavior(behavior.clone());
13491            let first = c.behavior().unwrap();
13492            let second = c.behavior().unwrap();
13493            assert_eq!(
13494                first, second,
13495                "Caixa::behavior must be idempotent — two successive \
13496                 calls on the same &self must return the same \
13497                 &BehaviorSpec",
13498            );
13499            assert!(
13500                std::ptr::eq(first, second),
13501                "Caixa::behavior must borrow the underlying \
13502                 Option<BehaviorSpec> storage — two successive calls \
13503                 must return references with the same backing pointer \
13504                 (a fresh BehaviorSpec clone would change the pointer \
13505                 on every call)",
13506            );
13507            assert_eq!(
13508                Some(first),
13509                behavior.as_ref(),
13510                "Caixa::behavior must return :behavior verbatim by \
13511                 borrow — got {first:?}, expected {:?}",
13512                behavior.as_ref(),
13513            );
13514        }
13515        let c = caixa_with_behavior(None);
13516        assert!(
13517            c.behavior().is_none(),
13518            "Caixa::behavior must return None when :behavior is absent \
13519             — the author-omitted arm must project through the \
13520             accessor's Option::None unchanged",
13521        );
13522    }
13523
13524    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
13525
13526    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
13527        use crate::aplicacao::{Membro, WitContract};
13528        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13529        c.kind = CaixaKind::Aplicacao;
13530        c.membros = vec![Membro {
13531            caixa: "a".into(),
13532            versao: "^0.1".into(),
13533        }];
13534        c.contratos = vec![WitContract {
13535            de: "a".into(),
13536            para: "a".into(),
13537            wit: "wasi:http/proxy".into(),
13538            endpoint: Some("/x".into()),
13539            subject: None,
13540            slot: None,
13541        }];
13542        c.politicas = politicas;
13543        c
13544    }
13545
13546    #[test]
13547    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
13548        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
13549        // composite optional-composite-reference-shape pin:
13550        // [`Caixa::politicas`] must return the `:politicas` typed
13551        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
13552        // reference over the same backing storage the raw
13553        // `self.politicas.as_ref()` field access borrows from,
13554        // byte-equal across every representative fixture in the
13555        // accept-set — the author-omitted `None` shape (the "cluster-
13556        // default applies" partition every downstream mesh-artifact
13557        // emitter treats as "emit no `:politicas` overlay"), the
13558        // empty-composite `Some(MeshPolicy { .. default })` shape
13559        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
13560        // per-axis mesh-policy scalar is `None`, so the peer inner
13561        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
13562        // caixa-mesh overlay elides every per-axis emit but the outer
13563        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
13564        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
13565        // single-axis fixture (only `:timeout` set — the canonical
13566        // shape a latency-sensitive Aplicacao carries), and a
13567        // fully-populated composite (every per-axis mesh-policy
13568        // scalar set — the canonical shape a fully-governed
13569        // Aplicacao carries).
13570        //
13571        // Pins against a future silent detour that returned a fresh-
13572        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
13573        // type-check via the `Clone` impl but silently break every
13574        // downstream caller that relied on the reference sharing the
13575        // composite's backing identity), a reference to an operator-
13576        // resolved overlay (the future per-cluster
13577        // `:politicas-overrides` slot — its resolution must land at
13578        // exactly this accessor body, not silently divert the raw
13579        // slot away from the peer [`Caixa::declared_mesh_slots`]
13580        // enumerator's presence probe), a
13581        // `None` → `Some(MeshPolicy::default)` cluster-default
13582        // projection (which would collapse the load-bearing
13583        // "author-omitted `:politicas` ⇒ cluster-default applies"
13584        // partition the peer [`Caixa::declared_mesh_slots`]
13585        // enumerator and the peer [`Caixa::aplicacao_view`]
13586        // Aplicacao-composition seed both read), or an axis-shuffled
13587        // projection (a future detour that swapped `timeout` and
13588        // `retries` through the accessor would silently split the
13589        // paired [`Caixa::aplicacao_view`] seed's fold input from the
13590        // sibling M3 mesh-artifact emitter's projection input).
13591        //
13592        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
13593        // composite-reference accessor pin on the substrate primitive
13594        // — peer of the sibling
13595        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13596        // (b2bd9d7) and
13597        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
13598        // (35d8b52) opening tetrad pins on the outer top-level
13599        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
13600        // here to the first of the three M3 mesh-slot axes so the
13601        // opening third of the outer `Option<&Composite>` sub-family
13602        // carries the same "byte-equal, borrow-shared, presence-bit-
13603        // preserved" outer-accessor discipline.
13604        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
13605        use std::time::Duration;
13606        let fixtures: Vec<Option<MeshPolicy>> = vec![
13607            None,
13608            Some(MeshPolicy::default()),
13609            Some(MeshPolicy {
13610                timeout: Some(Duration::from_secs(30)),
13611                ..Default::default()
13612            }),
13613            Some(MeshPolicy {
13614                timeout: Some(Duration::from_secs(30)),
13615                retries: Some(3),
13616                circuit_breaker: Some(CircuitBreaker {
13617                    max_failures: 5,
13618                    window: Duration::from_secs(60),
13619                }),
13620                mtls_required: Some(true),
13621                rate_limit: Some(RateLimit {
13622                    rate: 100,
13623                    window: Duration::from_secs(1),
13624                }),
13625            }),
13626        ];
13627        for politicas in fixtures {
13628            let c = caixa_aplicacao_with_politicas(politicas.clone());
13629            assert_eq!(
13630                c.politicas(),
13631                politicas.as_ref(),
13632                "Caixa::politicas must return :politicas verbatim (got \
13633                 {:?}, expected {:?})",
13634                c.politicas(),
13635                politicas.as_ref(),
13636            );
13637            match (c.politicas(), c.politicas.as_ref()) {
13638                (Some(a), Some(b)) => assert!(
13639                    std::ptr::eq(a, b),
13640                    "Caixa::politicas accessor and self.politicas.as_ref() \
13641                     field access must borrow the same backing storage \
13642                     — the accessor is the substrate-primitive typed \
13643                     dispatch every downstream Aplicacao-mesh-overlay \
13644                     composite consumer must route through, and a \
13645                     reference-identity split would silently break \
13646                     every consumer that relied on the borrow sharing \
13647                     the composite's storage",
13648                ),
13649                (None, None) => {}
13650                _ => panic!(
13651                    "Caixa::politicas presence bit must byte-equal \
13652                     self.politicas.is_some() — a presence-bit drift \
13653                     would silently split the paired \
13654                     Caixa::aplicacao_view Aplicacao-composition seed's \
13655                     traversal head from the peer \
13656                     Caixa::declared_mesh_slots M3 declared-slot \
13657                     enumerator's presence probe",
13658                ),
13659            }
13660            assert_eq!(
13661                c.politicas().is_some(),
13662                c.politicas.is_some(),
13663                "Caixa::politicas().is_some() must byte-equal \
13664                 self.politicas.is_some() — a presence-bit drift would \
13665                 silently split every downstream Option<&MeshPolicy> \
13666                 consumer's partition on the cluster-default arm",
13667            );
13668        }
13669    }
13670
13671    #[test]
13672    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
13673        // Composition pin: [`Caixa::declared_mesh_slots`]'s
13674        // `:politicas` presence-probe arm must key off
13675        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
13676        // field-probe. Structurally: a `Caixa { politicas:
13677        // Some(MeshPolicy::default()), .. }` must still push
13678        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
13679        // presence bit is `Some`, so the M3 kind-coherence gate must
13680        // surface the slot as "declared" even when every per-axis
13681        // scalar is unset), and a `Caixa { politicas: None, .. }` must
13682        // NOT push the label (the "author omitted the slot entirely"
13683        // partition). The pair jointly pins the accessor + declared-
13684        // slot enumerator composition: any future silent detour that
13685        // had the accessor collapse `Some(MeshPolicy::default())` to
13686        // `None` (a `.filter(|p| !p.is_empty())` projection) would
13687        // silently absorb the "declared but empty" arm at the
13688        // accessor boundary and the
13689        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
13690        // coherence gate would silently accept a struct-literal
13691        // `Caixa` carrying the drift.
13692        //
13693        // Peer of the sibling
13694        // `declared_servico_slots_limits_arm_routes_through_accessor`
13695        // (b2bd9d7) and
13696        // `declared_servico_slots_behavior_arm_routes_through_accessor`
13697        // (35d8b52) composition pins on the sibling `:limits` /
13698        // `:behavior` outer-`Option<&Composite>` arms of the peer
13699        // [`Caixa::declared_servico_slots`] M2 declared-slot
13700        // enumerator's traversal — same "the enumerator gate must
13701        // route through the substrate-primitive typed dispatch"
13702        // discipline extended onto the outer top-level [`Caixa`] M3
13703        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
13704        // enumerator carries the same routing invariant as its M2
13705        // sibling.
13706        use crate::aplicacao::MeshPolicy;
13707        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
13708        let slots = c.declared_mesh_slots();
13709        assert!(
13710            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
13711            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
13712             when `:politicas` is Some (even for MeshPolicy::default()) \
13713             — the accessor and the enumerator gate must route through \
13714             the same substrate-primitive typed dispatch on the outer \
13715             :politicas presence bit (got slots={slots:?})",
13716        );
13717        let c = caixa_aplicacao_with_politicas(None);
13718        let slots = c.declared_mesh_slots();
13719        assert!(
13720            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
13721            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
13722             when `:politicas` is None — the author-omitted arm must \
13723             route through the accessor's None-return unchanged (got \
13724             slots={slots:?})",
13725        );
13726    }
13727
13728    #[test]
13729    fn aplicacao_view_politicas_arm_folds_through_accessor() {
13730        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
13731        // Aplicacao-composition seed must fold through
13732        // [`Caixa::politicas`], not the raw
13733        // `self.politicas.clone().unwrap_or_default()` field-borrow.
13734        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
13735        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
13736        // must surface a projected [`crate::AplicacaoSpec`] whose
13737        // `politicas().timeout()` field byte-equals the outer
13738        // composite's `timeout` scalar (the fold must project the
13739        // authored composite verbatim), a `Caixa { politicas:
13740        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
13741        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
13742        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
13743        // fold's empty-composite arm collapses to the same default the
13744        // author-omitted arm does), and a `Caixa { politicas: None,
13745        // kind: Aplicacao, .. }` must surface an
13746        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
13747        // [`crate::aplicacao::MeshPolicy::default`] (the "author
13748        // omitted the slot entirely" arm folds through the
13749        // `unwrap_or_default` onto the cluster-default). The triad
13750        // jointly pins the accessor + Aplicacao-composition seed
13751        // composition: any future silent detour that had the accessor
13752        // divert the raw slot away from the seed's fold (an operator-
13753        // resolved overlay's default-fold arm silently differing from
13754        // the raw slot's default-fold arm) would silently split the
13755        // build-time mesh-artifact emission gate from the caixa-mesh
13756        // renderer's Aplicacao-view input at the composition boundary.
13757        use crate::aplicacao::MeshPolicy;
13758        use std::time::Duration;
13759        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
13760            timeout: Some(Duration::from_secs(30)),
13761            ..Default::default()
13762        }));
13763        let view = c.aplicacao_view().unwrap();
13764        assert_eq!(
13765            view.politicas().timeout(),
13766            Some(Duration::from_secs(30)),
13767            "Caixa::aplicacao_view must fold the authored :politicas \
13768             :timeout scalar through the accessor verbatim onto the \
13769             projected AplicacaoSpec — a future silent detour at the \
13770             seed's fold arm would surface here as a projected-scalar \
13771             drift (got {:?})",
13772            view.politicas().timeout(),
13773        );
13774        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
13775        let view = c.aplicacao_view().unwrap();
13776        assert_eq!(
13777            view.politicas(),
13778            &MeshPolicy::default(),
13779            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
13780             through the accessor onto MeshPolicy::default — the empty- \
13781             composite arm collapses to the same default the author- \
13782             omitted arm does (got {:?})",
13783            view.politicas(),
13784        );
13785        let c = caixa_aplicacao_with_politicas(None);
13786        let view = c.aplicacao_view().unwrap();
13787        assert_eq!(
13788            view.politicas(),
13789            &MeshPolicy::default(),
13790            "Caixa::aplicacao_view must fold None through the accessor's \
13791             unwrap_or_default onto MeshPolicy::default — the author- \
13792             omitted arm must route through the accessor's None-return \
13793             unchanged (got {:?})",
13794            view.politicas(),
13795        );
13796    }
13797
13798    #[test]
13799    fn politicas_projects_option_ref_by_borrow() {
13800        // The by-borrow pin: [`Caixa::politicas`] returns
13801        // `Option<&MeshPolicy>` by borrow — the returned reference
13802        // borrows the underlying `Option<MeshPolicy>` storage of the
13803        // `:politicas` slot and the accessor must not clone the
13804        // backing composite on every call. Peer of the sibling
13805        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
13806        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
13807        // pins on the outer top-level [`Caixa`]
13808        // `Option<&Composite>`-return sub-family — extended here to
13809        // the third axis of the same sub-family: the accessor's
13810        // returned reference must borrow from `&self` (the returned
13811        // reference's lifetime is tied to `&self`), and calling the
13812        // accessor twice on the same [`Caixa`] must yield references
13813        // that are pointer-equal (the underlying byte-buffer is the
13814        // storage `MeshPolicy`'s allocation, not a fresh copy) as
13815        // well as value-equal (idempotent, no side effects on
13816        // `&self`).
13817        //
13818        // Pins against a future silent detour that returned an owned
13819        // `MeshPolicy` (which would type-check via the `Clone` impl
13820        // but silently clone on every call), a `&MeshPolicy` panic-
13821        // return on the `None` arm (which would collapse the load-
13822        // bearing `Option` presence-bit into a runtime panic), or a
13823        // one-arm-only accessor that returned a saturating composite
13824        // on some sentinel input.
13825        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
13826        use std::time::Duration;
13827        for politicas in [
13828            Some(MeshPolicy::default()),
13829            Some(MeshPolicy {
13830                timeout: Some(Duration::from_secs(30)),
13831                retries: Some(3),
13832                circuit_breaker: Some(CircuitBreaker {
13833                    max_failures: 5,
13834                    window: Duration::from_secs(60),
13835                }),
13836                mtls_required: Some(true),
13837                rate_limit: Some(RateLimit {
13838                    rate: 100,
13839                    window: Duration::from_secs(1),
13840                }),
13841            }),
13842        ] {
13843            let c = caixa_aplicacao_with_politicas(politicas.clone());
13844            let first = c.politicas().unwrap();
13845            let second = c.politicas().unwrap();
13846            assert_eq!(
13847                first, second,
13848                "Caixa::politicas must be idempotent — two successive \
13849                 calls on the same &self must return the same \
13850                 &MeshPolicy",
13851            );
13852            assert!(
13853                std::ptr::eq(first, second),
13854                "Caixa::politicas must borrow the underlying \
13855                 Option<MeshPolicy> storage — two successive calls \
13856                 must return references with the same backing pointer \
13857                 (a fresh MeshPolicy clone would change the pointer on \
13858                 every call)",
13859            );
13860            assert_eq!(
13861                Some(first),
13862                politicas.as_ref(),
13863                "Caixa::politicas must return :politicas verbatim by \
13864                 borrow — got {first:?}, expected {:?}",
13865                politicas.as_ref(),
13866            );
13867        }
13868        let c = caixa_aplicacao_with_politicas(None);
13869        assert!(
13870            c.politicas().is_none(),
13871            "Caixa::politicas must return None when :politicas is \
13872             absent — the author-omitted arm must project through the \
13873             accessor's Option::None unchanged",
13874        );
13875    }
13876
13877    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
13878
13879    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
13880        use crate::aplicacao::{Membro, WitContract};
13881        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13882        c.kind = CaixaKind::Aplicacao;
13883        c.membros = vec![Membro {
13884            caixa: "a".into(),
13885            versao: "^0.1".into(),
13886        }];
13887        c.contratos = vec![WitContract {
13888            de: "a".into(),
13889            para: "a".into(),
13890            wit: "wasi:http/proxy".into(),
13891            endpoint: Some("/x".into()),
13892            subject: None,
13893            slot: None,
13894        }];
13895        c.placement = placement;
13896        c
13897    }
13898
13899    #[test]
13900    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
13901        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
13902        // composite optional-composite-reference-shape pin:
13903        // [`Caixa::placement`] must return the `:placement` typed
13904        // `Option<Placement>` verbatim as an `Option<&Placement>`
13905        // reference over the same backing storage the raw
13906        // `self.placement.as_ref()` field access borrows from,
13907        // byte-equal across every representative fixture in the
13908        // accept-set — the author-omitted `None` shape (the
13909        // "cluster-default applies" partition every downstream mesh-
13910        // artifact emitter treats as "emit no `:placement` overlay"),
13911        // the empty-composite `Some(Placement { .. default })` shape
13912        // (`estrategia: SingleNode`, empty clusters, no shard-key /
13913        // affinity — the outer presence-bit is `Some` so
13914        // [`Caixa::declared_mesh_slots`] still pushes the
13915        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
13916        // `Replicated`-on-two-clusters fixture (the canonical shape a
13917        // stateless HTTP Aplicacao carries), and a fully-populated
13918        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
13919        // shape a stateful Akka-style cluster-sharding Aplicacao
13920        // carries).
13921        //
13922        // Pins against a future silent detour that returned a fresh-
13923        // cloned [`crate::aplicacao::Placement`] copy (which would
13924        // type-check via the `Clone` impl but silently break every
13925        // downstream caller that relied on the reference sharing the
13926        // composite's backing identity), a reference to an operator-
13927        // resolved overlay (the future per-cluster
13928        // `:placement-overrides` slot — its resolution must land at
13929        // exactly this accessor body, not silently divert the raw
13930        // slot away from the peer [`Caixa::declared_mesh_slots`]
13931        // enumerator's presence probe), a `None` →
13932        // `Some(Placement::default)` cluster-default projection (which
13933        // would collapse the load-bearing "author-omitted `:placement`
13934        // ⇒ cluster-default applies" partition the peer
13935        // [`Caixa::declared_mesh_slots`] enumerator and the peer
13936        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
13937        // read), or an axis-shuffled projection (a future detour that
13938        // swapped `clusters` and `affinity` through the accessor would
13939        // silently split the paired [`Caixa::aplicacao_view`] seed's
13940        // fold input from the sibling M3 mesh-artifact emitter's
13941        // projection input).
13942        //
13943        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
13944        // composite-reference accessor pin on the substrate primitive
13945        // — peer of the sibling
13946        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13947        // (b2bd9d7),
13948        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
13949        // (35d8b52), and
13950        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
13951        // (5d23d29) opening triad pins on the outer top-level
13952        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
13953        // here to the second of the three M3 mesh-slot axes so the
13954        // opening four-fifths of the outer `Option<&Composite>` sub-
13955        // family carries the same "byte-equal, borrow-shared,
13956        // presence-bit-preserved" outer-accessor discipline.
13957        use crate::aplicacao::{Placement, PlacementStrategy};
13958        let fixtures: Vec<Option<Placement>> = vec![
13959            None,
13960            Some(Placement::default()),
13961            Some(Placement {
13962                estrategia: PlacementStrategy::Replicated,
13963                clusters: vec!["rio".into(), "sao-paulo".into()],
13964                affinity: None,
13965                shard_key: None,
13966            }),
13967            Some(Placement {
13968                estrategia: PlacementStrategy::Sharded,
13969                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
13970                affinity: Some("data-locality".into()),
13971                shard_key: Some("$tenantId".into()),
13972            }),
13973        ];
13974        for placement in fixtures {
13975            let c = caixa_aplicacao_with_placement(placement.clone());
13976            assert_eq!(
13977                c.placement(),
13978                placement.as_ref(),
13979                "Caixa::placement must return :placement verbatim (got \
13980                 {:?}, expected {:?})",
13981                c.placement(),
13982                placement.as_ref(),
13983            );
13984            match (c.placement(), c.placement.as_ref()) {
13985                (Some(a), Some(b)) => assert!(
13986                    std::ptr::eq(a, b),
13987                    "Caixa::placement accessor and self.placement.as_ref() \
13988                     field access must borrow the same backing storage \
13989                     — the accessor is the substrate-primitive typed \
13990                     dispatch every downstream Aplicacao-distribution- \
13991                     overlay composite consumer must route through, and \
13992                     a reference-identity split would silently break \
13993                     every consumer that relied on the borrow sharing \
13994                     the composite's storage",
13995                ),
13996                (None, None) => {}
13997                _ => panic!(
13998                    "Caixa::placement presence bit must byte-equal \
13999                     self.placement.is_some() — a presence-bit drift \
14000                     would silently split the paired \
14001                     Caixa::aplicacao_view Aplicacao-composition seed's \
14002                     traversal head from the peer \
14003                     Caixa::declared_mesh_slots M3 declared-slot \
14004                     enumerator's presence probe",
14005                ),
14006            }
14007            assert_eq!(
14008                c.placement().is_some(),
14009                c.placement.is_some(),
14010                "Caixa::placement().is_some() must byte-equal \
14011                 self.placement.is_some() — a presence-bit drift would \
14012                 silently split every downstream Option<&Placement> \
14013                 consumer's partition on the cluster-default arm",
14014            );
14015        }
14016    }
14017
14018    #[test]
14019    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
14020        // Composition pin: [`Caixa::declared_mesh_slots`]'s
14021        // `:placement` presence-probe arm must key off
14022        // [`Caixa::placement`], not the raw `self.placement.is_some()`
14023        // field-probe. Structurally: a `Caixa { placement:
14024        // Some(Placement::default()), .. }` must still push
14025        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
14026        // presence bit is `Some`, so the M3 kind-coherence gate must
14027        // surface the slot as "declared" even when every per-axis
14028        // scalar defers to the cluster-default arm), and a `Caixa {
14029        // placement: None, .. }` must NOT push the label (the "author
14030        // omitted the slot entirely" partition). The pair jointly pins
14031        // the accessor + declared-slot enumerator composition: any
14032        // future silent detour that had the accessor collapse
14033        // `Some(Placement::default())` to `None` (a `.filter(|p|
14034        // p.clusters().is_empty().not())` projection) would silently
14035        // absorb the "declared but empty" arm at the accessor boundary
14036        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
14037        // kind-coherence gate would silently accept a struct-literal
14038        // `Caixa` carrying the drift.
14039        //
14040        // Peer of the sibling
14041        // `declared_servico_slots_limits_arm_routes_through_accessor`
14042        // (b2bd9d7),
14043        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14044        // (35d8b52), and
14045        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
14046        // (5d23d29) composition pins on the sibling `:limits` /
14047        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
14048        // — same "the enumerator gate must route through the
14049        // substrate-primitive typed dispatch" discipline extended onto
14050        // the second of the three M3 mesh-slot axes so the
14051        // [`Caixa::declared_mesh_slots`] enumerator carries the same
14052        // routing invariant on the `:placement` arm as the peer
14053        // `:politicas` arm.
14054        use crate::aplicacao::Placement;
14055        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
14056        let slots = c.declared_mesh_slots();
14057        assert!(
14058            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
14059            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
14060             when `:placement` is Some (even for Placement::default()) \
14061             — the accessor and the enumerator gate must route through \
14062             the same substrate-primitive typed dispatch on the outer \
14063             :placement presence bit (got slots={slots:?})",
14064        );
14065        let c = caixa_aplicacao_with_placement(None);
14066        let slots = c.declared_mesh_slots();
14067        assert!(
14068            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
14069            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
14070             when `:placement` is None — the author-omitted arm must \
14071             route through the accessor's None-return unchanged (got \
14072             slots={slots:?})",
14073        );
14074    }
14075
14076    #[test]
14077    fn aplicacao_view_placement_arm_folds_through_accessor() {
14078        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
14079        // Aplicacao-composition seed must fold through
14080        // [`Caixa::placement`], not the raw
14081        // `self.placement.clone().unwrap_or_default()` field-borrow.
14082        // Structurally: a `Caixa { placement: Some(Placement {
14083        // estrategia: Replicated, clusters: ["rio"], .. default }),
14084        // kind: Aplicacao, .. }` must surface a projected
14085        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
14086        // `placement().clusters()` byte-equal the outer composite's
14087        // authored values (the fold must project the authored
14088        // composite verbatim), a `Caixa { placement:
14089        // Some(Placement::default()), kind: Aplicacao, .. }` must
14090        // surface an [`crate::AplicacaoSpec`] whose `placement()`
14091        // byte-equals [`crate::aplicacao::Placement::default`] (the
14092        // fold's empty-composite arm collapses to the same default
14093        // the author-omitted arm does), and a `Caixa { placement:
14094        // None, kind: Aplicacao, .. }` must surface an
14095        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
14096        // [`crate::aplicacao::Placement::default`] (the "author
14097        // omitted the slot entirely" arm folds through the
14098        // `unwrap_or_default` onto the cluster-default). The triad
14099        // jointly pins the accessor + Aplicacao-composition seed
14100        // composition: any future silent detour that had the accessor
14101        // divert the raw slot away from the seed's fold (an operator-
14102        // resolved overlay's default-fold arm silently differing from
14103        // the raw slot's default-fold arm) would silently split the
14104        // build-time distribution-artifact emission gate from the
14105        // caixa-mesh renderer's Aplicacao-view input at the
14106        // composition boundary.
14107        use crate::aplicacao::{Placement, PlacementStrategy};
14108        let c = caixa_aplicacao_with_placement(Some(Placement {
14109            estrategia: PlacementStrategy::Replicated,
14110            clusters: vec!["rio".into()],
14111            affinity: None,
14112            shard_key: None,
14113        }));
14114        let view = c.aplicacao_view().unwrap();
14115        assert_eq!(
14116            view.placement().estrategia(),
14117            PlacementStrategy::Replicated,
14118            "Caixa::aplicacao_view must fold the authored :placement \
14119             :estrategia scalar 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-scalar \
14122             drift (got {:?})",
14123            view.placement().estrategia(),
14124        );
14125        assert_eq!(
14126            view.placement().clusters(),
14127            &["rio"],
14128            "Caixa::aplicacao_view must fold the authored :placement \
14129             :clusters list through the accessor verbatim onto the \
14130             projected AplicacaoSpec — a future silent detour at the \
14131             seed's fold arm would surface here as a projected-list \
14132             drift (got {:?})",
14133            view.placement().clusters(),
14134        );
14135        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
14136        let view = c.aplicacao_view().unwrap();
14137        assert_eq!(
14138            view.placement(),
14139            &Placement::default(),
14140            "Caixa::aplicacao_view must fold Some(Placement::default()) \
14141             through the accessor onto Placement::default — the empty- \
14142             composite arm collapses to the same default the author- \
14143             omitted arm does (got {:?})",
14144            view.placement(),
14145        );
14146        let c = caixa_aplicacao_with_placement(None);
14147        let view = c.aplicacao_view().unwrap();
14148        assert_eq!(
14149            view.placement(),
14150            &Placement::default(),
14151            "Caixa::aplicacao_view must fold None through the accessor's \
14152             unwrap_or_default onto Placement::default — the author- \
14153             omitted arm must route through the accessor's None-return \
14154             unchanged (got {:?})",
14155            view.placement(),
14156        );
14157    }
14158
14159    #[test]
14160    fn placement_projects_option_ref_by_borrow() {
14161        // The by-borrow pin: [`Caixa::placement`] returns
14162        // `Option<&Placement>` by borrow — the returned reference
14163        // borrows the underlying `Option<Placement>` storage of the
14164        // `:placement` slot and the accessor must not clone the
14165        // backing composite on every call. Peer of the sibling
14166        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
14167        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
14168        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
14169        // pins on the outer top-level [`Caixa`]
14170        // `Option<&Composite>`-return sub-family — extended here to
14171        // the fourth axis of the same sub-family: the accessor's
14172        // returned reference must borrow from `&self` (the returned
14173        // reference's lifetime is tied to `&self`), and calling the
14174        // accessor twice on the same [`Caixa`] must yield references
14175        // that are pointer-equal (the underlying byte-buffer is the
14176        // storage `Placement`'s allocation, not a fresh copy) as well
14177        // as value-equal (idempotent, no side effects on `&self`).
14178        //
14179        // Pins against a future silent detour that returned an owned
14180        // `Placement` (which would type-check via the `Clone` impl
14181        // but silently clone on every call), a `&Placement` panic-
14182        // return on the `None` arm (which would collapse the load-
14183        // bearing `Option` presence-bit into a runtime panic), or a
14184        // one-arm-only accessor that returned a saturating composite
14185        // on some sentinel input.
14186        use crate::aplicacao::{Placement, PlacementStrategy};
14187        for placement in [
14188            Some(Placement::default()),
14189            Some(Placement {
14190                estrategia: PlacementStrategy::Sharded,
14191                clusters: vec!["rio".into(), "sao-paulo".into()],
14192                affinity: Some("data-locality".into()),
14193                shard_key: Some("$tenantId".into()),
14194            }),
14195        ] {
14196            let c = caixa_aplicacao_with_placement(placement.clone());
14197            let first = c.placement().unwrap();
14198            let second = c.placement().unwrap();
14199            assert_eq!(
14200                first, second,
14201                "Caixa::placement must be idempotent — two successive \
14202                 calls on the same &self must return the same \
14203                 &Placement",
14204            );
14205            assert!(
14206                std::ptr::eq(first, second),
14207                "Caixa::placement must borrow the underlying \
14208                 Option<Placement> storage — two successive calls \
14209                 must return references with the same backing pointer \
14210                 (a fresh Placement clone would change the pointer on \
14211                 every call)",
14212            );
14213            assert_eq!(
14214                Some(first),
14215                placement.as_ref(),
14216                "Caixa::placement must return :placement verbatim by \
14217                 borrow — got {first:?}, expected {:?}",
14218                placement.as_ref(),
14219            );
14220        }
14221        let c = caixa_aplicacao_with_placement(None);
14222        assert!(
14223            c.placement().is_none(),
14224            "Caixa::placement must return None when :placement is \
14225             absent — the author-omitted arm must project through the \
14226             accessor's Option::None unchanged",
14227        );
14228    }
14229
14230    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
14231
14232    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
14233        use crate::aplicacao::{Membro, WitContract};
14234        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14235        c.kind = CaixaKind::Aplicacao;
14236        c.membros = vec![Membro {
14237            caixa: "a".into(),
14238            versao: "^0.1".into(),
14239        }];
14240        c.contratos = vec![WitContract {
14241            de: "a".into(),
14242            para: "a".into(),
14243            wit: "wasi:http/proxy".into(),
14244            endpoint: Some("/x".into()),
14245            subject: None,
14246            slot: None,
14247        }];
14248        c.entrada = entrada;
14249        c
14250    }
14251
14252    #[test]
14253    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
14254        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
14255        // composite optional-composite-reference-shape pin:
14256        // [`Caixa::entrada`] must return the `:entrada` typed
14257        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
14258        // reference over the same backing storage the raw
14259        // `self.entrada.as_ref()` field access borrows from,
14260        // byte-equal across every representative fixture in the
14261        // accept-set — the author-omitted `None` shape (the
14262        // "cluster-internal Aplicacao" partition every downstream
14263        // Gateway-API emitter treats as "emit no listener + no
14264        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
14265        // (empty `paths` — the resolved-paths fallback the peer
14266        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
14267        // onto the substrate catch-all), and a fully-populated
14268        // multi-path-with-non-default-port fixture (the canonical
14269        // shape a public HTTP Aplicacao carries).
14270        //
14271        // Pins against a future silent detour that returned a fresh-
14272        // cloned [`crate::aplicacao::Entrada`] copy (which would
14273        // type-check via the `Clone` impl but silently break every
14274        // downstream caller that relied on the reference sharing the
14275        // composite's backing identity), a reference to an operator-
14276        // resolved overlay (the future per-cluster
14277        // `:entrada-overrides` slot — its resolution must land at
14278        // exactly this accessor body, not silently divert the raw
14279        // slot away from the peer [`Caixa::declared_mesh_slots`]
14280        // enumerator's presence probe), or an axis-shuffled projection
14281        // (a future detour that swapped `host` and `para` through the
14282        // accessor would silently split the paired
14283        // [`Caixa::aplicacao_view`] seed's forward input from the
14284        // sibling M3 gateway-artifact emitter's projection input).
14285        //
14286        // Fifth and final outer top-level [`Caixa`]
14287        // `Option<&Composite>`-return composite-reference accessor pin
14288        // on the substrate primitive — peer of the sibling
14289        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14290        // (b2bd9d7),
14291        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14292        // (35d8b52),
14293        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
14294        // (5d23d29), and
14295        // `placement_returns_placement_option_ref_verbatim_across_permutations`
14296        // (4fb8074) opening tetrad pins on the outer top-level
14297        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14298        // here to the third and final M3 mesh-slot axis so the closed
14299        // outer `Option<&Composite>` sub-family carries the same
14300        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
14301        // accessor discipline across all five arms.
14302        use crate::aplicacao::Entrada;
14303        let fixtures: Vec<Option<Entrada>> = vec![
14304            None,
14305            Some(Entrada {
14306                host: "checkout.quero.cloud".into(),
14307                para: "gateway".into(),
14308                paths: Vec::new(),
14309                port: crate::DEFAULT_SERVICO_PORT,
14310            }),
14311            Some(Entrada {
14312                host: "api.pleme.io".into(),
14313                para: "public-api".into(),
14314                paths: vec!["/v1".into(), "/v2".into()],
14315                port: 8080,
14316            }),
14317        ];
14318        for entrada in fixtures {
14319            let c = caixa_aplicacao_with_entrada(entrada.clone());
14320            assert_eq!(
14321                c.entrada(),
14322                entrada.as_ref(),
14323                "Caixa::entrada must return :entrada verbatim (got \
14324                 {:?}, expected {:?})",
14325                c.entrada(),
14326                entrada.as_ref(),
14327            );
14328            match (c.entrada(), c.entrada.as_ref()) {
14329                (Some(a), Some(b)) => assert!(
14330                    std::ptr::eq(a, b),
14331                    "Caixa::entrada accessor and self.entrada.as_ref() \
14332                     field access must borrow the same backing storage \
14333                     — the accessor is the substrate-primitive typed \
14334                     dispatch every downstream Aplicacao-external- \
14335                     gateway composite consumer must route through, and \
14336                     a reference-identity split would silently break \
14337                     every consumer that relied on the borrow sharing \
14338                     the composite's storage",
14339                ),
14340                (None, None) => {}
14341                _ => panic!(
14342                    "Caixa::entrada presence bit must byte-equal \
14343                     self.entrada.is_some() — a presence-bit drift \
14344                     would silently split the paired \
14345                     Caixa::aplicacao_view Aplicacao-composition seed's \
14346                     traversal head from the peer \
14347                     Caixa::declared_mesh_slots M3 declared-slot \
14348                     enumerator's presence probe",
14349                ),
14350            }
14351            assert_eq!(
14352                c.entrada().is_some(),
14353                c.entrada.is_some(),
14354                "Caixa::entrada().is_some() must byte-equal \
14355                 self.entrada.is_some() — a presence-bit drift would \
14356                 silently split every downstream Option<&Entrada> \
14357                 consumer's partition on the cluster-internal arm",
14358            );
14359        }
14360    }
14361
14362    #[test]
14363    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
14364        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
14365        // presence-probe arm must key off [`Caixa::entrada`], not the
14366        // raw `self.entrada.is_some()` field-probe. Structurally: a
14367        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
14368        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
14369        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
14370        // presence bit is `Some`, so the M3 kind-coherence gate must
14371        // surface the slot as "declared" even when every per-axis
14372        // scalar defers to the substrate catch-all / default port),
14373        // and a `Caixa { entrada: None, .. }` must NOT push the label
14374        // (the "author omitted the slot entirely" partition). The pair
14375        // jointly pins the accessor + declared-slot enumerator
14376        // composition: any future silent detour that had the accessor
14377        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
14378        // `.filter(|e| !e.paths.is_empty())` projection) would silently
14379        // absorb the "declared but empty-paths" arm at the accessor
14380        // boundary and the
14381        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
14382        // coherence gate would silently accept a struct-literal
14383        // `Caixa` carrying the drift.
14384        //
14385        // Peer of the sibling
14386        // `declared_servico_slots_limits_arm_routes_through_accessor`
14387        // (b2bd9d7),
14388        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14389        // (35d8b52),
14390        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
14391        // (5d23d29), and
14392        // `declared_mesh_slots_placement_arm_routes_through_accessor`
14393        // (4fb8074) composition pins on the sibling `:limits` /
14394        // `:behavior` / `:politicas` / `:placement` outer-
14395        // `Option<&Composite>` arms — same "the enumerator gate must
14396        // route through the substrate-primitive typed dispatch"
14397        // discipline extended onto the third and final M3 mesh-slot
14398        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
14399        // carries the routing invariant on every M3 mesh-slot arm.
14400        use crate::aplicacao::Entrada;
14401        let c = caixa_aplicacao_with_entrada(Some(Entrada {
14402            host: "checkout.quero.cloud".into(),
14403            para: "gateway".into(),
14404            paths: Vec::new(),
14405            port: crate::DEFAULT_SERVICO_PORT,
14406        }));
14407        let slots = c.declared_mesh_slots();
14408        assert!(
14409            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
14410            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
14411             `:entrada` is Some (even for empty-paths / default-port) \
14412             — the accessor and the enumerator gate must route through \
14413             the same substrate-primitive typed dispatch on the outer \
14414             :entrada presence bit (got slots={slots:?})",
14415        );
14416        let c = caixa_aplicacao_with_entrada(None);
14417        let slots = c.declared_mesh_slots();
14418        assert!(
14419            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
14420            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
14421             when `:entrada` is None — the author-omitted arm must \
14422             route through the accessor's None-return unchanged (got \
14423             slots={slots:?})",
14424        );
14425    }
14426
14427    #[test]
14428    fn aplicacao_view_entrada_arm_folds_through_accessor() {
14429        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
14430        // Aplicacao-composition seed must fold through
14431        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
14432        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
14433        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
14434        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
14435        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
14436        // equals the outer composite's authored value (the fold must
14437        // project the authored composite verbatim), and a `Caixa {
14438        // entrada: None, kind: Aplicacao, .. }` must surface an
14439        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
14440        // "author omitted the slot entirely" arm folds through the
14441        // accessor's `Option::cloned` onto the same `None` presence
14442        // bit — unlike the peer `:politicas` / `:placement` arms
14443        // `:entrada` has no cluster-default fold, the omitted arm
14444        // stays omitted). The pair jointly pins the accessor +
14445        // Aplicacao-composition seed composition: any future silent
14446        // detour that had the accessor divert the raw slot away from
14447        // the seed's fold (an operator-resolved overlay's forward arm
14448        // silently differing from the raw slot's forward arm) would
14449        // silently split the build-time gateway-artifact emission gate
14450        // from the caixa-mesh renderer's Aplicacao-view input at the
14451        // composition boundary.
14452        use crate::aplicacao::Entrada;
14453        let authored = Entrada {
14454            host: "api.pleme.io".into(),
14455            para: "public-api".into(),
14456            paths: vec!["/v1".into()],
14457            port: 8080,
14458        };
14459        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
14460        let view = c.aplicacao_view().unwrap();
14461        assert_eq!(
14462            view.entrada(),
14463            Some(&authored),
14464            "Caixa::aplicacao_view must fold the authored :entrada \
14465             composite through the accessor verbatim onto the \
14466             projected AplicacaoSpec — a future silent detour at the \
14467             seed's fold arm would surface here as a projected- \
14468             composite drift (got {:?})",
14469            view.entrada(),
14470        );
14471        let c = caixa_aplicacao_with_entrada(None);
14472        let view = c.aplicacao_view().unwrap();
14473        assert!(
14474            view.entrada().is_none(),
14475            "Caixa::aplicacao_view must fold None through the \
14476             accessor's Option::cloned onto None — the author- \
14477             omitted arm must route through the accessor's None-return \
14478             unchanged (got {:?})",
14479            view.entrada(),
14480        );
14481    }
14482
14483    #[test]
14484    fn entrada_projects_option_ref_by_borrow() {
14485        // The by-borrow pin: [`Caixa::entrada`] returns
14486        // `Option<&Entrada>` by borrow — the returned reference
14487        // borrows the underlying `Option<Entrada>` storage of the
14488        // `:entrada` slot and the accessor must not clone the backing
14489        // composite on every call. Peer of the sibling
14490        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
14491        // `behavior_projects_option_ref_by_borrow` (35d8b52),
14492        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
14493        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
14494        // borrow pins on the outer top-level [`Caixa`]
14495        // `Option<&Composite>`-return sub-family — extended here to
14496        // the fifth and final axis of the same sub-family, closing
14497        // the discipline: the accessor's returned reference must
14498        // borrow from `&self` (the returned reference's lifetime is
14499        // tied to `&self`), and calling the accessor twice on the
14500        // same [`Caixa`] must yield references that are pointer-equal
14501        // (the underlying byte-buffer is the storage `Entrada`'s
14502        // allocation, not a fresh copy) as well as value-equal
14503        // (idempotent, no side effects on `&self`).
14504        //
14505        // Pins against a future silent detour that returned an owned
14506        // `Entrada` (which would type-check via the `Clone` impl but
14507        // silently clone on every call), a `&Entrada` panic-return on
14508        // the `None` arm (which would collapse the load-bearing
14509        // `Option` presence-bit into a runtime panic), or a one-arm-
14510        // only accessor that returned a saturating composite on some
14511        // sentinel input.
14512        use crate::aplicacao::Entrada;
14513        for entrada in [
14514            Some(Entrada {
14515                host: "checkout.quero.cloud".into(),
14516                para: "gateway".into(),
14517                paths: Vec::new(),
14518                port: crate::DEFAULT_SERVICO_PORT,
14519            }),
14520            Some(Entrada {
14521                host: "api.pleme.io".into(),
14522                para: "public-api".into(),
14523                paths: vec!["/v1".into(), "/v2".into()],
14524                port: 8080,
14525            }),
14526        ] {
14527            let c = caixa_aplicacao_with_entrada(entrada.clone());
14528            let first = c.entrada().unwrap();
14529            let second = c.entrada().unwrap();
14530            assert_eq!(
14531                first, second,
14532                "Caixa::entrada must be idempotent — two successive \
14533                 calls on the same &self must return the same &Entrada",
14534            );
14535            assert!(
14536                std::ptr::eq(first, second),
14537                "Caixa::entrada must borrow the underlying \
14538                 Option<Entrada> storage — two successive calls must \
14539                 return references with the same backing pointer (a \
14540                 fresh Entrada clone would change the pointer on every \
14541                 call)",
14542            );
14543            assert_eq!(
14544                Some(first),
14545                entrada.as_ref(),
14546                "Caixa::entrada must return :entrada verbatim by \
14547                 borrow — got {first:?}, expected {:?}",
14548                entrada.as_ref(),
14549            );
14550        }
14551        let c = caixa_aplicacao_with_entrada(None);
14552        assert!(
14553            c.entrada().is_none(),
14554            "Caixa::entrada must return None when :entrada is absent \
14555             — the author-omitted arm must project through the \
14556             accessor's Option::None unchanged",
14557        );
14558    }
14559
14560    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
14561
14562    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
14563        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14564        c.estrategia = estrategia;
14565        c
14566    }
14567
14568    #[test]
14569    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
14570        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
14571        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
14572        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
14573        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
14574        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
14575        // over the same discriminant the raw `self.estrategia` field
14576        // access carries, byte-equal across every representative fixture
14577        // in the accept-set — the author-omitted `None` shape (the
14578        // "defer to [`RestartStrategy::default`] through the
14579        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
14580        // every non-`Supervisor`-kind `defcaixa` carries by
14581        // `#[serde(default)]`), and each of the four closed-set variants
14582        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
14583        // / [`RestartStrategy::RestForOne`] /
14584        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
14585        // partitions on.
14586        //
14587        // Pins against a future silent detour that re-derived the
14588        // strategy from a peer axis (an accidental fallback to
14589        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
14590        // collapse that read the outer `:children` list-length axis into
14591        // the strategy discriminator at the accessor boundary), a
14592        // stale-derive detour that substituted [`RestartStrategy::default`]
14593        // when the outer `Option` held `None` (which would silently
14594        // collapse the load-bearing "author explicitly declared
14595        // `:estrategia OneForOne`" vs "author omitted the slot and
14596        // inherited the default" partition the [`Self::declared_supervisor_slots`]
14597        // presence-probe reads — the enumerator gate would still push
14598        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
14599        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
14600        // kind-coherence gate's traversal head from the
14601        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
14602        // composition head), a reference to an operator-resolved overlay
14603        // (the future per-cluster `:estrategia-overrides` slot — its
14604        // resolution must land at exactly this accessor body, not
14605        // silently divert the raw slot away from a second consumer), or
14606        // an axis-remap projection (a future detour that mapped
14607        // `OneForAll` through the accessor onto `OneForOne` would
14608        // silently split every downstream sibling-restart-strategy
14609        // consumer's per-arm fan-out).
14610        //
14611        // First outer top-level [`Caixa`] `Option<Copy>`-return
14612        // supervisor-tree-slot flat-spread accessor pin on the substrate
14613        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
14614        // projection pattern the sibling per-`Caixa` `:max-restarts` /
14615        // `:restart-window` future outer-scalar pins fold on. Peer of
14616        // the inner-altitude
14617        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
14618        // (eafb619) pin on the post-composition [`SupervisorSpec`]
14619        // altitude — same "the substrate-primitive accessor must byte-
14620        // equal the raw field access verbatim across every author-
14621        // declared value" discipline extended onto the pre-composition
14622        // outer author-surface [`Caixa`] altitude. Peer of the closed
14623        // outer-`Caixa` `Option<&Composite>` composite-reference family
14624        // the sibling `limits` / `behavior` / `politicas` / `placement` /
14625        // `entrada`
14626        // `..._returns_..._option_ref_verbatim_across_permutations` pins
14627        // already carry on the outer `Option<&Composite>` altitude.
14628        use crate::supervisor::RestartStrategy;
14629        let fixtures: Vec<Option<RestartStrategy>> = vec![
14630            None,
14631            Some(RestartStrategy::OneForOne),
14632            Some(RestartStrategy::OneForAll),
14633            Some(RestartStrategy::RestForOne),
14634            Some(RestartStrategy::SimpleOneForOne),
14635        ];
14636        for estrategia in fixtures {
14637            let c = caixa_with_estrategia(estrategia);
14638            assert_eq!(
14639                c.estrategia(),
14640                estrategia,
14641                "Caixa::estrategia must return :estrategia verbatim (got \
14642                 {:?}, expected {:?})",
14643                c.estrategia(),
14644                estrategia,
14645            );
14646            assert_eq!(
14647                c.estrategia(),
14648                c.estrategia,
14649                "Caixa::estrategia accessor and self.estrategia field \
14650                 access must byte-equal — the accessor is the substrate-\
14651                 primitive typed dispatch every downstream supervisor-\
14652                 tree flat-spread consumer must route through, and a \
14653                 discriminant split would silently break every consumer \
14654                 that relied on the accessor sharing the field's own \
14655                 Option<Copy> shape",
14656            );
14657            assert_eq!(
14658                c.estrategia().is_some(),
14659                c.estrategia.is_some(),
14660                "Caixa::estrategia().is_some() must byte-equal \
14661                 self.estrategia.is_some() — a presence-bit drift would \
14662                 silently split the paired Caixa::declared_supervisor_slots \
14663                 presence-probe arm from the Caixa::supervisor_view \
14664                 unwrap_or_default() fold's composition input",
14665            );
14666        }
14667    }
14668
14669    #[test]
14670    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
14671        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
14672        // `:estrategia` presence-probe arm must key off
14673        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
14674        // field-probe. Structurally: every `Caixa { estrategia:
14675        // Some(RestartStrategy::_), .. }` variant must push
14676        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
14677        // (the presence bit is `Some` for every closed-set variant, so
14678        // the M2 supervisor-tree kind-coherence gate must surface the
14679        // slot as "declared" regardless of which variant the author
14680        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
14681        // the label (the "author omitted the slot entirely, deferring
14682        // to [`RestartStrategy::default`] through the supervisor_view
14683        // fold" partition). The pair jointly pins the accessor +
14684        // declared-slot enumerator composition: any future silent detour
14685        // that had the accessor collapse `Some(RestartStrategy::default())`
14686        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
14687        // projection) would silently absorb the "declared but default-
14688        // valued" arm at the accessor boundary and the
14689        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
14690        // coherence gate would silently accept a struct-literal `Caixa`
14691        // carrying the drift.
14692        //
14693        // Peer of the sibling per-`Caixa`
14694        // `declared_servico_slots_limits_arm_routes_through_accessor`
14695        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
14696        // `Option<&LimitsSpec>` composition axis — same "the enumerator
14697        // gate must route through the substrate-primitive typed
14698        // dispatch" discipline extended onto the flat-spread M2
14699        // supervisor-tree `Option<RestartStrategy>`-composition surface,
14700        // opening the outer-`Caixa` supervisor-tree-slot arm of the
14701        // composition-pin family.
14702        use crate::supervisor::RestartStrategy;
14703        for estrategia in [
14704            RestartStrategy::OneForOne,
14705            RestartStrategy::OneForAll,
14706            RestartStrategy::RestForOne,
14707            RestartStrategy::SimpleOneForOne,
14708        ] {
14709            let c = caixa_with_estrategia(Some(estrategia));
14710            let slots = c.declared_supervisor_slots();
14711            assert!(
14712                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
14713                "declared_supervisor_slots must push \
14714                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
14715                 Some({estrategia:?}) — the accessor and the enumerator \
14716                 gate must route through the same substrate-primitive \
14717                 typed dispatch on the outer :estrategia presence bit \
14718                 (got slots={slots:?})",
14719            );
14720        }
14721        let c = caixa_with_estrategia(None);
14722        let slots = c.declared_supervisor_slots();
14723        assert!(
14724            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
14725            "declared_supervisor_slots must NOT push \
14726             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
14727             — the author-omitted arm must route through the accessor's \
14728             None-return unchanged (got slots={slots:?})",
14729        );
14730    }
14731
14732    #[test]
14733    fn supervisor_view_estrategia_arm_routes_through_accessor() {
14734        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
14735        // [`SupervisorSpec`] construction arm must key off
14736        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
14737        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
14738        // for every `:kind Supervisor` `Caixa` carrying an author-
14739        // declared `Some(RestartStrategy::_)` variant, the composed
14740        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
14741        // outer accessor's declared variant unchanged; and for a
14742        // `:kind Supervisor` `Caixa` carrying `None`, the composed
14743        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
14744        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
14745        // arm the flat-spread `unwrap_or_default()` fold projects to on
14746        // the author-omitted arm — this is the *composition* between the
14747        // outer `Option<RestartStrategy>` accessor's presence-bit
14748        // surface and the inner post-composition non-`Option`
14749        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
14750        // pins the accessor + supervisor_view composition: any future
14751        // silent detour that had the accessor promote `None` to
14752        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
14753        // projection) would silently collapse the two arms into one at
14754        // the accessor boundary and the [`Self::declared_supervisor_slots`]
14755        // presence probe would silently drift from the composition site.
14756        //
14757        // Peer of the sibling M2 supervisor-slot post-composition
14758        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
14759        // pin on the [`SupervisorSpec::validate`] altitude — this pin
14760        // extends that inner-altitude accessor-routing discipline onto
14761        // the pre-composition outer author-surface [`Caixa`] altitude,
14762        // pinning the composition edge between the flat-spread outer
14763        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
14764        // `RestartStrategy` axes.
14765        use crate::CaixaKind;
14766        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
14767        for estrategia in [
14768            RestartStrategy::OneForOne,
14769            RestartStrategy::OneForAll,
14770            RestartStrategy::RestForOne,
14771            RestartStrategy::SimpleOneForOne,
14772        ] {
14773            let mut c = caixa_with_estrategia(Some(estrategia));
14774            c.kind = CaixaKind::Supervisor;
14775            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
14776            // shape partition through the [`gen_platform::IsVariant`]
14777            // derive-generated
14778            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
14779            // than the raw `matches!(estrategia, RestartStrategy::
14780            // SimpleOneForOne)` open-coded pattern-match — same closed-
14781            // set-typed-enum arm-discriminator dispatch discipline the
14782            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
14783            // convergence (915a934) extended onto its two paired positive
14784            // / negated `matches!` sites and the peer
14785            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
14786            // predicate convergence (766ec63) extended onto the M3 mesh-
14787            // slot per-`:placement` distribution-strategy discriminator
14788            // axis. See the sibling `supervisor::tests::
14789            // round_trip_all_strategies` and
14790            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
14791            // fixtures — the three sites (all test-only,
14792            // acknowledged in 915a934's Prior-commits footnote as the
14793            // outstanding follow-up) now consult one typed dispatch on
14794            // the substrate primitive.
14795            c.children = if estrategia.is_simple_one_for_one() {
14796                Vec::new()
14797            } else {
14798                vec![ChildSpec {
14799                    caixa: "worker".into(),
14800                    versao: "^0.1".into(),
14801                    restart: RestartPolicy::Permanent,
14802                }]
14803            };
14804            let view = c.supervisor_view().expect(
14805                "supervisor_view must materialize a SupervisorSpec for a \
14806                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
14807            );
14808            assert_eq!(
14809                view.estrategia(),
14810                c.estrategia().unwrap(),
14811                "supervisor_view must carry the outer Caixa::estrategia() \
14812                 declared variant onto the composed SupervisorSpec.estrategia \
14813                 field verbatim on the Some arm (got {:?}, expected {:?})",
14814                view.estrategia(),
14815                c.estrategia().unwrap(),
14816            );
14817        }
14818        // The author-omitted arm: outer `None` → composed
14819        // `RestartStrategy::default()` through the flat-spread
14820        // `unwrap_or_default()` fold.
14821        let mut c = caixa_with_estrategia(None);
14822        c.kind = CaixaKind::Supervisor;
14823        // Populate children so the sibling supervisor slots are coherent
14824        // for the [`Self::supervisor_view`] projection; the `:estrategia`
14825        // arm still defers to [`RestartStrategy::default`] on the
14826        // author-omitted arm even when the sibling slots carry values.
14827        c.children = vec![ChildSpec {
14828            caixa: "worker".into(),
14829            versao: "^0.1".into(),
14830            restart: RestartPolicy::Permanent,
14831        }];
14832        let view = c.supervisor_view().expect(
14833            "supervisor_view must materialize a SupervisorSpec for a \
14834             :kind Supervisor Caixa carrying a None `:estrategia` slot",
14835        );
14836        assert_eq!(
14837            view.estrategia(),
14838            RestartStrategy::default(),
14839            "supervisor_view must project the outer Caixa::estrategia() \
14840             None arm onto RestartStrategy::default() through the flat-\
14841             spread unwrap_or_default() fold (got {:?}, expected {:?})",
14842            view.estrategia(),
14843            RestartStrategy::default(),
14844        );
14845        assert!(
14846            c.estrategia().is_none(),
14847            "Caixa::estrategia() must remain None on the author-omitted \
14848             arm — the supervisor_view fold must not mutate the outer \
14849             flat-spread presence bit",
14850        );
14851    }
14852
14853    #[test]
14854    fn estrategia_projects_option_by_copy() {
14855        // The by-`Copy` pin: [`Caixa::estrategia`] returns
14856        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
14857        // the accessor does not borrow `&self` past the call (no
14858        // lifetime on the return type), and calling the accessor twice
14859        // on the same [`Caixa`] must yield discriminant-equal values
14860        // (idempotent, no side effects on `&self`). Peer of the sibling
14861        // outer-`Caixa` `Option<&Composite>` by-borrow
14862        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
14863        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
14864        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
14865        // `placement_projects_option_ref_by_borrow` (4fb8074) /
14866        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
14867        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
14868        // extended here to the outer-`Caixa` `Option<Copy>`-return
14869        // flat-spread axis. The `Copy` discipline replaces the pointer-
14870        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
14871        // `Copy` discriminant is definitionally the same discriminant, so
14872        // the axis reduces to discriminant equality).
14873        //
14874        // Pins against a future silent detour that returned a fresh
14875        // `Option<&RestartStrategy>` (which would type-check but silently
14876        // introduce a borrow of `&self` past the call, collapsing the
14877        // load-bearing "no lifetime on the return type" `Copy` projection
14878        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
14879        // read side effect that flipped the outer discriminant on
14880        // successive calls, or an axis-remap projection that returned a
14881        // different variant than the field storage.
14882        use crate::supervisor::RestartStrategy;
14883        for estrategia in [
14884            Some(RestartStrategy::OneForOne),
14885            Some(RestartStrategy::OneForAll),
14886            Some(RestartStrategy::RestForOne),
14887            Some(RestartStrategy::SimpleOneForOne),
14888        ] {
14889            let c = caixa_with_estrategia(estrategia);
14890            let first = c.estrategia();
14891            let second = c.estrategia();
14892            assert_eq!(
14893                first, second,
14894                "Caixa::estrategia must be idempotent — two successive \
14895                 calls on the same &self must return the same \
14896                 Option<RestartStrategy>",
14897            );
14898            assert_eq!(
14899                first, estrategia,
14900                "Caixa::estrategia must return :estrategia verbatim by \
14901                 Copy — got {first:?}, expected {estrategia:?}",
14902            );
14903        }
14904        let c = caixa_with_estrategia(None);
14905        assert!(
14906            c.estrategia().is_none(),
14907            "Caixa::estrategia must return None when :estrategia is \
14908             absent — the author-omitted arm must project through the \
14909             accessor's Option::None unchanged",
14910        );
14911    }
14912
14913    // ── Caixa::max_restarts / Caixa::restart_window —
14914    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
14915    //    (Option<u32> / Option<&str>) folding on the ed04d3c
14916    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
14917
14918    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
14919        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14920        c.max_restarts = max_restarts;
14921        c
14922    }
14923
14924    fn caixa_supervisor_with_max_restarts_and_window(
14925        max_restarts: Option<u32>,
14926        restart_window: Option<&str>,
14927    ) -> Caixa {
14928        use crate::CaixaKind;
14929        use crate::supervisor::{ChildSpec, RestartPolicy};
14930        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
14931        c.kind = CaixaKind::Supervisor;
14932        c.max_restarts = max_restarts;
14933        c.restart_window = restart_window.map(str::to_string);
14934        c.children = vec![ChildSpec {
14935            caixa: "worker".into(),
14936            versao: "^0.1".into(),
14937            restart: RestartPolicy::Permanent,
14938        }];
14939        c
14940    }
14941
14942    #[test]
14943    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
14944        // Value-shape pin: [`Caixa::max_restarts`] returns the
14945        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
14946        // from the typed slot's own storage, byte-equal across the
14947        // author-omitted `None` arm (the "defer to the
14948        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
14949        // `{intensity, 5, 60}` default" partition every
14950        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
14951        // and each of the representative fixtures in the accept-set —
14952        // `0` (the zero-floor arm the peer
14953        // [`crate::supervisor::SupervisorSpec::validate`]
14954        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
14955        // the post-composition altitude — the accessor must ship the
14956        // raw slot verbatim so struct-literal fixtures continue to
14957        // expose the zero at the accessor boundary), the OTP-canonical
14958        // `5` default (`{intensity, 5, 60}` worker-supervisor from
14959        // Learn You Some Erlang), `1000` (the
14960        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
14961        // upper-bound gate accepts on the boundary), `u32::MAX` (a
14962        // past-the-cap sentinel that the substrate-primitive accessor
14963        // must still ship verbatim). Second outer top-level
14964        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
14965        // pin — folds on the sibling
14966        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
14967        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
14968        // onto the sibling `Option<u32>` restart-budget-count arm.
14969        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
14970        for max_restarts in fixtures {
14971            let c = caixa_with_max_restarts(max_restarts);
14972            assert_eq!(
14973                c.max_restarts(),
14974                max_restarts,
14975                "Caixa::max_restarts must return :max-restarts verbatim \
14976                 (got {:?}, expected {max_restarts:?})",
14977                c.max_restarts(),
14978            );
14979            assert_eq!(
14980                c.max_restarts(),
14981                c.max_restarts,
14982                "Caixa::max_restarts accessor and self.max_restarts \
14983                 field access must byte-equal — a presence-bit or count \
14984                 drift would silently split the paired \
14985                 Caixa::declared_supervisor_slots presence-probe arm \
14986                 from the Caixa::supervisor_view unwrap_or(5) fold's \
14987                 composition input",
14988            );
14989        }
14990    }
14991
14992    #[test]
14993    fn max_restarts_projects_option_by_copy() {
14994        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
14995        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
14996        // borrow `&self` past the call (no lifetime on the return type),
14997        // and calling the accessor twice on the same [`Caixa`] must
14998        // yield equal values (idempotent, no side effects). Peer of the
14999        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
15000        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
15001        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
15002            let c = caixa_with_max_restarts(max_restarts);
15003            let first = c.max_restarts();
15004            let second = c.max_restarts();
15005            assert_eq!(
15006                first, second,
15007                "Caixa::max_restarts must be idempotent — two successive \
15008                 calls on the same &self must return the same Option<u32>",
15009            );
15010            assert_eq!(
15011                first, max_restarts,
15012                "Caixa::max_restarts must return :max-restarts verbatim \
15013                 by Copy — got {first:?}, expected {max_restarts:?}",
15014            );
15015        }
15016    }
15017
15018    #[test]
15019    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
15020        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15021        // `:max-restarts` presence-probe arm must key off
15022        // [`Caixa::max_restarts`], not the raw
15023        // `self.max_restarts.is_some()` field-probe. Structurally: every
15024        // `Caixa { max_restarts: Some(_), .. }` variant must push
15025        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
15026        // list (the presence bit is `Some` for every representative
15027        // count, so the M2 kind-coherence gate must surface the slot as
15028        // "declared"), and a `Caixa { max_restarts: None, .. }` must
15029        // NOT push the label. Peer of the sibling
15030        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
15031        // (ed04d3c) composition pin — same routing-through-accessor
15032        // discipline extended onto the sibling flat-spread `Option<u32>`
15033        // arm.
15034        for max_restarts in [0u32, 5, 1000, u32::MAX] {
15035            let c = caixa_with_max_restarts(Some(max_restarts));
15036            let slots = c.declared_supervisor_slots();
15037            assert!(
15038                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
15039                "declared_supervisor_slots must push \
15040                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
15041                 is Some({max_restarts}) — the accessor and the \
15042                 enumerator gate must route through the same \
15043                 substrate-primitive typed dispatch on the outer \
15044                 :max-restarts presence bit (got slots={slots:?})",
15045            );
15046        }
15047        let c = caixa_with_max_restarts(None);
15048        let slots = c.declared_supervisor_slots();
15049        assert!(
15050            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
15051            "declared_supervisor_slots must NOT push \
15052             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
15053             None — the author-omitted arm must route through the \
15054             accessor's None-return unchanged (got slots={slots:?})",
15055        );
15056    }
15057
15058    #[test]
15059    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
15060        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
15061        // [`SupervisorSpec`] construction arm must key off
15062        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
15063        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
15064        // every `:kind Supervisor` `Caixa` carrying an author-declared
15065        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
15066        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
15067        // carrying `None`, the composed [`SupervisorSpec`]'s
15068        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
15069        // of the sibling
15070        // `supervisor_view_estrategia_arm_routes_through_accessor`
15071        // (ed04d3c) composition pin.
15072        for max_restarts in [1u32, 5, 1000] {
15073            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
15074            let view = c.supervisor_view().expect(
15075                "supervisor_view must materialize a SupervisorSpec for a \
15076                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
15077            );
15078            assert_eq!(
15079                view.max_restarts(),
15080                max_restarts,
15081                "supervisor_view must carry the outer \
15082                 Caixa::max_restarts() Some arm onto the composed \
15083                 SupervisorSpec.max_restarts field verbatim (got {}, \
15084                 expected {max_restarts})",
15085                view.max_restarts(),
15086            );
15087        }
15088        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
15089        let view = c.supervisor_view().expect(
15090            "supervisor_view must materialize a SupervisorSpec for a \
15091             :kind Supervisor Caixa carrying a None :max-restarts",
15092        );
15093        assert_eq!(
15094            view.max_restarts(),
15095            5,
15096            "supervisor_view must project the outer \
15097             Caixa::max_restarts() None arm onto the OTP-canonical \
15098             {{intensity, 5, 60}} default (5) through the flat-spread \
15099             unwrap_or(5) fold (got {})",
15100            view.max_restarts(),
15101        );
15102        assert!(
15103            c.max_restarts().is_none(),
15104            "Caixa::max_restarts() must remain None on the author-\
15105             omitted arm — the supervisor_view fold must not mutate \
15106             the outer flat-spread presence bit",
15107        );
15108    }
15109
15110    #[test]
15111    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
15112        // Value-shape pin: [`Caixa::restart_window`] returns the
15113        // `:restart-window` typed `Option<String>` verbatim as an
15114        // `Option<&str>`, borrowed from the typed slot's own storage,
15115        // byte-equal across the author-omitted `None` arm and each of
15116        // the representative fixtures in the accept-set — the canonical
15117        // `"60s"` from `{intensity, 5, 60}`, the sibling
15118        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
15119        // / `"0s"`) the shared codec's positive-set sweep pin covers,
15120        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
15121        // seconds drift the sibling [`Self::validate_restart_window`]
15122        // gate refuses; the accessor must ship the raw slot verbatim
15123        // so struct-literal fixtures continue to expose the drift at
15124        // the accessor boundary). Third outer top-level [`Caixa`]
15125        // supervisor-tree flat-spread pin — extends the sub-family onto
15126        // the sibling `Option<&str>` raw-duration-string arm.
15127        for window in [
15128            None,
15129            Some("60s"),
15130            Some("5m"),
15131            Some("1h"),
15132            Some("500ms"),
15133            Some("1.5s"),
15134            Some(""),
15135        ] {
15136            let c = caixa_with_restart_window(window);
15137            assert_eq!(
15138                c.restart_window(),
15139                window,
15140                "Caixa::restart_window must return :restart-window \
15141                 verbatim as Option<&str> (got {:?}, expected {window:?})",
15142                c.restart_window(),
15143            );
15144            assert_eq!(
15145                c.restart_window(),
15146                c.restart_window.as_deref(),
15147                "Caixa::restart_window accessor and \
15148                 self.restart_window.as_deref() field access must \
15149                 byte-equal — a byte-level drift would silently split \
15150                 the paired Caixa::declared_supervisor_slots \
15151                 presence-probe arm from the \
15152                 Caixa::validate_restart_window shared-codec gate and \
15153                 the Caixa::supervisor_view soft-swallowing fold",
15154            );
15155        }
15156    }
15157
15158    #[test]
15159    fn restart_window_projects_slice_by_borrow() {
15160        // The by-borrow pin: [`Caixa::restart_window`] returns
15161        // `Option<&str>` by borrow — the returned string slice borrows
15162        // the underlying `Option<String>` storage of the `:restart-window`
15163        // slot and the accessor must not clone on every call. Peer of
15164        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
15165        // by-borrow pins on the universal-axis scalar family
15166        // (`licenca_projects_option_ref_by_borrow` /
15167        // `descricao_projects_option_ref_by_borrow` and siblings) —
15168        // extended onto the M2 supervisor-tree flat-spread
15169        // `Option<&str>` raw-duration-string axis.
15170        for window in [None, Some("60s"), Some("5m"), Some("")] {
15171            let c = caixa_with_restart_window(window);
15172            let first = c.restart_window();
15173            let second = c.restart_window();
15174            assert_eq!(
15175                first, second,
15176                "Caixa::restart_window must be idempotent — two \
15177                 successive calls on the same &self must return the \
15178                 same Option<&str>",
15179            );
15180            if let (Some(a), Some(b)) = (first, second) {
15181                assert_eq!(
15182                    a.as_ptr(),
15183                    b.as_ptr(),
15184                    "Caixa::restart_window must borrow the underlying \
15185                     String storage — two successive Some-arm calls must \
15186                     return slices with the same backing pointer (a fresh \
15187                     String clone would change the pointer on every call)",
15188                );
15189            }
15190            assert_eq!(
15191                first, window,
15192                "Caixa::restart_window must return :restart-window \
15193                 verbatim by borrow — got {first:?}, expected {window:?}",
15194            );
15195        }
15196    }
15197
15198    #[test]
15199    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
15200        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15201        // `:restart-window` presence-probe arm must key off
15202        // [`Caixa::restart_window`], not the raw
15203        // `self.restart_window.is_some()` field-probe. Structurally:
15204        // every `Caixa { restart_window: Some(_), .. }` must push
15205        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
15206        // list, and a `Caixa { restart_window: None, .. }` must NOT
15207        // push the label. Peer of the sibling
15208        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
15209        // routing pin.
15210        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
15211            let c = caixa_with_restart_window(Some(window));
15212            let slots = c.declared_supervisor_slots();
15213            assert!(
15214                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
15215                "declared_supervisor_slots must push \
15216                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
15217                 `:restart-window` is Some({window:?}) — the accessor \
15218                 and the enumerator gate must route through the same \
15219                 substrate-primitive typed dispatch on the outer \
15220                 :restart-window presence bit (got slots={slots:?})",
15221            );
15222        }
15223        let c = caixa_with_restart_window(None);
15224        let slots = c.declared_supervisor_slots();
15225        assert!(
15226            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
15227            "declared_supervisor_slots must NOT push \
15228             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
15229             is None — the author-omitted arm must route through the \
15230             accessor's None-return unchanged (got slots={slots:?})",
15231        );
15232    }
15233
15234    #[test]
15235    fn validate_restart_window_arm_routes_through_accessor() {
15236        // Composition pin: [`Caixa::validate_restart_window`]'s
15237        // shared-codec fold arm must key off [`Caixa::restart_window`],
15238        // not the raw `self.restart_window.as_deref()` field-projection.
15239        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
15240        // express no reset" canonical shape); (2) a canonical `Some`
15241        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
15242        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
15243        // .. })` carrying the offending raw string verbatim. The three
15244        // arms jointly pin that the validator's raw-string binding is
15245        // the accessor's return, not a peer projection — any future
15246        // silent detour that had the accessor collapse `Some("")` to
15247        // `None` would silently absorb the empty-after-trim refusal
15248        // case at the accessor boundary.
15249        caixa_with_restart_window(None)
15250            .validate_restart_window()
15251            .expect("None :restart-window must validate through the accessor");
15252        caixa_with_restart_window(Some("60s"))
15253            .validate_restart_window()
15254            .expect("canonical :restart-window \"60s\" must validate through the accessor");
15255        let err = caixa_with_restart_window(Some("1.5s"))
15256            .validate_restart_window()
15257            .expect_err("fractional-seconds :restart-window must fail through the accessor");
15258        assert!(
15259            matches!(
15260                err,
15261                ManifestError::RestartWindowMalformed { ref restart_window, .. }
15262                    if restart_window == "1.5s"
15263            ),
15264            "validator must carry the offending raw string verbatim \
15265             from the accessor's borrowed &str (got {err:?})",
15266        );
15267    }
15268
15269    #[test]
15270    fn supervisor_view_restart_window_arm_routes_through_accessor() {
15271        // Composition pin: [`Caixa::supervisor_view`]'s
15272        // per-`:restart-window` [`SupervisorSpec`] construction arm
15273        // must key off [`Caixa::restart_window`]'s soft-swallowing
15274        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
15275        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
15276        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
15277        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
15278        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
15279        // (the shared codec's canonical parse); (3) codec-rejected
15280        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
15281        // (the soft-swallow preserving the view's best-effort shape).
15282        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
15283        let view = c.supervisor_view().expect("Supervisor kind has a view");
15284        assert_eq!(
15285            view.restart_window(),
15286            None,
15287            "supervisor_view must project outer None :restart-window \
15288             onto None on the composed SupervisorSpec (never-reset \
15289             sentinel) through the accessor's None-return unchanged",
15290        );
15291
15292        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
15293        let view = c.supervisor_view().expect("Supervisor kind has a view");
15294        assert_eq!(
15295            view.restart_window(),
15296            Some(std::time::Duration::from_secs(60)),
15297            "supervisor_view must fold outer Some(\"60s\") through the \
15298             shared duration_codec into Duration::from_secs(60) on the \
15299             composed SupervisorSpec (accessor's Some(&str) → codec \
15300             parse → Some(Duration))",
15301        );
15302
15303        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
15304        let view = c.supervisor_view().expect("Supervisor kind has a view");
15305        assert_eq!(
15306            view.restart_window(),
15307            None,
15308            "supervisor_view must soft-swallow the shared-codec parse \
15309             failure to None (the view's best-effort shape the sibling \
15310             manifest-level validate_restart_window surfaces as \
15311             RestartWindowMalformed); the accessor's raw-string return \
15312             is the single input every downstream consumer keys off",
15313        );
15314    }
15315
15316    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
15317
15318    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
15319        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15320        c.upgrade_from = upgrade_from;
15321        c
15322    }
15323
15324    #[test]
15325    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
15326        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
15327        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
15328        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
15329        // typed `Vec<UpgradeFromEntry>` verbatim as a
15330        // `&[UpgradeFromEntry]` slice-view over the same backing
15331        // buffer the raw `self.upgrade_from.as_slice()` field access
15332        // borrows from, element-equal across every representative
15333        // fixture in the accept-set — `[]` (the "no hot-upgrade path
15334        // declared" arm every `defcaixa` without an `:upgrade-from`
15335        // block carries; `#[serde(default)]` folds an omitted slot
15336        // onto `Vec::new()`), a canonical single-entry `Restart`
15337        // fixture (the shape most Servicos carry — a single prior
15338        // version with the fallback strategy), a canonical multi-
15339        // entry list carrying every typed instruction variant
15340        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
15341        // `Restart`), and a past-the-guard sentinel — a duplicate-
15342        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
15343        // ([`crate::upgrade::validate_upgrade_from`] rejects through
15344        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
15345        // ship the raw slot verbatim so struct-literal fixtures
15346        // continue to expose the duplicate at the accessor boundary).
15347        //
15348        // Pins against a future silent detour that returned an owned
15349        // `Vec<UpgradeFromEntry>` (which would type-check but silently
15350        // clone on every accessor call, breaking the zero-cost
15351        // projection every peer sibling slice accessor carries), a
15352        // `[dup, dup] → [dup]` dedup collapse (which would silently
15353        // absorb the `DuplicateFrom` refusal case at the accessor
15354        // boundary and the [`crate::StandardLayout::verify`] cross-
15355        // entry gate would silently accept a struct-literal `Caixa`
15356        // carrying the drift), a reference to an operator-resolved
15357        // overlay (the future per-cluster `:upgrade-overrides` slot
15358        // — its resolution must land at exactly this accessor body,
15359        // not silently divert the raw slot away from a second
15360        // consumer), or an axis-shuffled projection (a future detour
15361        // that reordered entries through the accessor would silently
15362        // split the paired [`crate::StandardLayout::verify`] per-
15363        // `:upgrade-from` shape gate's traversal input from the peer
15364        // [`crate::render::servico_m2_overlay`] emitter's projection
15365        // input, since the operator's hot-upgrade dispatch matches
15366        // per-`:from` and axis reordering would silently split the
15367        // per-entry script-path existence probe's iteration order
15368        // from the M2 overlay emitter's serialized-entry order).
15369        //
15370        // First outer top-level [`Caixa`] `&[Composite]`-return
15371        // slice accessor pin on the substrate primitive for M2 / M3
15372        // typed-slot vec-carry axes — opens the outer-`Caixa`
15373        // `&[Composite]` composite-slice projection pattern the
15374        // sibling `:children` [`crate::supervisor::ChildSpec`] /
15375        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
15376        // [`crate::aplicacao::WitContract`] future outer-composite-
15377        // slice pins fold on. Peer of the closed outer-`Caixa`
15378        // scalar `Option<&Composite>` composite-reference family the
15379        // sibling `limits` / `behavior` / `politicas` / `placement`
15380        // / `entrada` `..._returns_..._option_ref_verbatim_across_
15381        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
15382        // the "byte-equal, borrow-shared" outer-accessor discipline
15383        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
15384        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15385        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
15386            vec![],
15387            vec![UpgradeFromEntry {
15388                from: "0.0.1".into(),
15389                instructions: vec![UpgradeInstruction::Restart],
15390            }],
15391            vec![
15392                UpgradeFromEntry {
15393                    from: "0.0.1".into(),
15394                    instructions: vec![
15395                        UpgradeInstruction::LoadModule {
15396                            module: "demo".into(),
15397                        },
15398                        UpgradeInstruction::SoftPurge {
15399                            module: "demo".into(),
15400                        },
15401                    ],
15402                },
15403                UpgradeFromEntry {
15404                    from: "0.0.2".into(),
15405                    instructions: vec![
15406                        UpgradeInstruction::StateChange {
15407                            script: "servicos/upgrade.lisp".into(),
15408                        },
15409                        UpgradeInstruction::Purge {
15410                            module: "demo".into(),
15411                        },
15412                        UpgradeInstruction::Restart,
15413                    ],
15414                },
15415            ],
15416            vec![
15417                UpgradeFromEntry {
15418                    from: "0.1.0".into(),
15419                    instructions: vec![UpgradeInstruction::Restart],
15420                },
15421                UpgradeFromEntry {
15422                    from: "0.1.0".into(),
15423                    instructions: vec![UpgradeInstruction::Restart],
15424                },
15425            ],
15426        ];
15427        for upgrade_from in fixtures {
15428            let c = caixa_with_upgrade_from(upgrade_from.clone());
15429            assert_eq!(
15430                c.upgrade_from(),
15431                upgrade_from.as_slice(),
15432                "Caixa::upgrade_from must return :upgrade-from \
15433                 verbatim (got {:?}, expected {upgrade_from:?})",
15434                c.upgrade_from(),
15435            );
15436            assert_eq!(
15437                c.upgrade_from(),
15438                c.upgrade_from.as_slice(),
15439                "Caixa::upgrade_from must element-equal the raw \
15440                 `self.upgrade_from.as_slice()` field access across \
15441                 every value in the Vec<UpgradeFromEntry> accept-set",
15442            );
15443            assert_eq!(
15444                c.upgrade_from().is_empty(),
15445                c.upgrade_from.is_empty(),
15446                "Caixa::upgrade_from().is_empty() must byte-equal \
15447                 self.upgrade_from.is_empty() — a presence-bit drift \
15448                 would silently split the paired \
15449                 Caixa::declared_servico_slots M2 declared-slot \
15450                 enumerator's presence probe from the peer \
15451                 crate::render::servico_m2_overlay M2 overlay \
15452                 emitter's presence gate",
15453            );
15454        }
15455    }
15456
15457    #[test]
15458    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
15459        // Composition pin: [`Caixa::declared_servico_slots`]'s
15460        // `:upgrade-from` presence-probe arm must key off
15461        // [`Caixa::upgrade_from`], not the raw
15462        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
15463        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
15464        // instructions: vec![Restart] }], .. }` must push
15465        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
15466        // (the presence bit is non-empty, so the M2 kind-coherence
15467        // gate must surface the slot as "declared"), and a `Caixa {
15468        // upgrade_from: vec![], .. }` must NOT push the label (the
15469        // "author omitted the slot entirely" arm — the empty-slice
15470        // partition the serde-default folds onto). The pair jointly
15471        // pins the accessor + declared-slot enumerator composition:
15472        // any future silent detour that had the accessor collapse
15473        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
15474        // is_empty())` projection) would silently absorb the
15475        // "declared but degenerate" arm at the accessor boundary and
15476        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
15477        // coherence gate would silently accept a struct-literal
15478        // `Caixa` carrying the drift.
15479        //
15480        // Peer of the sibling
15481        // `declared_servico_slots_limits_arm_routes_through_accessor`
15482        // (b2bd9d7) and
15483        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15484        // (35d8b52) composition pins on the sibling `:limits` /
15485        // `:behavior` outer-`Option<&Composite>` arms — same "the
15486        // enumerator gate must route through the substrate-primitive
15487        // typed dispatch" discipline extended onto the third M2
15488        // Servico-runtime slot axis, closing the enumerator's routing
15489        // invariant on every M2 arm.
15490        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15491        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
15492            from: "0.0.1".into(),
15493            instructions: vec![UpgradeInstruction::Restart],
15494        }]);
15495        let slots = c.declared_servico_slots();
15496        assert!(
15497            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
15498            "declared_servico_slots must push \
15499             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
15500             non-empty — the accessor and the enumerator gate must \
15501             route through the same substrate-primitive typed \
15502             dispatch on the outer :upgrade-from presence bit (got \
15503             slots={slots:?})",
15504        );
15505        let c = caixa_with_upgrade_from(vec![]);
15506        let slots = c.declared_servico_slots();
15507        assert!(
15508            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
15509            "declared_servico_slots must NOT push \
15510             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
15511             empty — the author-omitted arm must route through the \
15512             accessor's empty-slice return unchanged (got \
15513             slots={slots:?})",
15514        );
15515    }
15516
15517    #[test]
15518    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
15519        // Composition pin: [`crate::render::servico_m2_overlay`]'s
15520        // per-`:upgrade-from` M2 overlay emit arm must key off
15521        // [`Caixa::upgrade_from`], not the raw
15522        // `!caixa.upgrade_from.is_empty()` presence gate + the
15523        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
15524        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
15525        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
15526        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
15527        // sequence in the overlay (the emitter fans onto the serde
15528        // slice-serialization), and a `Caixa { upgrade_from: vec![],
15529        // .. }` must omit the key entirely (the empty-slice
15530        // partition — the `!.is_empty()` outer gate elides the key
15531        // when the author omitted the slot). The pair jointly pins
15532        // the accessor + M2 overlay emitter composition: any future
15533        // silent detour that had the accessor return a fresh-cloned
15534        // `Vec<UpgradeFromEntry>` copy would silently break the
15535        // reference-identity pin the peer per-entry
15536        // `serde_yaml::to_value(caixa.upgrade_from())` projection
15537        // reads from — the projection would clone once per accessor
15538        // call instead of borrowing the storage buffer verbatim.
15539        //
15540        // Peer of the sibling
15541        // `servico_m2_overlay_limits_arm_routes_through_accessor`
15542        // (b2bd9d7) and
15543        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
15544        // (35d8b52) composition pins on the sibling `:limits` /
15545        // `:behavior` outer-`Option<&Composite>` arms — same "the
15546        // M2 overlay emitter must route through the substrate-
15547        // primitive typed dispatch" discipline extended onto the
15548        // third M2 Servico-runtime slot axis, closing the overlay
15549        // emitter's routing invariant on every M2 arm.
15550        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
15551        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15552        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
15553            from: "0.0.1".into(),
15554            instructions: vec![UpgradeInstruction::Restart],
15555        }]);
15556        let overlay = servico_m2_overlay(&c).unwrap();
15557        assert!(
15558            overlay.contains_key(M2_KEY_UPGRADE_FROM),
15559            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
15560             `:upgrade-from` is non-empty — the accessor and the M2 \
15561             overlay emitter must route through the same substrate- \
15562             primitive typed dispatch on the outer :upgrade-from \
15563             slice (got overlay={overlay:?})",
15564        );
15565        let c = caixa_with_upgrade_from(vec![]);
15566        let overlay = servico_m2_overlay(&c).unwrap();
15567        assert!(
15568            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
15569            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
15570             `:upgrade-from` is empty — the empty-slice partition \
15571             must route through the accessor's empty-slice return \
15572             unchanged (got overlay={overlay:?})",
15573        );
15574    }
15575
15576    #[test]
15577    fn upgrade_from_projects_slice_by_borrow() {
15578        // The by-borrow pin: [`Caixa::upgrade_from`] returns
15579        // `&[UpgradeFromEntry]` by borrow — the returned slice
15580        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
15581        // the `:upgrade-from` slot and the accessor must not clone
15582        // the backing `Vec` on every call. Peer of the sibling
15583        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
15584        // (`autores_projects_slice_by_borrow` b5d813f,
15585        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
15586        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15587        // `exe_projects_slice_by_borrow` 65d9527,
15588        // `servicos_projects_slice_by_borrow` 611f78b,
15589        // `deps_projects_slice_by_borrow` ad34b4e,
15590        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
15591        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
15592        // axes — extended here to the first outer-`Caixa`
15593        // composite-element `&[Composite]` axis: the accessor's
15594        // returned slice must borrow from `&self` (the returned
15595        // reference's lifetime is tied to `&self`), and calling the
15596        // accessor twice on the same [`Caixa`] must yield slices
15597        // that are pointer-equal (the underlying byte-buffer is the
15598        // storage `Vec`'s allocation, not a fresh copy) as well as
15599        // value-equal (idempotent, no side effects on `&self`).
15600        //
15601        // Pins against a future silent detour that returned an owned
15602        // `Vec<UpgradeFromEntry>` (which would type-check but
15603        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
15604        // return (which would leak the backing `Vec`'s
15605        // grow/push/reserve surface no downstream consumer reaches
15606        // for), or a one-arm-only accessor that returned a
15607        // saturating value on some sentinel input.
15608        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15609        for upgrade_from in [
15610            vec![],
15611            vec![UpgradeFromEntry {
15612                from: "0.0.1".into(),
15613                instructions: vec![UpgradeInstruction::Restart],
15614            }],
15615            vec![
15616                UpgradeFromEntry {
15617                    from: "0.0.1".into(),
15618                    instructions: vec![UpgradeInstruction::Restart],
15619                },
15620                UpgradeFromEntry {
15621                    from: "0.0.2".into(),
15622                    instructions: vec![UpgradeInstruction::SoftPurge {
15623                        module: "demo".into(),
15624                    }],
15625                },
15626            ],
15627        ] {
15628            let c = caixa_with_upgrade_from(upgrade_from.clone());
15629            let first = c.upgrade_from();
15630            let second = c.upgrade_from();
15631            assert_eq!(
15632                first, second,
15633                "Caixa::upgrade_from must be idempotent — two \
15634                 successive calls on the same &self must return the \
15635                 same &[UpgradeFromEntry]",
15636            );
15637            assert_eq!(
15638                first.as_ptr(),
15639                second.as_ptr(),
15640                "Caixa::upgrade_from must borrow the underlying \
15641                 Vec<UpgradeFromEntry> storage — two successive calls \
15642                 must return slices with the same backing pointer (a \
15643                 fresh Vec<UpgradeFromEntry> clone would change the \
15644                 pointer on every call)",
15645            );
15646            assert_eq!(
15647                first,
15648                upgrade_from.as_slice(),
15649                "Caixa::upgrade_from must return :upgrade-from \
15650                 verbatim by borrow — got {first:?}, expected \
15651                 {upgrade_from:?}",
15652            );
15653        }
15654    }
15655
15656    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
15657
15658    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
15659        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15660        c.children = children;
15661        c
15662    }
15663
15664    #[test]
15665    fn children_returns_children_slice_verbatim_across_permutations() {
15666        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
15667        // outer-composite `&[ChildSpec]`-return slice-shape pin:
15668        // [`Caixa::children`] must return the `:children` typed
15669        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
15670        // the same backing buffer the raw `self.children.as_slice()`
15671        // field access borrows from, element-equal across every
15672        // representative fixture in the accept-set — `[]` (the "no
15673        // static children declared" arm every non-`Supervisor`-kind
15674        // `defcaixa` carries by `#[serde(default)]` and every
15675        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
15676        // a canonical single-child `Permanent` fixture (the shape
15677        // most `OneForOne` supervisors carry — a single long-running
15678        // worker child), a canonical multi-child list carrying every
15679        // typed restart-policy variant (`Permanent` / `Transient` /
15680        // `Temporary`), and a past-the-guard sentinel — a duplicate
15681        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
15682        // ([`crate::SupervisorSpec::validate`] rejects through
15683        // `DuplicateChildNome { nome: "w" }` but the accessor must
15684        // ship the raw slot verbatim so struct-literal fixtures
15685        // continue to expose the duplicate at the accessor boundary).
15686        //
15687        // Pins against a future silent detour that returned an owned
15688        // `Vec<ChildSpec>` (which would type-check but silently clone
15689        // on every accessor call, breaking the zero-cost projection
15690        // every peer sibling slice accessor carries), a `[dup, dup] →
15691        // [dup]` dedup collapse (which would silently absorb the
15692        // `DuplicateChildNome` refusal case at the accessor boundary
15693        // and the [`crate::StandardLayout::verify`] cross-child gate
15694        // would silently accept a struct-literal `Caixa` carrying the
15695        // drift), a reference to an operator-resolved overlay (the
15696        // future per-cluster `:children-overrides` slot — its
15697        // resolution must land at exactly this accessor body, not
15698        // silently divert the raw slot away from a second consumer),
15699        // or an axis-shuffled projection (a future detour that
15700        // reordered children through the accessor would silently
15701        // split the paired [`crate::StandardLayout::verify`] per-
15702        // supervisor gate's traversal input from the peer
15703        // [`Self::supervisor_view`] fold-in path's clone-order input,
15704        // since the OTP `RestForOne` restart strategy dispatches on
15705        // declared child order and axis reordering would silently
15706        // split the operator's per-cluster restart-fan-out order
15707        // from the caixa.lisp source-order).
15708        //
15709        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
15710        // accessor pin on the substrate primitive for M2 / M3 typed-
15711        // slot vec-carry axes — folds on the outer-`Caixa`
15712        // `&[Composite]` composite-slice sub-family the sibling
15713        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
15714        // (2a1f907) pin opened, peer at the outer altitude of the
15715        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
15716        // (bc92bce) accessor on the same OTP-supervisor static-child-
15717        // list axis.
15718        use crate::supervisor::{ChildSpec, RestartPolicy};
15719        let fixtures: Vec<Vec<ChildSpec>> = vec![
15720            vec![],
15721            vec![ChildSpec {
15722                caixa: "worker".into(),
15723                versao: "^0.1".into(),
15724                restart: RestartPolicy::Permanent,
15725            }],
15726            vec![
15727                ChildSpec {
15728                    caixa: "worker-a".into(),
15729                    versao: "^0.1".into(),
15730                    restart: RestartPolicy::Permanent,
15731                },
15732                ChildSpec {
15733                    caixa: "worker-b".into(),
15734                    versao: "^0.1".into(),
15735                    restart: RestartPolicy::Transient,
15736                },
15737                ChildSpec {
15738                    caixa: "worker-c".into(),
15739                    versao: "^0.1".into(),
15740                    restart: RestartPolicy::Temporary,
15741                },
15742            ],
15743            vec![
15744                ChildSpec {
15745                    caixa: "w".into(),
15746                    versao: "^0.1".into(),
15747                    restart: RestartPolicy::Permanent,
15748                },
15749                ChildSpec {
15750                    caixa: "w".into(),
15751                    versao: "^0.1".into(),
15752                    restart: RestartPolicy::Permanent,
15753                },
15754            ],
15755        ];
15756        for children in fixtures {
15757            let c = caixa_with_children(children.clone());
15758            assert_eq!(
15759                c.children(),
15760                children.as_slice(),
15761                "Caixa::children must return :children verbatim \
15762                 (got {:?}, expected {children:?})",
15763                c.children(),
15764            );
15765            assert_eq!(
15766                c.children(),
15767                c.children.as_slice(),
15768                "Caixa::children must element-equal the raw \
15769                 `self.children.as_slice()` field access across \
15770                 every value in the Vec<ChildSpec> accept-set",
15771            );
15772            assert_eq!(
15773                c.children().is_empty(),
15774                c.children.is_empty(),
15775                "Caixa::children().is_empty() must byte-equal \
15776                 self.children.is_empty() — a presence-bit drift \
15777                 would silently split the paired \
15778                 Caixa::declared_supervisor_slots supervisor-tree \
15779                 declared-slot enumerator's presence probe from the \
15780                 peer Caixa::supervisor_view typed-view composer's \
15781                 fold-in path",
15782            );
15783        }
15784    }
15785
15786    #[test]
15787    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
15788        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15789        // `:children` presence-probe arm must key off
15790        // [`Caixa::children`], not the raw
15791        // `!self.children.is_empty()` field-probe. Structurally: a
15792        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
15793        // "^0.1", restart: Permanent }], .. }` must push
15794        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
15795        // (the presence bit is non-empty, so the supervisor-tree
15796        // kind-coherence gate must surface the slot as "declared"),
15797        // and a `Caixa { children: vec![], .. }` must NOT push the
15798        // label (the "author omitted the slot entirely" arm — the
15799        // empty-slice partition the serde-default folds onto). The
15800        // pair jointly pins the accessor + declared-slot enumerator
15801        // composition: any future silent detour that had the accessor
15802        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
15803        // "__reserved__")` projection) would silently absorb the
15804        // "declared but degenerate" arm at the accessor boundary and
15805        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
15806        // kind-coherence gate would silently accept a struct-literal
15807        // `Caixa` carrying the drift.
15808        //
15809        // Peer of the sibling
15810        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
15811        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
15812        // same "the enumerator gate must route through the substrate-
15813        // primitive typed dispatch" discipline extended onto the
15814        // supervisor-tree `:children` composite-slice arm.
15815        use crate::supervisor::{ChildSpec, RestartPolicy};
15816        let c = caixa_with_children(vec![ChildSpec {
15817            caixa: "w".into(),
15818            versao: "^0.1".into(),
15819            restart: RestartPolicy::Permanent,
15820        }]);
15821        let slots = c.declared_supervisor_slots();
15822        assert!(
15823            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
15824            "declared_supervisor_slots must push \
15825             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
15826             non-empty — the accessor and the enumerator gate must \
15827             route through the same substrate-primitive typed \
15828             dispatch on the outer :children presence bit (got \
15829             slots={slots:?})",
15830        );
15831        let c = caixa_with_children(vec![]);
15832        let slots = c.declared_supervisor_slots();
15833        assert!(
15834            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
15835            "declared_supervisor_slots must NOT push \
15836             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
15837             empty — the author-omitted arm must route through the \
15838             accessor's empty-slice return unchanged (got \
15839             slots={slots:?})",
15840        );
15841    }
15842
15843    #[test]
15844    fn supervisor_view_children_arm_routes_through_accessor() {
15845        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
15846        // fold-in arm must key off [`Caixa::children`], not the raw
15847        // `self.children.clone()` field-clone. Structurally: a `Caixa {
15848        // kind: Supervisor, estrategia: Some(OneForOne), children:
15849        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
15850        // per-child list through the accessor into the typed
15851        // [`SupervisorSpec`] view's `children` field verbatim — every
15852        // entry the accessor surfaces must land in the view's
15853        // `children` slot in the same order. The pair jointly pins the
15854        // accessor + view-composer composition: any future silent
15855        // detour that had the accessor return a fresh-cloned
15856        // `Vec<ChildSpec>` copy would silently break the reference-
15857        // identity pin the peer `supervisor_view` fold-in path reads
15858        // from — the fold would clone once more per accessor call
15859        // instead of borrowing the storage buffer verbatim once.
15860        //
15861        // Peer of the sibling
15862        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
15863        // family) composition pin on the peer kind-gate arm — same
15864        // "the view composer must route through the substrate-
15865        // primitive typed dispatch" discipline extended onto the
15866        // per-`:children` fold-in arm, closing the supervisor-view
15867        // composer's routing invariant on the composite-slice input.
15868        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
15869        let mut c = caixa_with_children(vec![
15870            ChildSpec {
15871                caixa: "worker-a".into(),
15872                versao: "^0.1".into(),
15873                restart: RestartPolicy::Permanent,
15874            },
15875            ChildSpec {
15876                caixa: "worker-b".into(),
15877                versao: "^0.1".into(),
15878                restart: RestartPolicy::Transient,
15879            },
15880        ]);
15881        c.kind = crate::CaixaKind::Supervisor;
15882        c.estrategia = Some(RestartStrategy::OneForOne);
15883        let view = c
15884            .supervisor_view()
15885            .expect("Supervisor kind must produce a supervisor_view");
15886        assert_eq!(
15887            view.children(),
15888            c.children(),
15889            "supervisor_view must fold Caixa::children verbatim into \
15890             SupervisorSpec::children — the accessor and the view \
15891             composer must route through the same substrate-primitive \
15892             typed dispatch on the outer :children slice (got view \
15893             children={:?}, expected {:?})",
15894            view.children(),
15895            c.children(),
15896        );
15897    }
15898
15899    #[test]
15900    fn children_projects_slice_by_borrow() {
15901        // The by-borrow pin: [`Caixa::children`] returns
15902        // `&[ChildSpec]` by borrow — the returned slice borrows the
15903        // underlying `Vec<ChildSpec>` storage of the `:children` slot
15904        // and the accessor must not clone the backing `Vec` on every
15905        // call. Peer of the sibling outer top-level [`Caixa`]
15906        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
15907        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
15908        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15909        // `exe_projects_slice_by_borrow` 65d9527,
15910        // `servicos_projects_slice_by_borrow` 611f78b,
15911        // `deps_projects_slice_by_borrow` ad34b4e,
15912        // `deps_dev_projects_slice_by_borrow` f7fd81e,
15913        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
15914        // sibling outer top-level [`Caixa`] scalar-element and
15915        // composite-element `&[T]` axes — folds on the outer-`Caixa`
15916        // composite-element `&[Composite]` axis: the accessor's
15917        // returned slice must borrow from `&self` (the returned
15918        // reference's lifetime is tied to `&self`), and calling the
15919        // accessor twice on the same [`Caixa`] must yield slices
15920        // that are pointer-equal (the underlying byte-buffer is the
15921        // storage `Vec`'s allocation, not a fresh copy) as well as
15922        // value-equal (idempotent, no side effects on `&self`).
15923        //
15924        // Pins against a future silent detour that returned an owned
15925        // `Vec<ChildSpec>` (which would type-check but silently clone
15926        // on every call), a `&Vec<ChildSpec>` return (which would leak
15927        // the backing `Vec`'s grow/push/reserve surface no downstream
15928        // consumer reaches for), or a one-arm-only accessor that
15929        // returned a saturating value on some sentinel input.
15930        use crate::supervisor::{ChildSpec, RestartPolicy};
15931        for children in [
15932            vec![],
15933            vec![ChildSpec {
15934                caixa: "w".into(),
15935                versao: "^0.1".into(),
15936                restart: RestartPolicy::Permanent,
15937            }],
15938            vec![
15939                ChildSpec {
15940                    caixa: "worker-a".into(),
15941                    versao: "^0.1".into(),
15942                    restart: RestartPolicy::Permanent,
15943                },
15944                ChildSpec {
15945                    caixa: "worker-b".into(),
15946                    versao: "^0.1".into(),
15947                    restart: RestartPolicy::Transient,
15948                },
15949            ],
15950        ] {
15951            let c = caixa_with_children(children.clone());
15952            let first = c.children();
15953            let second = c.children();
15954            assert_eq!(
15955                first, second,
15956                "Caixa::children must be idempotent — two successive \
15957                 calls on the same &self must return the same \
15958                 &[ChildSpec]",
15959            );
15960            assert_eq!(
15961                first.as_ptr(),
15962                second.as_ptr(),
15963                "Caixa::children must borrow the underlying \
15964                 Vec<ChildSpec> storage — two successive calls must \
15965                 return slices with the same backing pointer (a fresh \
15966                 Vec<ChildSpec> clone would change the pointer on \
15967                 every call)",
15968            );
15969            assert_eq!(
15970                first,
15971                children.as_slice(),
15972                "Caixa::children must return :children verbatim by \
15973                 borrow — got {first:?}, expected {children:?}",
15974            );
15975        }
15976    }
15977
15978    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
15979
15980    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
15981        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15982        c.kind = CaixaKind::Aplicacao;
15983        c.membros = membros;
15984        c
15985    }
15986
15987    #[test]
15988    fn membros_returns_membros_slice_verbatim_across_permutations() {
15989        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
15990        // composite `&[Membro]`-return slice-shape pin:
15991        // [`Caixa::membros`] must return the `:membros` typed
15992        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
15993        // same backing buffer the raw `self.membros.as_slice()` field
15994        // access borrows from, element-equal across every
15995        // representative fixture in the accept-set — `[]` (the "no
15996        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
15997        // carries by `#[serde(default)]` and every partially-authored
15998        // Aplicacao carries before the
15999        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
16000        // canonical single-member fixture (the shape a minimal
16001        // Aplicacao carries — one Servico wrapping one contained
16002        // computation), a canonical multi-member list carrying three
16003        // distinct entries (the canonical checkout-shape Aplicacao —
16004        // cart / pricing / auth — every canonical example carries), and
16005        // a past-the-guard sentinel — a duplicate `:caixa`
16006        // `[("cart", ...), ("cart", ...)]` entry pair
16007        // ([`crate::AplicacaoSpec::validate`] rejects through
16008        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
16009        // the raw slot verbatim so struct-literal fixtures continue to
16010        // expose the duplicate at the accessor boundary).
16011        //
16012        // Pins against a future silent detour that returned an owned
16013        // `Vec<Membro>` (which would type-check but silently clone on
16014        // every accessor call, breaking the zero-cost projection every
16015        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
16016        // dedup collapse (which would silently absorb the
16017        // `DuplicateMembro` refusal case at the accessor boundary and
16018        // the [`crate::StandardLayout::verify`] cross-member gate would
16019        // silently accept a struct-literal `Caixa` carrying the drift),
16020        // a reference to an operator-resolved overlay (the future per-
16021        // cluster `:membros-overrides` slot — its resolution must land
16022        // at exactly this accessor body, not silently divert the raw
16023        // slot away from a second consumer), or an axis-shuffled
16024        // projection (a future detour that reordered members through
16025        // the accessor would silently split the paired
16026        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
16027        // traversal input from the peer [`Self::aplicacao_view`] fold-
16028        // in path's clone-order input, since the canonical `:contratos`
16029        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
16030        // read the member set through the same slice).
16031        //
16032        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
16033        // accessor pin on the substrate primitive for M2 / M3 typed-
16034        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
16035        // arm of the `&[Composite]` composite-slice sub-family the
16036        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16037        // (2a1f907) and
16038        // `children_returns_children_slice_verbatim_across_permutations`
16039        // (c17b51e) pins opened, peer at the outer altitude of the
16040        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
16041        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
16042        // list axis.
16043        use crate::aplicacao::Membro;
16044        let fixtures: Vec<Vec<Membro>> = vec![
16045            vec![],
16046            vec![Membro {
16047                caixa: "cart".into(),
16048                versao: "^0.1".into(),
16049            }],
16050            vec![
16051                Membro {
16052                    caixa: "cart".into(),
16053                    versao: "^0.1".into(),
16054                },
16055                Membro {
16056                    caixa: "pricing".into(),
16057                    versao: "^0.2".into(),
16058                },
16059                Membro {
16060                    caixa: "auth".into(),
16061                    versao: "^1.0".into(),
16062                },
16063            ],
16064            vec![
16065                Membro {
16066                    caixa: "cart".into(),
16067                    versao: "^0.1".into(),
16068                },
16069                Membro {
16070                    caixa: "cart".into(),
16071                    versao: "^0.1".into(),
16072                },
16073            ],
16074        ];
16075        for membros in fixtures {
16076            let c = caixa_aplicacao_with_membros(membros.clone());
16077            assert_eq!(
16078                c.membros(),
16079                membros.as_slice(),
16080                "Caixa::membros must return :membros verbatim \
16081                 (got {:?}, expected {membros:?})",
16082                c.membros(),
16083            );
16084            assert_eq!(
16085                c.membros(),
16086                c.membros.as_slice(),
16087                "Caixa::membros must element-equal the raw \
16088                 `self.membros.as_slice()` field access across every \
16089                 value in the Vec<Membro> accept-set",
16090            );
16091            assert_eq!(
16092                c.membros().is_empty(),
16093                c.membros.is_empty(),
16094                "Caixa::membros().is_empty() must byte-equal \
16095                 self.membros.is_empty() — a presence-bit drift would \
16096                 silently split the paired Caixa::declared_mesh_slots \
16097                 mesh declared-slot enumerator's presence probe from \
16098                 the peer Caixa::aplicacao_view typed-view composer's \
16099                 fold-in path",
16100            );
16101        }
16102    }
16103
16104    #[test]
16105    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
16106        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
16107        // presence-probe arm must key off [`Caixa::membros`], not the
16108        // raw `!self.membros.is_empty()` field-probe. Structurally: a
16109        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
16110        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
16111        // declared-slot list (the presence bit is non-empty, so the
16112        // mesh kind-coherence gate must surface the slot as
16113        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
16114        // push the label (the "author omitted the slot entirely" arm
16115        // — the empty-slice partition the serde-default folds onto).
16116        // The pair jointly pins the accessor + declared-slot
16117        // enumerator composition: any future silent detour that had
16118        // the accessor collapse `[Membro { .. }]` to `[]` (a
16119        // `.filter(|m| m.nome() != "__reserved__")` projection) would
16120        // silently absorb the "declared but degenerate" arm at the
16121        // accessor boundary and the
16122        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16123        // coherence gate would silently accept a struct-literal
16124        // `Caixa` carrying the drift.
16125        //
16126        // Peer of the sibling
16127        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16128        // (2a1f907) and
16129        // `declared_supervisor_slots_children_arm_routes_through_accessor`
16130        // (c17b51e) composition pins on the M2 `:upgrade-from` /
16131        // `:children` composite-slice arms — same "the enumerator gate
16132        // must route through the substrate-primitive typed dispatch"
16133        // discipline extended onto the M3 `:membros` composite-slice
16134        // arm, opening the M3 arm of the declared-slot enumerator's
16135        // routing invariant.
16136        use crate::aplicacao::Membro;
16137        let c = caixa_aplicacao_with_membros(vec![Membro {
16138            caixa: "cart".into(),
16139            versao: "^0.1".into(),
16140        }]);
16141        let slots = c.declared_mesh_slots();
16142        assert!(
16143            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
16144            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
16145             `:membros` is non-empty — the accessor and the enumerator \
16146             gate must route through the same substrate-primitive \
16147             typed dispatch on the outer :membros presence bit (got \
16148             slots={slots:?})",
16149        );
16150        let c = caixa_aplicacao_with_membros(vec![]);
16151        let slots = c.declared_mesh_slots();
16152        assert!(
16153            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
16154            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
16155             when `:membros` is empty — the author-omitted arm must \
16156             route through the accessor's empty-slice return unchanged \
16157             (got slots={slots:?})",
16158        );
16159    }
16160
16161    #[test]
16162    fn aplicacao_view_membros_arm_routes_through_accessor() {
16163        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
16164        // fold-in arm must key off [`Caixa::membros`], not the raw
16165        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
16166        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
16167        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
16168        // member list through the accessor into the typed
16169        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
16170        // every entry the accessor surfaces must land in the view's
16171        // `membros` slot in the same order. The pair jointly pins the
16172        // accessor + view-composer composition: any future silent
16173        // detour that had the accessor return a fresh-cloned
16174        // `Vec<Membro>` copy would silently break the reference-
16175        // identity pin the peer `aplicacao_view` fold-in path reads
16176        // from — the fold would clone once more per accessor call
16177        // instead of borrowing the storage buffer verbatim once.
16178        //
16179        // Peer of the sibling
16180        // `aplicacao_view_politicas_arm_folds_through_accessor`
16181        // (5d23d29) /
16182        // `aplicacao_view_placement_arm_folds_through_accessor`
16183        // (4fb8074) /
16184        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
16185        // composition pins on the M3 `:politicas` / `:placement` /
16186        // `:entrada` outer-`Option<&Composite>` arms — extended here to
16187        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
16188        // closing the aplicacao-view composer's routing invariant on
16189        // the composite-slice input.
16190        use crate::aplicacao::Membro;
16191        let c = caixa_aplicacao_with_membros(vec![
16192            Membro {
16193                caixa: "cart".into(),
16194                versao: "^0.1".into(),
16195            },
16196            Membro {
16197                caixa: "pricing".into(),
16198                versao: "^0.2".into(),
16199            },
16200        ]);
16201        let view = c
16202            .aplicacao_view()
16203            .expect("Aplicacao kind must produce an aplicacao_view");
16204        assert_eq!(
16205            view.membros(),
16206            c.membros(),
16207            "aplicacao_view must fold Caixa::membros verbatim into \
16208             AplicacaoSpec::membros — the accessor and the view \
16209             composer must route through the same substrate-primitive \
16210             typed dispatch on the outer :membros slice (got view \
16211             membros={:?}, expected {:?})",
16212            view.membros(),
16213            c.membros(),
16214        );
16215    }
16216
16217    #[test]
16218    fn membros_projects_slice_by_borrow() {
16219        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
16220        // borrow — the returned slice borrows the underlying
16221        // `Vec<Membro>` storage of the `:membros` slot and the
16222        // accessor must not clone the backing `Vec` on every call.
16223        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
16224        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
16225        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16226        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16227        // `exe_projects_slice_by_borrow` 65d9527,
16228        // `servicos_projects_slice_by_borrow` 611f78b,
16229        // `deps_projects_slice_by_borrow` ad34b4e,
16230        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16231        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
16232        // `children_projects_slice_by_borrow` c17b51e) on the sibling
16233        // outer top-level [`Caixa`] scalar-element and composite-
16234        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
16235        // slot composite-element `&[Composite]` axis: the accessor's
16236        // returned slice must borrow from `&self` (the returned
16237        // reference's lifetime is tied to `&self`), and calling the
16238        // accessor twice on the same [`Caixa`] must yield slices that
16239        // are pointer-equal (the underlying byte-buffer is the storage
16240        // `Vec`'s allocation, not a fresh copy) as well as value-equal
16241        // (idempotent, no side effects on `&self`).
16242        //
16243        // Pins against a future silent detour that returned an owned
16244        // `Vec<Membro>` (which would type-check but silently clone on
16245        // every call), a `&Vec<Membro>` return (which would leak the
16246        // backing `Vec`'s grow/push/reserve surface no downstream
16247        // consumer reaches for), or a one-arm-only accessor that
16248        // returned a saturating value on some sentinel input.
16249        use crate::aplicacao::Membro;
16250        for membros in [
16251            vec![],
16252            vec![Membro {
16253                caixa: "cart".into(),
16254                versao: "^0.1".into(),
16255            }],
16256            vec![
16257                Membro {
16258                    caixa: "cart".into(),
16259                    versao: "^0.1".into(),
16260                },
16261                Membro {
16262                    caixa: "pricing".into(),
16263                    versao: "^0.2".into(),
16264                },
16265            ],
16266        ] {
16267            let c = caixa_aplicacao_with_membros(membros.clone());
16268            let first = c.membros();
16269            let second = c.membros();
16270            assert_eq!(
16271                first, second,
16272                "Caixa::membros must be idempotent — two successive \
16273                 calls on the same &self must return the same &[Membro]",
16274            );
16275            assert_eq!(
16276                first.as_ptr(),
16277                second.as_ptr(),
16278                "Caixa::membros must borrow the underlying Vec<Membro> \
16279                 storage — two successive calls must return slices with \
16280                 the same backing pointer (a fresh Vec<Membro> clone \
16281                 would change the pointer on every call)",
16282            );
16283            assert_eq!(
16284                first,
16285                membros.as_slice(),
16286                "Caixa::membros must return :membros verbatim by borrow \
16287                 — got {first:?}, expected {membros:?}",
16288            );
16289        }
16290    }
16291
16292    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
16293
16294    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
16295        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16296        c.kind = CaixaKind::Aplicacao;
16297        c.contratos = contratos;
16298        c
16299    }
16300
16301    fn contrato_http_for_test(
16302        de: &str,
16303        para: &str,
16304        endpoint: &str,
16305    ) -> crate::aplicacao::WitContract {
16306        crate::aplicacao::WitContract {
16307            de: de.into(),
16308            para: para.into(),
16309            wit: "wasi:http/proxy".into(),
16310            endpoint: Some(endpoint.into()),
16311            subject: None,
16312            slot: None,
16313        }
16314    }
16315
16316    #[test]
16317    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
16318        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
16319        // composite `&[WitContract]`-return slice-shape pin:
16320        // [`Caixa::contratos`] must return the `:contratos` typed
16321        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
16322        // over the same backing buffer the raw
16323        // `self.contratos.as_slice()` field access borrows from,
16324        // element-equal across every representative fixture in the
16325        // accept-set — `[]` (the "no contracts declared" arm every
16326        // non-`Aplicacao`-kind `defcaixa` carries by
16327        // `#[serde(default)]` and every leaf-Aplicacao with a single
16328        // member carries), a canonical single-edge fixture (the
16329        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
16330        // edge), and a canonical multi-edge fixture with three distinct
16331        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
16332        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
16333        //
16334        // Pins against a future silent detour that returned an owned
16335        // `Vec<WitContract>` (which would type-check but silently clone
16336        // on every accessor call, breaking the zero-cost projection
16337        // every peer sibling slice accessor carries), an axis-shuffled
16338        // projection (a future detour that reordered edges through the
16339        // accessor would silently split the paired
16340        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
16341        // traversal input from the peer [`Self::aplicacao_view`] fold-
16342        // in path's clone-order input, since every canonical
16343        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
16344        // seed dispatch reads the edge set through the same slice),
16345        // or a reference to an operator-resolved overlay (the future
16346        // per-cluster `:contratos-overrides` slot — its resolution
16347        // must land at exactly this accessor body, not silently divert
16348        // the raw slot away from a second consumer).
16349        //
16350        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
16351        // accessor pin on the substrate primitive for M2 / M3 typed-
16352        // slot vec-carry axes — closes the outer-`Caixa`
16353        // `&[Composite]` composite-slice sub-family the sibling M2
16354        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16355        // (2a1f907) and
16356        // `children_returns_children_slice_verbatim_across_permutations`
16357        // (c17b51e) pins opened and the M3
16358        // `membros_returns_membros_slice_verbatim_across_permutations`
16359        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
16360        // slot arm of the composite-slice sub-family. Peer at the outer
16361        // altitude of the closed inner-
16362        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
16363        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
16364        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
16365            vec![],
16366            vec![contrato_http_for_test("cart", "catalog", "/items")],
16367            vec![
16368                contrato_http_for_test("cart", "catalog", "/items"),
16369                contrato_http_for_test("cart", "pricing", "/price"),
16370                contrato_http_for_test("cart", "auth", "/whoami"),
16371            ],
16372        ];
16373        for contratos in fixtures {
16374            let c = caixa_aplicacao_with_contratos(contratos.clone());
16375            assert_eq!(
16376                c.contratos(),
16377                contratos.as_slice(),
16378                "Caixa::contratos must return :contratos verbatim \
16379                 (got {:?}, expected {contratos:?})",
16380                c.contratos(),
16381            );
16382            assert_eq!(
16383                c.contratos(),
16384                c.contratos.as_slice(),
16385                "Caixa::contratos must element-equal the raw \
16386                 `self.contratos.as_slice()` field access across every \
16387                 value in the Vec<WitContract> accept-set",
16388            );
16389            assert_eq!(
16390                c.contratos().is_empty(),
16391                c.contratos.is_empty(),
16392                "Caixa::contratos().is_empty() must byte-equal \
16393                 self.contratos.is_empty() — a presence-bit drift would \
16394                 silently split the paired Caixa::declared_mesh_slots \
16395                 mesh declared-slot enumerator's presence probe from \
16396                 the peer Caixa::aplicacao_view typed-view composer's \
16397                 fold-in path",
16398            );
16399        }
16400    }
16401
16402    #[test]
16403    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
16404        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
16405        // presence-probe arm must key off [`Caixa::contratos`], not the
16406        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
16407        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
16408        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
16409        // presence bit is non-empty, so the mesh kind-coherence gate
16410        // must surface the slot as "declared"), and a `Caixa {
16411        // contratos: vec![], .. }` must NOT push the label (the "author
16412        // omitted the slot entirely" arm — the empty-slice partition
16413        // the serde-default folds onto). The pair jointly pins the
16414        // accessor + declared-slot enumerator composition: any future
16415        // silent detour that had the accessor collapse
16416        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
16417        // "__reserved__")` projection) would silently absorb the
16418        // "declared but degenerate" arm at the accessor boundary and
16419        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16420        // coherence gate would silently accept a struct-literal
16421        // `Caixa` carrying the drift.
16422        //
16423        // Peer of the sibling
16424        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16425        // (2a1f907),
16426        // `declared_supervisor_slots_children_arm_routes_through_accessor`
16427        // (c17b51e), and
16428        // `declared_mesh_slots_membros_arm_routes_through_accessor`
16429        // (0f26987) composition pins on the M2 `:upgrade-from` /
16430        // `:children` / M3 `:membros` composite-slice arms — same "the
16431        // enumerator gate must route through the substrate-primitive
16432        // typed dispatch" discipline extended onto the M3 `:contratos`
16433        // composite-slice arm, closing the M3 mesh-slot arm of the
16434        // declared-slot enumerator's routing invariant on the
16435        // composite-slice inputs.
16436        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
16437            "cart", "catalog", "/items",
16438        )]);
16439        let slots = c.declared_mesh_slots();
16440        assert!(
16441            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
16442            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
16443             `:contratos` is non-empty — the accessor and the enumerator \
16444             gate must route through the same substrate-primitive \
16445             typed dispatch on the outer :contratos presence bit (got \
16446             slots={slots:?})",
16447        );
16448        let c = caixa_aplicacao_with_contratos(vec![]);
16449        let slots = c.declared_mesh_slots();
16450        assert!(
16451            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
16452            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
16453             when `:contratos` is empty — the author-omitted arm must \
16454             route through the accessor's empty-slice return unchanged \
16455             (got slots={slots:?})",
16456        );
16457    }
16458
16459    #[test]
16460    fn aplicacao_view_contratos_arm_routes_through_accessor() {
16461        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
16462        // fold-in arm must key off [`Caixa::contratos`], not the raw
16463        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
16464        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
16465        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
16466        // per-edge list through the accessor into the typed
16467        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
16468        // every entry the accessor surfaces must land in the view's
16469        // `contratos` slot in the same order. The pair jointly pins
16470        // the accessor + view-composer composition: a future silent
16471        // detour that had the accessor shuffle or drop an edge would
16472        // silently split the paired declared-slot enumerator's
16473        // presence bit from the typed-view composer's edge-list, a
16474        // two-consumer split at the enumerator and the view composer
16475        // far from the source `caixa.lisp`.
16476        //
16477        // Peer of the sibling
16478        // `aplicacao_view_membros_arm_routes_through_accessor`
16479        // (0f26987) composition pin on the M3 `:membros` outer-
16480        // `&[Composite]` composite-slice arm, closing the aplicacao-
16481        // view composer's routing invariant on the composite-slice
16482        // inputs at the outer altitude.
16483        let c = caixa_aplicacao_with_contratos(vec![
16484            contrato_http_for_test("cart", "catalog", "/items"),
16485            contrato_http_for_test("cart", "pricing", "/price"),
16486        ]);
16487        let view = c
16488            .aplicacao_view()
16489            .expect("Aplicacao kind must produce an aplicacao_view");
16490        assert_eq!(
16491            view.contratos(),
16492            c.contratos(),
16493            "aplicacao_view must fold Caixa::contratos verbatim into \
16494             AplicacaoSpec::contratos — the accessor and the view \
16495             composer must route through the same substrate-primitive \
16496             typed dispatch on the outer :contratos slice (got view \
16497             contratos={:?}, expected {:?})",
16498            view.contratos(),
16499            c.contratos(),
16500        );
16501    }
16502
16503    #[test]
16504    fn contratos_projects_slice_by_borrow() {
16505        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
16506        // by borrow — the returned slice borrows the underlying
16507        // `Vec<WitContract>` storage of the `:contratos` slot and the
16508        // accessor must not clone the backing `Vec` on every call.
16509        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
16510        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
16511        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16512        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16513        // `exe_projects_slice_by_borrow` 65d9527,
16514        // `servicos_projects_slice_by_borrow` 611f78b,
16515        // `deps_projects_slice_by_borrow` ad34b4e,
16516        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16517        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
16518        // `children_projects_slice_by_borrow` c17b51e,
16519        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
16520        // outer top-level [`Caixa`] scalar-element and composite-
16521        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
16522        // composite-element `&[Composite]` axis on the by-borrow pin:
16523        // the accessor's returned slice must borrow from `&self` (the
16524        // returned reference's lifetime is tied to `&self`), and
16525        // calling the accessor twice on the same [`Caixa`] must yield
16526        // slices that are pointer-equal (the underlying byte-buffer is
16527        // the storage `Vec`'s allocation, not a fresh copy) as well as
16528        // value-equal (idempotent, no side effects on `&self`).
16529        //
16530        // Pins against a future silent detour that returned an owned
16531        // `Vec<WitContract>` (which would type-check but silently clone
16532        // on every call), a `&Vec<WitContract>` return (which would
16533        // leak the backing `Vec`'s grow/push/reserve surface no
16534        // downstream consumer reaches for), or a one-arm-only accessor
16535        // that returned a saturating value on some sentinel input.
16536        for contratos in [
16537            vec![],
16538            vec![contrato_http_for_test("cart", "catalog", "/items")],
16539            vec![
16540                contrato_http_for_test("cart", "catalog", "/items"),
16541                contrato_http_for_test("cart", "pricing", "/price"),
16542            ],
16543        ] {
16544            let c = caixa_aplicacao_with_contratos(contratos.clone());
16545            let first = c.contratos();
16546            let second = c.contratos();
16547            assert_eq!(
16548                first, second,
16549                "Caixa::contratos must be idempotent — two successive \
16550                 calls on the same &self must return the same \
16551                 &[WitContract]",
16552            );
16553            assert_eq!(
16554                first.as_ptr(),
16555                second.as_ptr(),
16556                "Caixa::contratos must borrow the underlying \
16557                 Vec<WitContract> storage — two successive calls must \
16558                 return slices with the same backing pointer (a fresh \
16559                 Vec<WitContract> clone would change the pointer on \
16560                 every call)",
16561            );
16562            assert_eq!(
16563                first,
16564                contratos.as_slice(),
16565                "Caixa::contratos must return :contratos verbatim by \
16566                 borrow — got {first:?}, expected {contratos:?}",
16567            );
16568        }
16569    }
16570
16571    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
16572
16573    #[test]
16574    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
16575        // Load-bearing invariant: every multi-word top-level [`Caixa`]
16576        // serde-derived JSON key routes through a lifted `&'static str`
16577        // const. The Rust field names are `snake_case`
16578        // (`deps_dev` / `upgrade_from` / `max_restarts` /
16579        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
16580        // "camelCase")]` derive attribute maps each to the camelCase
16581        // byte-string the [`Caixa::to_lisp`] round-trip's
16582        // `serde_json::to_value(self)` step lands under before
16583        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
16584        // to the kebab-case `:deps-dev` / `:upgrade-from` /
16585        // `:max-restarts` / `:restart-window` author surface. Serialize
16586        // a fully-populated [`Caixa`] and pin that each canonical
16587        // byte-sequence appears verbatim in the JSON — a future
16588        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
16589        // verbatim-field-name flip at the derive attribute (any of
16590        // which would silently break every [`Caixa::to_lisp`]
16591        // round-trip and the future M4 operator-side manifest ingest's
16592        // `Value::get(<key>)` navigation) surfaces here as a build-time
16593        // test failure at `manifest.rs`, not as an apply-time
16594        // `.get(<stale-canonical-const>)` returning `None` far from the
16595        // derive-attr drift's commit. Same discipline the sibling
16596        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
16597        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
16598        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
16599        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
16600        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
16601        // [`UpgradeFromEntry`] per-entry axes — extended here to the
16602        // enclosing M0 [`Caixa`] top-level axis so the last of the four
16603        // multi-word top-level [`Caixa`] serde-derived JSON keys
16604        // (`depsDev`) joins the substrate's "one canonical byte-string
16605        // per typed serialized-key axis" discipline.
16606        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16607        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16608        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16609        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
16610        c.upgrade_from = vec![UpgradeFromEntry {
16611            from: "0.0.1".into(),
16612            instructions: vec![UpgradeInstruction::Restart],
16613        }];
16614        c.estrategia = Some(RestartStrategy::OneForOne);
16615        c.max_restarts = Some(3);
16616        c.restart_window = Some("60s".into());
16617        c.children = vec![ChildSpec {
16618            caixa: "child".into(),
16619            versao: "^0.1".into(),
16620            restart: RestartPolicy::Permanent,
16621        }];
16622        let json = serde_json::to_string(&c).unwrap();
16623        for key in [
16624            crate::render::CAIXA_KEY_DEPS_DEV,
16625            crate::render::M2_KEY_UPGRADE_FROM,
16626            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16627            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16628        ] {
16629            let quoted = format!("\"{key}\"");
16630            assert!(
16631                json.contains(&quoted),
16632                "serialized Caixa must carry the lifted top-level \
16633                 multi-word byte-sequence {quoted} verbatim in the JSON \
16634                 emission (got: {json})",
16635            );
16636        }
16637    }
16638
16639    #[test]
16640    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
16641        // Cross-axis drift-detection pin: a future collapse of the four
16642        // canonical [`Caixa`] top-level multi-word byte-strings onto the
16643        // same value (e.g. an accidental copy-paste flip of
16644        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
16645        // `"upgradeFrom"`) would silently reroute every downstream
16646        // `Value::get(<key>)` probe on one axis onto the sibling axis's
16647        // top-level entry and pass every propagation-probe test that
16648        // expected only the stale axis's value. Peer of the sibling
16649        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
16650        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
16651        let all = [
16652            crate::render::CAIXA_KEY_DEPS_DEV,
16653            crate::render::M2_KEY_UPGRADE_FROM,
16654            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16655            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16656        ];
16657        for (i, a) in all.iter().enumerate() {
16658            for b in all.iter().skip(i + 1) {
16659                assert_ne!(
16660                    a, b,
16661                    "Caixa top-level multi-word key consts must be \
16662                     pairwise-distinct canonical byte-sequences — got \
16663                     `{a}` == `{b}`",
16664                );
16665            }
16666        }
16667    }
16668
16669    #[test]
16670    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
16671        // Shape-pin: every [`Caixa`] top-level multi-word key const must
16672        // be a lowerCamelCase byte-sequence (no `snake_case`
16673        // underscores, no `kebab-case` hyphens, no leading colon, no
16674        // `PascalCase` leading capital, no whitespace / dots) — the
16675        // canonical shape the `#[serde(rename_all = "camelCase")]`
16676        // derive produces on [`Caixa`]. A future flip to a
16677        // non-camelCase attribute at the derive surfaces both here
16678        // (this test fails on the stale-constant shape) and at
16679        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16680        // (that test fails on the mismatch between const and derive).
16681        // Peer with `membro_key_consts_are_lower_camel_case_shape`
16682        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
16683        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
16684        for key in [
16685            crate::render::CAIXA_KEY_DEPS_DEV,
16686            crate::render::M2_KEY_UPGRADE_FROM,
16687            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16688            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16689        ] {
16690            assert!(
16691                !key.is_empty(),
16692                "Caixa top-level multi-word key const must be non-empty \
16693                 (got {key:?})"
16694            );
16695            let first = key.chars().next().unwrap();
16696            assert!(
16697                first.is_ascii_lowercase(),
16698                "Caixa top-level multi-word key const must lead with an \
16699                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
16700            );
16701            assert!(
16702                key.chars().all(|c| c.is_ascii_alphanumeric()),
16703                "Caixa top-level multi-word key const must be \
16704                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
16705                 whitespace (got {key:?})",
16706            );
16707        }
16708    }
16709
16710    #[test]
16711    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
16712        // Scalar-value pin: the byte-string the
16713        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
16714        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
16715        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
16716        // → `depsTest` matching a hypothetical per-test-target
16717        // vocabulary flip) lands as an edit to exactly one const AND
16718        // one derive attribute — the sibling
16719        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16720        // pin already ties the const to the derive attribute, so a
16721        // rebrand that touches only one side of the pair fails at
16722        // caixa-core build time. Same "scalar-value pin per const"
16723        // discipline the sibling
16724        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
16725        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
16726        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
16727        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
16728    }
16729
16730    #[test]
16731    fn caixa_key_deps_pins_canonical_byte_string() {
16732        // Scalar-value pin: the byte-string the
16733        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
16734        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
16735        // on the two-list dep-graph serialized-key axis — the sibling
16736        // pin covers the multi-word `deps_dev → depsDev` camelCase
16737        // arm, this pin covers the single-word `deps → deps` no-op arm
16738        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
16739        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
16740        // axis and the emitted JSON key equals the source-side field
16741        // name byte-for-byte). A future [`crate::Caixa::deps`] field
16742        // rename (`deps` → `dependencies` matching Cargo's verbatim
16743        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
16744        // hypothetical per-runtime-target vocabulary flip) OR an added
16745        // `#[serde(rename = "…")]` explicit override lands as an edit
16746        // to exactly one const AND one derive-attr / field name — the
16747        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
16748        // pin ties the const to the emitted JSON key, so a rebrand
16749        // that touches only one side of the pair fails at caixa-core
16750        // build time.
16751        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
16752    }
16753
16754    #[test]
16755    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
16756        // Load-bearing invariant on the single-word `deps` top-level
16757        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
16758        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
16759        // `serde_json::to_value(self)` step emits. Serialize a
16760        // populated [`Caixa`] whose `:deps` slot carries at least one
16761        // entry (the `#[serde(default)]` attribute on the field emits
16762        // an empty `[]` even without members, but a non-empty vec
16763        // additionally covers the codec's per-`Dep`-entry emission
16764        // path) and pin that `"deps"` appears verbatim in the JSON
16765        // emission — a future accidental `rename_all = "snake_case"` /
16766        // `"kebab-case"` flip at the derive attribute (or an added
16767        // `#[serde(rename = "…")]` explicit override on the field, or
16768        // a Rust field rename) would break every [`Caixa::to_lisp`]
16769        // round-trip and the future M4 operator-side manifest ingest's
16770        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
16771        // build-time test failure at `manifest.rs`, not as an
16772        // apply-time `.get(<stale-canonical-const>)` returning `None`
16773        // far from the drift's commit. Peer of the sibling
16774        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16775        // multi-word pin on the same M0 [`Caixa`] top-level
16776        // serialized-key axis, extended here to the single-word arm
16777        // the multi-word test's `rename_all = "camelCase"` sweep can't
16778        // reach (single-word `deps → deps` is a no-op the multi-word
16779        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
16780        // `\"restartWindow\"` byte-scan can never observe).
16781        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16782        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
16783        let json = serde_json::to_string(&c).unwrap();
16784        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
16785        assert!(
16786            json.contains(&quoted),
16787            "serialized Caixa must carry the lifted top-level `deps` \
16788             byte-sequence {quoted} verbatim in the JSON emission (got: \
16789             {json})",
16790        );
16791    }
16792
16793    #[test]
16794    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
16795        // Cross-axis drift-detection pin on the two-list dep-graph
16796        // renderer-side wire-key axis: a future collapse of the
16797        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
16798        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
16799        // same value (e.g. an accidental copy-paste flip of
16800        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
16801        // reroute every downstream `Value::get(<key>)` probe on one
16802        // axis onto the sibling axis's dep-list and pass every
16803        // propagation-probe test that expected only the stale axis's
16804        // value — a dev-only dep would land in the runtime closure at
16805        // publish time, or a runtime dep would be excluded from the
16806        // published lacre. Peer of the sibling four-way distinct pin
16807        // on the top-level multi-word tetrad
16808        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
16809        // and the two-way pin on the sibling
16810        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
16811        // author-facing arm (4da6fba's test), extended here to the
16812        // renderer-side wire-key arm of the same two-list dep-graph
16813        // axis so both halves of the "one canonical byte-string per
16814        // typed axis per (author, wire)" grid carry the same
16815        // distinct-ness discipline.
16816        assert_ne!(
16817            crate::render::CAIXA_KEY_DEPS,
16818            crate::render::CAIXA_KEY_DEPS_DEV,
16819            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
16820             canonical byte-sequences on the two-list dep-graph \
16821             renderer-side wire-key axis"
16822        );
16823    }
16824
16825    // ── DepList / Caixa::push_dep pin ────────────────────────────────
16826    //
16827    // The compounding pin: the two-arm closed-set typed enum
16828    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
16829    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
16830    // consumer of the top-level manifest's dep-mutation surface reads
16831    // through, and the typed dispatch [`Caixa::push_dep`] on the
16832    // substrate primitive folds the "select list → check within-list
16833    // dup → push" cascade onto one method call. Prior to this landing
16834    // the two axes lived across two `&'static str` constants
16835    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
16836    // set type carrying the pair; the `feira add` mutation site's
16837    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
16838    // caixa.deps }` dispatch expressed no compile-time link back to
16839    // the substrate primitive, and a future third dep-list axis would
16840    // have silently split at every open-coded mutation site.
16841
16842    #[test]
16843    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
16844        // Every arm returns the same `&'static str` the substrate's
16845        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
16846        // constants carry. A future rebrand on either constant reaches
16847        // the enum through one edit; a regression to inline literals
16848        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
16849        // quotes from the wire-format constants every consumer routes
16850        // through and this pin flags it at build time.
16851        assert_eq!(
16852            crate::dep::DepList::Prod.as_str(),
16853            crate::render::DEP_AUTHOR_KEY_DEPS
16854        );
16855        assert_eq!(
16856            crate::dep::DepList::Dev.as_str(),
16857            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
16858        );
16859    }
16860
16861    #[test]
16862    fn dep_list_display_routes_through_as_str() {
16863        // Same as-str-through-Display convergence discipline the
16864        // sibling closed-set typed enums carry — a `format!("{list}")`
16865        // call must land byte-for-byte on the accessor's return so a
16866        // future consumer that formats the enum for a diagnostic line
16867        // reaches the same wire-format constant the wire-format
16868        // producers do.
16869        assert_eq!(
16870            format!("{}", crate::dep::DepList::Prod),
16871            crate::dep::DepList::Prod.as_str()
16872        );
16873        assert_eq!(
16874            format!("{}", crate::dep::DepList::Dev),
16875            crate::dep::DepList::Dev.as_str()
16876        );
16877    }
16878
16879    #[test]
16880    fn dep_list_all_enumerates_every_variant_once() {
16881        // Exhaustive-iteration pin — every arm appears exactly once in
16882        // `ALL`, matching the closed set the compiler enforces on the
16883        // sibling `match self` arms. A future variant addition that
16884        // extends only one method's match without extending `ALL`
16885        // would silently drop the new arm from every consumer that
16886        // iterates the slice.
16887        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
16888        assert!(variants.contains(&crate::dep::DepList::Prod));
16889        assert!(variants.contains(&crate::dep::DepList::Dev));
16890        assert_eq!(variants.len(), 2);
16891    }
16892
16893    #[test]
16894    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
16895        // Reverse projection on the two-list dep-graph axis: the
16896        // author-surface wire tag the sibling `as_str` emitter walks
16897        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
16898        // `Some(DepList::Prod)`. A regression that hand-rolled the
16899        // per-arm match without routing through the lifted
16900        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
16901        // future wire-tag rebrand and this pin flags it at build time.
16902        assert_eq!(
16903            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
16904            Some(crate::dep::DepList::Prod)
16905        );
16906    }
16907
16908    #[test]
16909    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
16910        // Peer of the `Prod`-arm pin on the dev-only axis: the
16911        // author-surface wire tag the sibling `as_str` emitter walks
16912        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
16913        // back to `Some(DepList::Dev)`. Same drift-detection posture
16914        // as the peer arm — the sibling method `match` arms are
16915        // compiler-checked exhaustive so a future variant addition
16916        // trips at build time.
16917        assert_eq!(
16918            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
16919            Some(crate::dep::DepList::Dev)
16920        );
16921    }
16922
16923    #[test]
16924    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
16925        // Every input outside the closed-set arm-string set the
16926        // sibling `as_str` emitter walks lands on the terminal `None`
16927        // fallback — no silent-accept surface. Sweeps a set of
16928        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
16929        // rebrand candidates, foreign wire tags, empty string) so a
16930        // future variant addition that widened one wire form without
16931        // extending the emitter's arm-set would trip the sibling
16932        // round-trip pin below rather than silently accepting the new
16933        // form here.
16934        for candidate in [
16935            "",
16936            "deps",
16937            "deps-dev",
16938            ":deps ",
16939            ":Deps",
16940            ":DEPS",
16941            ":build-dep",
16942            ":tool-dep",
16943            "prod",
16944            "dev",
16945        ] {
16946            assert_eq!(
16947                crate::dep::DepList::from_wire(candidate),
16948                None,
16949                "from_wire({candidate:?}) must return None; every input outside \
16950                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
16951                 the sibling as_str emitter walks lands on the terminal fallback",
16952            );
16953        }
16954    }
16955
16956    #[test]
16957    fn dep_list_round_trips_through_as_str_and_from_wire() {
16958        // Load-bearing round-trip pin: every arm the `ALL` iteration
16959        // exposes survives the `as_str` → `from_wire` composition
16960        // byte-for-byte. Same discipline the sibling closed-set enums
16961        // carry — `CaixaKind` /
16962        // `RestartStrategy` / `RestartPolicy` /
16963        // `PlacementStrategy` — extended onto the two-list dep-graph
16964        // axis. A future variant addition that extends `ALL` +
16965        // `as_str` without extending `from_wire` (or vice versa)
16966        // trips at build time on this iteration because the compiler
16967        // enforces exhaustiveness on the sibling `match self` arms.
16968        for &list in crate::dep::DepList::ALL {
16969            assert_eq!(
16970                crate::dep::DepList::from_wire(list.as_str()),
16971                Some(list),
16972                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
16973                 a silent split between the forward emitter and the reverse parser \
16974                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
16975            );
16976        }
16977    }
16978
16979    #[test]
16980    fn push_dep_routes_to_deps_slot_on_prod_arm() {
16981        // The `Prod` arm dispatches to the runtime-closure `:deps`
16982        // slot every downstream lacre-pipeline consumer resolves at
16983        // build time. A future arm that regressed to inline `&mut
16984        // self.deps_dev` on the `Prod` path would silently reroute
16985        // every runtime dep into the dev-only closure at publish time
16986        // — this pin refuses that regression.
16987        let src = Caixa::template("host");
16988        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16989        let before_deps = caixa.deps().len();
16990        let before_deps_dev = caixa.deps_dev().len();
16991        let dep = Dep {
16992            nome: "caixa-teia".to_string(),
16993            versao: "^0.1".to_string(),
16994            fonte: None,
16995            opcional: false,
16996            caracteristicas: Vec::new(),
16997        };
16998        caixa
16999            .push_dep(crate::dep::DepList::Prod, dep)
17000            .expect("first push into :deps succeeds");
17001        assert_eq!(caixa.deps().len(), before_deps + 1);
17002        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
17003        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
17004    }
17005
17006    #[test]
17007    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
17008        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
17009        // must dispatch to the dev-only-closure `:deps-dev` slot every
17010        // downstream test-facing artifact resolver reads. A future
17011        // regression that inverted the two arms would silently route
17012        // every dev-only dep into the runtime closure at publish time
17013        // and this pin catches it before the drift ships.
17014        let src = Caixa::template("host");
17015        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17016        let dep = Dep {
17017            nome: "tatara-check".to_string(),
17018            versao: "*".to_string(),
17019            fonte: None,
17020            opcional: false,
17021            caracteristicas: Vec::new(),
17022        };
17023        caixa
17024            .push_dep(crate::dep::DepList::Dev, dep)
17025            .expect("first push into :deps-dev succeeds");
17026        assert!(caixa.deps().is_empty());
17027        assert_eq!(caixa.deps_dev().len(), 1);
17028        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
17029    }
17030
17031    #[test]
17032    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
17033        // Within-list dup check routes through the canonical
17034        // [`DepError::DuplicateNome`] carrier — the substrate's typed
17035        // diagnostic for the same axis [`Caixa::validate_deps`]'s
17036        // parse-time [`crate::render::insert_first_seen`] walk raises
17037        // on. Prior to the lift the mutation site's inline
17038        // `bail!("dep '{}' already declared", …)` string-diagnostic
17039        // path expressed no through-line back to the typed error;
17040        // routing every dep-list refusal through one carrier means an
17041        // author reading a `feira add` refusal and a `feira build`
17042        // refusal reaches for the same corrective surface without
17043        // switching diagnostic idioms.
17044        let src = Caixa::template("host");
17045        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17046        let dep = Dep {
17047            nome: "caixa-teia".to_string(),
17048            versao: "^0.1".to_string(),
17049            fonte: None,
17050            opcional: false,
17051            caracteristicas: Vec::new(),
17052        };
17053        caixa
17054            .push_dep(crate::dep::DepList::Prod, dep.clone())
17055            .expect("first push succeeds");
17056        let dup = Dep {
17057            nome: "caixa-teia".to_string(),
17058            versao: "^0.2".to_string(),
17059            fonte: None,
17060            opcional: false,
17061            caracteristicas: Vec::new(),
17062        };
17063        let err = caixa
17064            .push_dep(crate::dep::DepList::Prod, dup)
17065            .expect_err("second push with same :nome refuses");
17066        assert_eq!(
17067            err,
17068            DepError::DuplicateNome {
17069                nome: "caixa-teia".to_string(),
17070                list: crate::render::DEP_AUTHOR_KEY_DEPS,
17071            }
17072        );
17073        // The refused mutation must not corrupt the target list —
17074        // exactly one entry lives past the refusal, matching the
17075        // canonical single-source-of-truth invariant `Caixa::deps()`
17076        // carries.
17077        assert_eq!(caixa.deps().len(), 1);
17078    }
17079
17080    #[test]
17081    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
17082        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
17083        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
17084        // `list` payload so a future author reading the refusal grep's
17085        // for the correct `:deps-dev` block in their `caixa.lisp`,
17086        // not the sibling `:deps` block the runtime closure resolves.
17087        let src = Caixa::template("host");
17088        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17089        let dep = Dep {
17090            nome: "tatara-check".to_string(),
17091            versao: "*".to_string(),
17092            fonte: None,
17093            opcional: false,
17094            caracteristicas: Vec::new(),
17095        };
17096        caixa
17097            .push_dep(crate::dep::DepList::Dev, dep.clone())
17098            .expect("first push succeeds");
17099        let err = caixa
17100            .push_dep(crate::dep::DepList::Dev, dep)
17101            .expect_err("second push with same :nome refuses");
17102        assert!(matches!(
17103            err,
17104            DepError::DuplicateNome {
17105                ref nome,
17106                list,
17107            } if nome == "tatara-check"
17108                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
17109        ));
17110    }
17111
17112    #[test]
17113    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
17114        // The within-list dup check is scoped to the target arm — a
17115        // caixa may legitimately carry the same `:nome` under both
17116        // `:deps` and `:deps-dev` (though the substrate's peer
17117        // [`crate::Caixa::validate_deps`] walk still refuses the
17118        // shape at parse time; the mutation-site refusal is scoped to
17119        // the mutation-site's list to match the peer parse-time
17120        // per-list [`crate::render::insert_first_seen`] discipline).
17121        // The two arms hold independent seen-sets.
17122        let src = Caixa::template("host");
17123        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17124        let dep_prod = Dep {
17125            nome: "shared".to_string(),
17126            versao: "^0.1".to_string(),
17127            fonte: None,
17128            opcional: false,
17129            caracteristicas: Vec::new(),
17130        };
17131        let dep_dev = Dep {
17132            nome: "shared".to_string(),
17133            versao: "*".to_string(),
17134            fonte: None,
17135            opcional: false,
17136            caracteristicas: Vec::new(),
17137        };
17138        caixa
17139            .push_dep(crate::dep::DepList::Prod, dep_prod)
17140            .expect("push into :deps succeeds");
17141        caixa
17142            .push_dep(crate::dep::DepList::Dev, dep_dev)
17143            .expect("push same :nome into :deps-dev succeeds");
17144        assert_eq!(caixa.deps().len(), 1);
17145        assert_eq!(caixa.deps_dev().len(), 1);
17146    }
17147
17148    #[test]
17149    fn deps_of_prod_returns_the_deps_slot_verbatim() {
17150        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
17151        // accessor must project onto the runtime-closure `:deps` slot —
17152        // element-equal and length-equal to the sibling per-slot
17153        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
17154        // A future arm that regressed to `self.deps_dev()` on the `Prod`
17155        // path would silently reroute every downstream typed-dispatch
17156        // walker (the [`Caixa::validate_deps`] per-list
17157        // [`crate::render::insert_first_seen`] dedup walk, any future
17158        // per-axis-parametrised consumer) into the sibling dev-only
17159        // closure and this pin refuses that regression.
17160        let src = Caixa::template("host");
17161        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17162        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
17163        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
17164        let dep = Dep {
17165            nome: "caixa-teia".to_string(),
17166            versao: "^0.1".to_string(),
17167            fonte: None,
17168            opcional: false,
17169            caracteristicas: Vec::new(),
17170        };
17171        caixa
17172            .push_dep(crate::dep::DepList::Prod, dep.clone())
17173            .expect("push into :deps succeeds");
17174        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
17175        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
17176        assert_eq!(
17177            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
17178            "caixa-teia"
17179        );
17180    }
17181
17182    #[test]
17183    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
17184        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
17185        // [`Caixa::deps_of`] must project onto the dev-only-closure
17186        // `:deps-dev` slot, element-equal and length-equal to the
17187        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
17188        // future regression that inverted the two arms would silently
17189        // route every dev-list walker onto the runtime closure and this
17190        // pin catches it before the drift ships.
17191        let src = Caixa::template("host");
17192        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17193        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
17194        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
17195        let dep = Dep {
17196            nome: "tatara-check".to_string(),
17197            versao: "*".to_string(),
17198            fonte: None,
17199            opcional: false,
17200            caracteristicas: Vec::new(),
17201        };
17202        caixa
17203            .push_dep(crate::dep::DepList::Dev, dep)
17204            .expect("push into :deps-dev succeeds");
17205        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
17206        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
17207        assert_eq!(
17208            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
17209            "tatara-check"
17210        );
17211    }
17212
17213    #[test]
17214    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
17215        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
17216        // [`Caixa::deps_of`] must land on the same two-slot partition the
17217        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
17218        // expose — the canonical dispatch a future per-axis-parametrised
17219        // walker (a future `feira app graph` per-list dep summary, a
17220        // future M4 per-cluster dev-closure-audit overlay the CR
17221        // materializer resolves per-CR) reads through. Prior to the
17222        // lift the two-block iteration lived open-coded at every walker,
17223        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
17224        // §I) would have had to grow a third block at every consumer.
17225        // A regression that dropped the `Dev` arm from `ALL` would flip
17226        // the collected pairs to `[(":deps", &[])]` alone and this pin
17227        // refuses that shape.
17228        let src = Caixa::template("host");
17229        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17230        let prod_dep = Dep {
17231            nome: "caixa-teia".to_string(),
17232            versao: "^0.1".to_string(),
17233            fonte: None,
17234            opcional: false,
17235            caracteristicas: Vec::new(),
17236        };
17237        let dev_dep = Dep {
17238            nome: "tatara-check".to_string(),
17239            versao: "*".to_string(),
17240            fonte: None,
17241            opcional: false,
17242            caracteristicas: Vec::new(),
17243        };
17244        caixa
17245            .push_dep(crate::dep::DepList::Prod, prod_dep)
17246            .expect("push into :deps succeeds");
17247        caixa
17248            .push_dep(crate::dep::DepList::Dev, dev_dep)
17249            .expect("push into :deps-dev succeeds");
17250        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
17251            .iter()
17252            .map(|&list| {
17253                let slice = caixa.deps_of(list);
17254                (list.as_str(), slice.len(), slice[0].nome())
17255            })
17256            .collect();
17257        assert_eq!(
17258            collected,
17259            vec![
17260                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
17261                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
17262            ]
17263        );
17264    }
17265
17266    #[test]
17267    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
17268        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
17269        // must route its per-list [`crate::render::insert_first_seen`]
17270        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
17271        // rather than the pre-lift open-coded two-block iteration over
17272        // `self.deps()` + `self.deps_dev()`. A regression that dropped
17273        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
17274        // stop refusing within-list dups on the sibling arm; a
17275        // regression that flipped the arm-to-list-key mapping
17276        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
17277        // diagnostic surface. Both drifts surface here through a paired
17278        // duplicate-name refusal per arm plus an offending-list-key
17279        // check on the emitted [`DepError::DuplicateNome`] carrier.
17280        for &list in crate::dep::DepList::ALL {
17281            let src = Caixa::template("host");
17282            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17283            let dup = Dep {
17284                nome: "twin".to_string(),
17285                versao: "^0.1".to_string(),
17286                fonte: None,
17287                opcional: false,
17288                caracteristicas: Vec::new(),
17289            };
17290            match list {
17291                crate::dep::DepList::Prod => {
17292                    caixa.deps.push(dup.clone());
17293                    caixa.deps.push(dup);
17294                }
17295                crate::dep::DepList::Dev => {
17296                    caixa.deps_dev.push(dup.clone());
17297                    caixa.deps_dev.push(dup);
17298                }
17299            }
17300            let err = caixa
17301                .validate_deps()
17302                .expect_err("within-list duplicate :nome must refuse");
17303            assert_eq!(
17304                err,
17305                DepError::DuplicateNome {
17306                    nome: "twin".to_string(),
17307                    list: list.as_str(),
17308                },
17309                "validate_deps on {list} arm must emit \
17310                 DepError::DuplicateNome carrying the arm's own \
17311                 as_str() diagnostic — the arm-to-list-key mapping \
17312                 flowed through DepList::ALL + Caixa::deps_of"
17313            );
17314        }
17315    }
17316}