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.
241    #[error(
242        "this is a `{palavra_canonica}` declaration ({descricao}), read by \
243         {consumidor} — not a caixa-core package manifest. `defcaixa` is the \
244         tatara-lisp package manifest (`:nome :versao :kind :deps …`); the two \
245         are different declarations that shared one keyword until 2026-07-31"
246    )]
247    DialetoEstrangeiro {
248        /// Which declaration this actually is.
249        dialeto: crate::dialeto::CaixaDialeto,
250        /// The keyword it should be written with.
251        palavra_canonica: &'static str,
252        /// Who reads it.
253        consumidor: &'static str,
254        /// One-line schema description.
255        descricao: &'static str,
256    },
257
258    /// Not a manifest declaration at all.
259    #[error(transparent)]
260    Dialeto(#[from] crate::dialeto::DialetoError),
261}
262
263impl Caixa {
264    /// Parse a `caixa.lisp` source string to a typed `Caixa`.
265    ///
266    /// Classifies the dialect **before** parsing. A `(defcaixa …)` of another
267    /// declaration is [`LeituraError::DialetoEstrangeiro`], naming what it is
268    /// and who reads it, instead of an unknown-keyword rejection that reads as
269    /// "your manifest is broken".
270    ///
271    /// The ordering is load-bearing. Handing a foreign dialect to the derive
272    /// first and interpreting the failure afterwards would mean guessing from
273    /// an error message, and the guess would be wrong for every file whose
274    /// first unknown slot happens to be one both schemas could plausibly carry.
275    pub fn from_lisp(src: &str) -> Result<Self, LeituraError> {
276        use tatara_lisp::domain::TataraDomain;
277        let forms = tatara_lisp::read(src).map_err(LeituraError::Leitura)?;
278        let first = forms.first().ok_or(crate::dialeto::DialetoError::Vazio)?;
279
280        match crate::dialeto::classify_form(first)? {
281            crate::dialeto::CaixaDialeto::Pacote => {}
282            // `Desconhecido` deliberately falls through to the derive rather
283            // than short-circuiting: a `(defcaixa …)` matching neither schema
284            // is most likely a genuine package manifest with a typo in
285            // `:nome`, and the derive's diagnostic — which names the offending
286            // keyword and suggests the nearest slot — is far better than
287            // anything this classifier could say.
288            crate::dialeto::CaixaDialeto::Desconhecido => {}
289            foreign => {
290                return Err(LeituraError::DialetoEstrangeiro {
291                    dialeto: foreign,
292                    palavra_canonica: foreign.palavra_canonica(),
293                    consumidor: foreign.consumidor(),
294                    descricao: foreign.descricao(),
295                });
296            }
297        }
298
299        Self::compile_from_sexp(first).map_err(LeituraError::Leitura)
300    }
301
302    /// Register `Caixa` with the global tatara-lisp domain registry so
303    /// `defcaixa` is dispatchable from any tatara-lisp binary that seeds
304    /// the registry (e.g. `tatara-check`).
305    ///
306    /// `pending-fallible-register`: upstream `tatara_lisp::domain::register`
307    /// became `-> Result<(), KeywordCollision>` on 2026-07-31, so a second type
308    /// claiming `defcaixa` in one process is refused and named instead of
309    /// silently displacing this one. This workspace pins
310    /// `tatara-lisp = "0.3.3"`, which predates that, so the result cannot be
311    /// checked here yet. Propagate it — `pub fn register() -> Result<(),
312    /// tatara_lisp::KeywordCollision>` — in the same commit that bumps the pin.
313    pub fn register() {
314        tatara_lisp::domain::register::<Self>();
315    }
316
317    /// Substrate-canonical per-`Caixa` `:licenca` SPDX-expression scalar
318    /// accessor every consumer of the top-level manifest's license axis
319    /// keys off — returns the author-declared `:licenca` byte-string
320    /// verbatim as an `Option<&str>`, borrowed from the typed slot's own
321    /// `Option<String>` storage. `None` when the slot is absent (the
322    /// canonical "omit to defer to the caixa-helm renderer's `MIT`
323    /// fallback" shape [`Self::validate_licenca`] documents at
324    /// caixa-core/src/manifest.rs:1560; the peer [`caixa-helm`]
325    /// `build_readme` fold at caixa-helm/src/lib.rs:962 reads this
326    /// predicate too, so an authored-but-unset `:licenca` round-trips to
327    /// a rendered `lareira-<nome>` chart's `README.md` `## License`
328    /// section structurally identical to one that omits the slot).
329    ///
330    /// The `:licenca` slot carries the universal-axis SPDX-expression
331    /// license identifier every kind of caixa emits under (CAIXA-SDLC
332    /// §I — the author-facing surface every `defcaixa` form supplies) —
333    /// the typed slot's `Option<String>` accept-set (empty-string
334    /// rejected through [`ManifestError::LicencaEmpty`], SPDX-alphabet-
335    /// invalid rejected through [`ManifestError::LicencaInvalid`]) maps
336    /// onto the `lareira-<nome>` Helm chart's `README.md` `## License`
337    /// section (caixa-helm/src/lib.rs:962) and (through future
338    /// tightening documented at [`Self::validate_licenca`]) the
339    /// Chart.yaml `annotations["artifacthub.io/license"]` axis every
340    /// registry-facing chart carries. Every downstream consumer that
341    /// reads the license byte-string keys off this scalar (the
342    /// [`Self::validate_licenca`] empty-arm + SPDX-shape gate that
343    /// routes through `self.licenca.as_deref()`, the caixa-helm
344    /// `build_readme` `unwrap_or_else(|| "MIT".into())` fold that keys
345    /// the fallback off the `Option::is_none()` arm, every future
346    /// per-`Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
347    /// acknowledges).
348    ///
349    /// Prior to this lift the `.licenca` field was accessed inline at
350    /// two production sites — [`Self::validate_licenca`]'s
351    /// `self.licenca.as_deref()` empty-and-shape gate binding and the
352    /// caixa-helm `build_readme` `caixa.licenca.clone().unwrap_or_else(||
353    /// "MIT".into())` `README.md` `## License` fold — two open-coded
354    /// field-accesses that expressed no compile-time link back to the
355    /// typed slot. A future extension of the `:licenca` axis to a
356    /// richer author surface — a per-`:licenca` structured SPDX
357    /// expression parser + license-id allowlist (the future tightening
358    /// [`Self::validate_licenca`]'s docstring acknowledges), a
359    /// per-cluster license-default overlay the M4 CR materializer
360    /// resolves per-CR (the "cluster policy pins `Apache-2.0` for every
361    /// unlisted caixa" arm), a promotion of the plain
362    /// `Option<String>` byte-string to a richer `SpdxExpression` enum
363    /// once the SPDX-expression parser lands — would have had to be
364    /// threaded through both open-coded copies in lockstep or the
365    /// validate gate and the caixa-helm emit path would silently
366    /// disagree on which license a given [`Caixa`] resolves to (an
367    /// author's `:licenca "MIT OR Apache-2.0"` would satisfy validate
368    /// while the emit path silently rendered a stale `MIT` fallback,
369    /// or vice versa). Lifting the resolution to a typed method on the
370    /// substrate primitive means every downstream consumer of the
371    /// caixa's per-`Caixa` license surface reaches for exactly one
372    /// typed dispatch — the resolver's accept-set migrates as a unit
373    /// on any future axis addition.
374    ///
375    /// First `Option<&str>`-return top-level [`Caixa`] scalar accessor —
376    /// opens the "outer [`Caixa`] `Option<&str>` scalar" projection
377    /// pattern the sibling per-`Caixa` `:descricao` / `:repositorio` /
378    /// `:edicao` future lifts fold on. Same "one typed dispatch on the
379    /// substrate primitive, thin projections at each consumer"
380    /// discipline the peer per-`:placement` [`crate::aplicacao::Placement::shard_key`]
381    /// (7cd2a28) / [`crate::aplicacao::Placement::affinity`] (74ec2d3)
382    /// / per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
383    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
384    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
385    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
386    /// typed-slot atom axes, extended here to the outer top-level
387    /// `Caixa` universal-axis surface. Named `licenca()` to match the
388    /// storage field's name; the accessor's identity maps onto the
389    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
390    /// carries.
391    #[must_use]
392    pub fn licenca(&self) -> Option<&str> {
393        self.licenca.as_deref()
394    }
395
396    /// Substrate-canonical per-`Caixa` `:repositorio` git-repo-URL scalar
397    /// accessor every consumer of the top-level manifest's homepage /
398    /// source-of-truth axis keys off — returns the author-declared
399    /// `:repositorio` byte-string verbatim as an `Option<&str>`, borrowed
400    /// from the typed slot's own `Option<String>` storage. `None` when
401    /// the slot is absent (the canonical "omit to defer to the renderer's
402    /// per-target placeholder" shape — [`caixa-helm`]'s `ChartYaml.home`
403    /// carries the `Option<String>` through verbatim so an author-omitted
404    /// `:repositorio` renders a `Chart.yaml` without a `home:` field
405    /// (`skip_serializing_if = "Option::is_none"`), while [`caixa-flux`]'s
406    /// `ClusterBundleOpts::for_caixa` folds the omitted slot through a
407    /// `format!("https://github.com/{DEFAULT_PLEME_GIT_ORG}/{nome}")`
408    /// fallback derived from `caixa.nome`).
409    ///
410    /// The `:repositorio` slot carries the universal-axis git-repo-URL
411    /// homepage identifier every kind of caixa emits under (CAIXA-SDLC
412    /// §I — the author-facing surface every `defcaixa` form supplies) —
413    /// the typed slot's `Option<String>` accept-set (empty-string
414    /// rejected through [`ManifestError::RepositorioEmpty`], git-repo-URL-
415    /// shape-invalid rejected through [`ManifestError::RepositorioInvalid`]
416    /// past the shared [`crate::render::is_git_repo_url`] predicate the
417    /// peer per-`:deps :fonte :repo` axis also routes through) maps onto
418    /// four load-bearing downstream consumers:
419    ///
420    ///   - [`Self::validate_repositorio`]'s empty-arm + shape-predicate
421    ///     gate binding at caixa-core/src/manifest.rs:1456 — the
422    ///     universal-axis identity gate wired at caixa-build time.
423    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.home` fold at
424    ///     caixa-helm/src/lib.rs:840 — the rendered `lareira-<nome>`
425    ///     Helm chart's `Chart.yaml` `home:` field, which every registry
426    ///     that ingests the chart (ArtifactHub, chartmuseum,
427    ///     `helm search repo`) surfaces as the chart's canonical source-
428    ///     of-truth link.
429    ///   - [`caixa-helm`]'s `build_readme` `## Source` fold at
430    ///     caixa-helm/src/lib.rs:957 — the rendered `lareira-<nome>`
431    ///     chart's `README.md` header link back to the source repo,
432    ///     which every author who inspects the rendered chart bundle
433    ///     lands at.
434    ///   - [`caixa-flux`]'s `ClusterBundleOpts::for_caixa`
435    ///     `GitRepository.spec.url` fold at caixa-flux/src/lib.rs:2006 —
436    ///     the rendered `GitRepository` CR's `spec.url` field, which
437    ///     FluxCD's `source-controller` polls to reconcile the caixa's
438    ///     manifest bundle from git.
439    ///
440    /// Prior to this lift the `.repositorio` field was accessed inline
441    /// at four production sites — [`Self::validate_repositorio`]'s
442    /// `self.repositorio.as_deref()` empty-and-shape gate binding, the
443    /// caixa-helm `build_chart_yaml` `caixa.repositorio.clone()`
444    /// `Chart.yaml` `home:` field fold, the caixa-helm `build_readme`
445    /// `caixa.repositorio.clone().unwrap_or_else(|| caixa.nome.clone())`
446    /// `README.md` `## Source` fold, and the caixa-flux
447    /// `ClusterBundleOpts::for_caixa`
448    /// `caixa.repositorio.clone().unwrap_or_else(|| format!(...))`
449    /// `GitRepository.spec.url` fold — four open-coded field-accesses
450    /// that expressed no compile-time link back to the typed slot. A
451    /// future extension of the `:repositorio` axis to a richer author
452    /// surface — a per-`:repositorio` structured
453    /// [`crate::render::GitRepoUrl`]-shaped scheme+host+path parse
454    /// (the future tightening [`Self::validate_repositorio`]'s
455    /// docstring anticipates alongside the peer per-`:deps :fonte
456    /// :repo` axis), a per-cluster repo-mirror overlay the M4 CR
457    /// materializer resolves per-CR (the "cluster policy rewrites
458    /// `github:pleme-io/...` to `git.internal/mirror/pleme-io/...`"
459    /// arm the private-registry story acknowledges), a promotion of
460    /// the plain `Option<String>` byte-string to a richer
461    /// `RepoUrl` enum discriminated on scheme — would have had to be
462    /// threaded through all four open-coded copies in lockstep or the
463    /// validate gate and the three emit paths would silently disagree
464    /// on which URL a given [`Caixa`] resolves to (an author's
465    /// `:repositorio "github:pleme-io/checkout"` would satisfy validate
466    /// while one of the emit paths silently rendered a stale URL, or
467    /// vice versa). Lifting the resolution to a typed method on the
468    /// substrate primitive means every downstream consumer of the
469    /// caixa's per-`Caixa` repo-URL surface reaches for exactly one
470    /// typed dispatch — the resolver's accept-set migrates as a unit on
471    /// any future axis addition.
472    ///
473    /// Second outer top-level [`Caixa`] `Option<&str>`-return scalar
474    /// accessor — sibling of [`Self::licenca`] (6d5bc28), the accessor
475    /// that opened the "outer [`Caixa`] `Option<&str>` scalar"
476    /// projection pattern this lift folds on. Same "one typed dispatch
477    /// on the substrate primitive, thin projections at each consumer"
478    /// discipline the peer per-`:placement`
479    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
480    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
481    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
482    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
483    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
484    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
485    /// typed-slot atom axes, extended here to the second outer top-level
486    /// `Caixa` universal-axis surface. Named `repositorio()` to match
487    /// the storage field's name; the accessor's identity maps onto the
488    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
489    /// carries.
490    #[must_use]
491    pub fn repositorio(&self) -> Option<&str> {
492        self.repositorio.as_deref()
493    }
494
495    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
496    /// chart-description scalar accessor every consumer of the top-level
497    /// manifest's Chart.yaml `description:` axis keys off — returns the
498    /// author-declared `:descricao` byte-string verbatim as an
499    /// `Option<&str>`, borrowed from the typed slot's own
500    /// `Option<String>` storage. `None` when the slot is absent (the
501    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
502    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
503    /// omitted slot through a `format!("Generated chart for caixa Servico
504    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
505    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
506    /// and [`caixa-feira`]'s `render_flake` folds it through a
507    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
508    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
509    ///
510    /// The `:descricao` slot carries the universal-axis free-form-prose
511    /// chart-description identifier every kind of caixa emits under
512    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
513    /// supplies) — the typed slot's `Option<String>` accept-set
514    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
515    /// chart-description-shape-invalid rejected through
516    /// [`ManifestError::DescricaoInvalid`] past the shared
517    /// [`crate::render::is_chart_description_shape`] predicate the peer
518    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
519    /// load-bearing downstream consumers:
520    ///
521    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
522    ///     gate binding — the universal-axis identity gate wired at
523    ///     caixa-build time.
524    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
525    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
526    ///     chart's `Chart.yaml` `description:` field, which
527    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
528    ///     `WARNING [chart.metadata.description]: description is required`
529    ///     when absent) and which every registry that ingests the chart
530    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
531    ///     chart's canonical one-line prose descriptor.
532    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
533    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
534    ///     header directly beneath the `# <chart-name>` title, which
535    ///     every author who inspects the rendered chart bundle lands at.
536    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
537    ///     top-level fold — the emitted `flake.nix`'s `description`
538    ///     field, which every Nix consumer (`nix flake show`,
539    ///     `nix flake metadata`, downstream flake-registry ingestors)
540    ///     surfaces as the flake's canonical descriptor.
541    ///
542    /// Prior to this lift the `.descricao` field was accessed inline at
543    /// four production sites — [`Self::validate_descricao`]'s
544    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
545    /// caixa-helm `build_chart_yaml`
546    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
547    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
548    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
549    /// `README.md` header fold, and the caixa-feira `render_flake`
550    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
551    /// `description = ""` fold — four open-coded field-accesses that
552    /// expressed no compile-time link back to the typed slot. A future
553    /// extension of the `:descricao` axis to a richer author surface —
554    /// a per-`:descricao` locale-tagged multi-language descriptor map
555    /// (the "one caixa, N language-tagged prose descriptions" arm
556    /// author-tooling internationalization anticipates), a
557    /// per-registry-target length-and-shape overlay the M4 CR
558    /// materializer resolves per-CR (the "ArtifactHub caps description
559    /// at 512 bytes but the internal registry caps at 256" arm), a
560    /// promotion of the plain `Option<String>` byte-string to a richer
561    /// `ChartDescription` newtype guaranteeing the
562    /// `is_chart_description_shape` predicate at the type level — would
563    /// have had to be threaded through all four open-coded copies in
564    /// lockstep or the validate gate and the three emit paths would
565    /// silently disagree on which prose string a given [`Caixa`]
566    /// resolves to (an author's
567    /// `:descricao "Checkout flow orchestration."` would satisfy
568    /// validate while one of the emit paths silently rendered a stale
569    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
570    /// resolution to a typed method on the substrate primitive means
571    /// every downstream consumer of the caixa's per-`Caixa`
572    /// chart-description surface reaches for exactly one typed dispatch
573    /// — the resolver's accept-set migrates as a unit on any future
574    /// axis addition.
575    ///
576    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
577    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
578    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
579    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
580    /// lift folds on. Same "one typed dispatch on the substrate
581    /// primitive, thin projections at each consumer" discipline the
582    /// peer per-`:placement`
583    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
584    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
585    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
586    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
587    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
588    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
589    /// typed-slot atom axes, extended here to the third outer top-level
590    /// `Caixa` universal-axis surface. Named `descricao()` to match the
591    /// storage field's name; the accessor's identity maps onto the
592    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
593    /// carries. The one remaining universal `Option<String>` slot
594    /// (`:edicao`) folds on this pattern next.
595    #[must_use]
596    pub fn descricao(&self) -> Option<&str> {
597        self.descricao.as_deref()
598    }
599
600    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
601    /// accessor every consumer of the top-level manifest's tatara-lisp
602    /// edition-selector axis keys off — returns the author-declared
603    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
604    /// the typed slot's own `Option<String>` storage. `None` when the
605    /// slot is absent (the canonical "omit the slot to defer to the
606    /// substrate's default edition" shape every existing
607    /// [`caixa-resolver`] integration test fixture carries via
608    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
609    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
610    /// arm by construction, so an author-omitted `:edicao` round-trips
611    /// to a build without triggering the year-shape predicate).
612    ///
613    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
614    /// decimal-year language-edition identifier every kind of caixa
615    /// emits under (CAIXA-SDLC §I — the author-facing surface every
616    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
617    /// accept-set (empty-string rejected through
618    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
619    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
620    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
621    /// onto one load-bearing downstream consumer today
622    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
623    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
624    /// future edition-aware substrate consumer the CAIXA-SDLC §I
625    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
626    /// selector every edition-aware build step keys off, the future
627    /// per-edition compatibility-flag overlay the M4 CR materializer
628    /// resolves per-CR, the peer [`Caixa::template`] canonical
629    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
630    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
631    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
632    /// carry `edicao: Some("2026".into())` by construction).
633    ///
634    /// Prior to this lift the `.edicao` field was accessed inline at
635    /// one production site — [`Self::validate_edicao`]'s
636    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
637    /// open-coded field-access that expressed no compile-time link
638    /// back to the typed slot. A future extension of the `:edicao`
639    /// axis to a richer author surface — a per-`:edicao` known-
640    /// edition allowlist (the future tightening
641    /// [`Self::validate_edicao`]'s docstring acknowledges past the
642    /// structural year-shape floor, rejecting year-shaped values that
643    /// don't name a tatara-lisp edition the substrate actually
644    /// understands — `"1999"` is year-shaped but no `1999` edition
645    /// exists), a per-edition compatibility-flag overlay the M4 CR
646    /// materializer resolves per-CR (the "edition `"2026"` enables
647    /// macro-surface features the sibling `"2018"` gates behind a
648    /// feature flag" arm the edition-selector story anticipates), a
649    /// promotion of the plain `Option<String>` byte-string to a
650    /// richer `CaixaEdition` enum discriminated on year once a sibling
651    /// edition to `"2026"` lands — would have had to be threaded
652    /// through the open-coded copy in lockstep with every future
653    /// edition-aware consumer, or the validate gate and the future
654    /// edition-aware consumer path would silently disagree on which
655    /// edition a given [`Caixa`] resolves to (an author's
656    /// `:edicao "2026"` would satisfy validate while a future
657    /// edition-aware consumer silently defaulted to a stale edition,
658    /// or vice versa). Lifting the resolution to a typed method on
659    /// the substrate primitive means every downstream consumer of the
660    /// caixa's per-`Caixa` edition surface reaches for exactly one
661    /// typed dispatch — the resolver's accept-set migrates as a unit
662    /// on any future axis addition.
663    ///
664    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
665    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
666    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
667    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
668    /// `Option<&str>` scalar" projection pattern this lift folds on.
669    /// Same "one typed dispatch on the substrate primitive, thin
670    /// projections at each consumer" discipline the peer per-`:placement`
671    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
672    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
673    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
674    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
675    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
676    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
677    /// typed-slot atom axes, extended here to close the outer top-level
678    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
679    /// slot. Named `edicao()` to match the storage field's name; the
680    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
681    /// vocabulary the slot's docstring already carries.
682    #[must_use]
683    pub fn edicao(&self) -> Option<&str> {
684        self.edicao.as_deref()
685    }
686
687    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
688    /// label caixa-identity scalar accessor every consumer of the top-
689    /// level manifest's identity axis keys off — returns the author-
690    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
691    /// the typed slot's own `String` storage. Non-optional (`:nome` is
692    /// a required-axis scalar every `defcaixa` form must supply; the
693    /// [`Self::from_lisp`] derive rejects an omitted / non-string
694    /// `:nome` at parse time, so a `Caixa` past parse definitionally
695    /// carries a non-`None` `:nome`).
696    ///
697    /// The `:nome` slot carries the universal-axis DNS-1123-label
698    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
699    /// the primary identity axis every `defcaixa` form supplies
700    /// alongside `:versao` / `:kind`; the substrate-wide identity every
701    /// other typed surface that names a caixa reaches through — `:deps`
702    /// entries, `:membros` entries, `:children` entries, the
703    /// `lareira-<nome>` Helm chart name every per-Servico renderer
704    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
705    /// renderer emits) — the typed slot's `String` accept-set (empty
706    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
707    /// invalid rejected through [`ManifestError::NomeInvalid`] past
708    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
709    /// the peer name axes each land on, joint-length-with-`lareira-`-
710    /// prefix rejected through
711    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
712    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
713    /// load-bearing downstream consumer the substrate carries — the
714    /// two universal-axis validate gates at caixa-build time
715    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
716    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
717    /// derivation every per-Servico renderer keys off, the caixa-helm
718    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
719    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
720    /// `HTTPRoute` per-Aplicacao name axes at
721    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
722    /// [`crate::pleme_program_selector`] /
723    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
724    /// derivations, and every future substrate renderer that emits an
725    /// artifact keyed by the caixa's identity.
726    ///
727    /// Prior to this lift the `.nome` field was accessed inline at a
728    /// dozen production sites across `caixa-core` (the two universal-
729    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
730    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
731    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
732    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
733    /// entry `name:` fold, the `flux_kustomization_source_subtree`
734    /// per-cluster subpath derivation), and `caixa-mesh` (the
735    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
736    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
737    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
738    /// insert) — a dozen open-coded field-accesses that expressed no
739    /// compile-time link back to the typed slot. A future extension of
740    /// the `:nome` axis to a richer author surface — a per-`:nome`
741    /// structured `CaixaIdentity` newtype that carries the joint-
742    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
743    /// enforces at the type level (rather than as a validate-time
744    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
745    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
746    /// `partner-org/checkout` collision" arm the multi-tenant-registry
747    /// story acknowledges), a promotion of the plain `String` byte-
748    /// string to a richer `CaixaNome` newtype discriminated on
749    /// namespace prefix — would have had to be threaded through every
750    /// open-coded copy in lockstep or the two validate gates and the
751    /// dozen emit paths would silently disagree on which identity a
752    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
753    /// would satisfy validate while one of the emit paths silently
754    /// rendered a drifted other identity, or vice versa). Lifting the
755    /// resolution to a typed method on the substrate primitive means
756    /// every downstream consumer of the caixa's per-`Caixa` identity
757    /// surface reaches for exactly one typed dispatch — the resolver's
758    /// accept-set migrates as a unit on any future axis addition.
759    ///
760    /// First outer top-level [`Caixa`] `&str`-return required-scalar
761    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
762    /// projection pattern the sibling per-`Caixa` `:versao` future lift
763    /// folds on. Sibling in shape to the peer per-`:membros`
764    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
765    /// [`crate::aplicacao::WitContract::source`] /
766    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
767    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
768    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
769    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
770    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
771    /// per-sub-struct required-axis accessors carry on the sibling M3
772    /// mesh-slot-atom scalar-value axes, extended here to open the
773    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
774    /// Named `nome()` to match the storage field's name; the accessor's
775    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
776    /// slot's docstring already carries.
777    #[must_use]
778    pub fn nome(&self) -> &str {
779        &self.nome
780    }
781
782    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
783    /// pinned-version scalar accessor every consumer of the top-level
784    /// manifest's version axis keys off — returns the author-declared
785    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
786    /// typed slot's own `String` storage. Non-optional (`:versao` is a
787    /// required-axis scalar every `defcaixa` form must supply alongside
788    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
789    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
790    /// parse definitionally carries a non-`None` `:versao`).
791    ///
792    /// The `:versao` slot carries the universal-axis SemVer-2
793    /// concrete-version body every kind of caixa emits under
794    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
795    /// supplies alongside `:nome` / `:kind`; the substrate-wide
796    /// pinned-version every downstream artifact-emitting consumer
797    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
798    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
799    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
800    /// prefix composes on top of, the programs.yaml entry's `versao:`
801    /// value the `lareira-fleet-programs` aggregator carries onto each
802    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
803    /// tags every substrate-side `skopeo push` writes, the lacre
804    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
805    /// prior-version references peers in the exact same SemVer-2 shape).
806    /// The typed slot's `String` accept-set (empty rejected through
807    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
808    /// through [`ManifestError::VersaoInvalid`] past
809    /// [`semver::Version::parse`]) maps onto every load-bearing
810    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
811    /// universal-axis validate gate at caixa-build time, the
812    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
813    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
814    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
815    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
816    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
817    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
818    /// tag derivation (`format!("{prefix}{versao}")`), and every future
819    /// substrate renderer that emits an artifact keyed by the caixa's
820    /// pinned version.
821    ///
822    /// Prior to this lift the `.versao` field was accessed inline at a
823    /// dozen production sites across `caixa-core` (the universal-axis
824    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
825    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
826    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
827    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
828    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
829    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
830    /// (the `feira publish` git-tag derivation + the `feira app graph` /
831    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
832    /// field-accesses that expressed no compile-time link back to the
833    /// typed slot. A future extension of the `:versao` axis to a richer
834    /// author surface — a per-`:versao` structured `CaixaVersion` at the
835    /// storage layer (the substrate already carries a `CaixaVersion`
836    /// newtype at [`crate::version::CaixaVersion`], deferred until the
837    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
838    /// a per-registry `:versao` immutability overlay the M4 CR
839    /// materializer enforces per-CR, a promotion of the plain `String`
840    /// byte-string to a richer `PinnedVersao` newtype discriminated on
841    /// SemVer-2 pre-release / build-metadata presence — would have had
842    /// to be threaded through every open-coded copy in lockstep or the
843    /// validate gate and the dozen emit paths would silently disagree
844    /// on which version a given [`Caixa`] resolves to (an author's
845    /// `:versao "0.1.0"` would satisfy validate while one of the emit
846    /// paths silently rendered a drifted other version, or vice versa).
847    /// Lifting the resolution to a typed method on the substrate
848    /// primitive means every downstream consumer of the caixa's
849    /// per-`Caixa` pinned-version surface reaches for exactly one typed
850    /// dispatch — the resolver's accept-set migrates as a unit on any
851    /// future axis addition.
852    ///
853    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
854    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
855    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
856    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
857    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
858    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
859    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
860    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
861    /// on the sibling per-typed-slot version-carrier axes, extended here
862    /// to close the second outer top-level [`Caixa`] required-`&str`-
863    /// carrying axis so the two universal-axis identity-carrying
864    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
865    /// share the same "one typed dispatch per axis" discipline. Named
866    /// `versao()` to match the storage field's name; the accessor's
867    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
868    /// slot's docstring already carries.
869    #[must_use]
870    pub fn versao(&self) -> &str {
871        &self.versao
872    }
873
874    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
875    /// closed-set-enum discriminant accessor every consumer of the top-
876    /// level manifest's kind axis keys off — returns the author-declared
877    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
878    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
879    /// (`:kind` is a required-axis discriminant every `defcaixa` form
880    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
881    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
882    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
883    /// variant).
884    ///
885    /// The `:kind` slot carries the universal-axis closed-set typed-
886    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
887    /// §I — the primary shape gate every renderer / verifier /
888    /// operator branches on; the five variants `Biblioteca` /
889    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
890    /// the caixa surface into disjoint runtime contracts) — the typed
891    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
892    /// values through the derive-macro's symbol-arm gate, exhaustively
893    /// matched at every downstream dispatch site) maps onto every
894    /// load-bearing downstream consumer the substrate carries:
895    ///
896    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
897    ///     predicate — the canonical two-line
898    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
899    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
900    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
901    ///     ComputeUnit` CR materializer) runs at its entry-point,
902    ///     alongside the [`crate::render::KindMismatch`] error carrier's
903    ///     `actual:` field the diagnostic surfaces to name the offending
904    ///     caixa's variant.
905    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
906    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
907    ///     `_view` composers that fold the flat mesh-slot / supervisor-
908    ///     slot columns into their typed sub-spec only when the kind
909    ///     matches (returns `None` otherwise); the future per-Servico
910    ///     M2-view composer (`servico_view`) will follow the same shape.
911    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
912    ///     coherence gate — the `!self.kind.requires_exe()` /
913    ///     `!self.kind.requires_servicos()` predicates that fence
914    ///     each code-surface slot from the wrong owning kind.
915    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
916    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
917    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
918    ///     coherence error carriers (`SupervisorOwnsCode` /
919    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
920    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
921    ///     / `ForeignCodeSlot`) which each name the offending caixa's
922    ///     variant in their `kind:` field.
923    ///
924    /// Prior to this lift the `.kind` field was accessed inline at
925    /// twenty-plus production sites across `caixa-core` (the
926    /// [`crate::render::require_kind`] entry-gate predicate + the
927    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
928    /// composers, the `declared_foreign_code_slots` per-slot kind-
929    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
930    /// kind ↔ code-surface predicates + four error carriers) — a score
931    /// of open-coded field-accesses that expressed no compile-time link
932    /// back to the typed slot. A future extension of the `:kind` axis
933    /// to a richer author surface — a per-`:kind` sub-variant discriminant
934    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
935    /// variant across the wasm-component / legacy-container / native-
936    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
937    /// kind-overlay the M4 CR materializer resolves per-CR (the
938    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
939    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
940    /// enum to a richer `KindWithRuntime` discriminated on the
941    /// component-model world axis — would have had to be threaded
942    /// through every open-coded copy in lockstep or the entry gate,
943    /// the view composers, and the layout invariants would silently
944    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
945    /// the resolution to a typed method on the substrate primitive
946    /// means every downstream consumer of the caixa's per-`Caixa`
947    /// kind surface reaches for exactly one typed dispatch — the
948    /// resolver's accept-set migrates as a unit on any future axis
949    /// addition.
950    ///
951    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
952    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
953    /// required-discriminant" projection pattern. Sibling in shape to
954    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
955    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
956    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
957    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
958    /// on the sibling nested-spec typed-slot discriminator axes,
959    /// extended here to the outer top-level [`Caixa`] universal-axis
960    /// surface. Named `kind()` to match the storage field's name;
961    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
962    /// vocabulary the slot's docstring already carries.
963    #[must_use]
964    pub fn kind(&self) -> CaixaKind {
965        self.kind
966    }
967
968    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
969    /// maintainer-name-list slice-accessor every consumer of the top-
970    /// level manifest's maintainer axis keys off — returns the author-
971    /// declared `:autores` list verbatim as a `&[String]` slice-view over
972    /// the same backing buffer the raw `self.autores.as_slice()` field
973    /// access borrows from. Empty-list-carrying (`:autores` is a default-
974    /// empty axis every `defcaixa` form supplies with an empty `()` when
975    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
976    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
977    /// parse definitionally carries a `Vec<String>` slot — possibly
978    /// empty — and the returned `&[String]` degenerates to an empty
979    /// slice on that arm without any silent `None` collapse).
980    ///
981    /// The `:autores` slot carries the universal-axis maintainer-name
982    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
983    /// facing surface every `defcaixa` form supplies alongside `:nome` /
984    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
985    /// every downstream registry-facing artifact emits under) — the
986    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
987    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
988    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
989    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
990    /// onto every load-bearing downstream consumer the substrate carries
991    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
992    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
993    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
994    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
995    /// name, email: None }` record, every future per-`Caixa` registry-
996    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
997    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
998    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
999    /// the future per-cluster author-notification overlay the M4 CR
1000    /// materializer resolves per-CR).
1001    ///
1002    /// Prior to this lift the `.autores` field was accessed inline at
1003    /// two production sites — [`Self::validate_autores`]'s `for autor
1004    /// in &self.autores` walk that gates every entry through
1005    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1006    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1007    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1008    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1009    /// two open-coded field-accesses that expressed no compile-time link
1010    /// back to the typed slot. A future extension of the `:autores` axis
1011    /// to a richer author surface — a per-`:autores` structured
1012    /// `Maintainer { name, email, url }` at the storage layer once the
1013    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1014    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1015    /// enforces per-CR (the "cluster policy demands every author declare
1016    /// an on-file `mailto:` contact" arm), a promotion of the plain
1017    /// `Vec<String>` byte-string list to a richer
1018    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1019    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1020    /// predicate already resolves through — would have had to be
1021    /// threaded through both open-coded copies in lockstep or the
1022    /// validate gate and the caixa-helm emit path would silently
1023    /// disagree on which authors a given [`Caixa`] resolves to (an
1024    /// author's `:autores ("alice" "bob")` would satisfy validate while
1025    /// the caixa-helm emit path silently rendered a drifted other
1026    /// maintainer list, or vice versa). Lifting the resolution to a
1027    /// typed method on the substrate primitive means every downstream
1028    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1029    /// for exactly one typed dispatch — the resolver's accept-set
1030    /// migrates as a unit on any future axis addition.
1031    ///
1032    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1033    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1034    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1035    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1036    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1037    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1038    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1039    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1040    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1041    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1042    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1043    /// per-M3 typed-slot list axes, extended here to the outer top-level
1044    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1045    /// `&Vec<String>`) because every downstream consumer of the author
1046    /// list treats it as a read-only sequence — the slice-view is the
1047    /// narrowest borrow that supports every present + roadmapped consumer
1048    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1049    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1050    /// reaches for (the storage-side `Vec` remains reachable through the
1051    /// `pub autores` field for the mutation-carrying serde round-trip and
1052    /// per-test fixture-mutation paths). Named `autores()` to match the
1053    /// storage field's name; the accessor's identity maps onto the
1054    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1055    /// carries.
1056    #[must_use]
1057    pub fn autores(&self) -> &[String] {
1058        self.autores.as_slice()
1059    }
1060
1061    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1062    /// registry-search-tag-list slice-accessor every consumer of the
1063    /// top-level manifest's topical-tag axis keys off — returns the
1064    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1065    /// slice-view over the same backing buffer the raw
1066    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1067    /// list-carrying (`:etiquetas` is a default-empty axis every
1068    /// `defcaixa` form supplies with an empty `()` when unset; the
1069    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1070    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1071    /// definitionally carries a `Vec<String>` slot — possibly empty —
1072    /// and the returned `&[String]` degenerates to an empty slice on
1073    /// that arm without any silent `None` collapse).
1074    ///
1075    /// The `:etiquetas` slot carries the universal-axis topical-tag
1076    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1077    /// author-facing surface every `defcaixa` form supplies alongside
1078    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1079    /// search-facing axis every downstream registry-facing artifact
1080    /// emits under) — the typed slot's `Vec<String>` accept-set
1081    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1082    /// non-chart-keyword-shape rejected through
1083    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1084    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1085    /// every load-bearing downstream consumer the substrate carries —
1086    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1087    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1088    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1089    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1090    /// `Chart.yaml` `keywords:` array (chained with the
1091    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1092    /// dedup'd through a `BTreeSet` at emit time), every future per-
1093    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1094    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1095    /// annotation, the future per-cluster tag-notification overlay the
1096    /// M4 CR materializer resolves per-CR).
1097    ///
1098    /// Prior to this lift the `.etiquetas` field was accessed inline at
1099    /// two production sites — [`Self::validate_etiquetas`]'s `for
1100    /// etiqueta in &self.etiquetas` walk that gates every entry through
1101    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1102    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1103    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1104    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1105    /// two open-coded field-accesses that expressed no compile-time
1106    /// link back to the typed slot. A future extension of the
1107    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1108    /// structured `ChartKeyword { name, uri, category }` at the storage
1109    /// layer once the substrate absorbs `artifacthub.io/keywords`
1110    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1111    /// CR materializer enforces per-CR (the "cluster policy demands
1112    /// every tag come from a substrate-approved taxonomy" arm), a
1113    /// promotion of the plain `Vec<String>` byte-string list to a
1114    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1115    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1116    /// already resolves through — would have had to be threaded through
1117    /// both open-coded copies in lockstep or the validate gate and the
1118    /// caixa-helm emit path would silently disagree on which tags a
1119    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1120    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1121    /// path silently rendered a drifted other keyword list, or vice
1122    /// versa). Lifting the resolution to a typed method on the
1123    /// substrate primitive means every downstream consumer of the
1124    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1125    /// typed dispatch — the resolver's accept-set migrates as a unit
1126    /// on any future axis addition.
1127    ///
1128    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1129    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1130    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1131    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1132    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1133    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1134    /// fold onto the same pattern in future lifts. Sibling in shape to
1135    /// the peer per-`:supervisor`
1136    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1137    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1138    /// (a6e18d7), per-`:membros`
1139    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1140    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1141    /// (0dcc926), and per-`:upgrade-from :instructions`
1142    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1143    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1144    /// typed-slot list axes, extended here to the outer top-level
1145    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1146    /// `&Vec<String>`) because every downstream consumer of the tag
1147    /// list treats it as a read-only sequence — the slice-view is the
1148    /// narrowest borrow that supports every present + roadmapped
1149    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1150    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1151    /// the typed view reaches for (the storage-side `Vec` remains
1152    /// reachable through the `pub etiquetas` field for the mutation-
1153    /// carrying serde round-trip and per-test fixture-mutation paths).
1154    /// Named `etiquetas()` to match the storage field's name; the
1155    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1156    /// vocabulary the slot's docstring already carries.
1157    #[must_use]
1158    pub fn etiquetas(&self) -> &[String] {
1159        self.etiquetas.as_slice()
1160    }
1161
1162    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1163    /// library-source-path-list slice-accessor every consumer of the
1164    /// top-level manifest's Biblioteca-source axis keys off — returns
1165    /// the author-declared `:bibliotecas` list verbatim as a
1166    /// `&[String]` slice-view over the same backing buffer the raw
1167    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1168    /// list-carrying (`:bibliotecas` is a default-empty axis every
1169    /// `defcaixa` form supplies with an empty `()` when unset; the
1170    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1171    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1172    /// parse definitionally carries a `Vec<String>` slot — possibly
1173    /// empty — and the returned `&[String]` degenerates to an empty
1174    /// slice on that arm without any silent `None` collapse).
1175    ///
1176    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1177    /// entry-path list every `:kind Biblioteca` caixa emits under
1178    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1179    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1180    /// substrate-wide library-carrier axis every downstream
1181    /// authoring-facing consumer keys off) — the typed slot's
1182    /// `Vec<String>` accept-set (empty-per-entry rejected through
1183    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1184    /// non-sandboxed-relative-shape rejected through
1185    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1186    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1187    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1188    /// maps onto every load-bearing downstream consumer the substrate
1189    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1190    /// empty-check + per-entry file-exists loop at
1191    /// caixa-core/src/layout.rs that gates each entry through
1192    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1193    /// [`Self::validate_code_paths`] per-slot shape gate at
1194    /// caixa-core/src/manifest.rs that walks each entry through the
1195    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1196    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1197    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1198    /// declared library file for lexical / structural errors before
1199    /// downstream `importar` resolution, every future per-`Caixa`
1200    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1201    /// (the future `tatara-lispc` compilation entry the docstring at
1202    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1203    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1204    /// the future `caixa-lsp` per-library semantic-token stream the
1205    /// caixa-lsp docstring roadmaps).
1206    ///
1207    /// Prior to this lift the `.bibliotecas` field was accessed inline
1208    /// at three production sites — [`crate::LayoutInvariants`]'s
1209    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1210    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1211    /// declared library path through the on-disk-existence check,
1212    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1213    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1214    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1215    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1216    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1217    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1218    /// coded field-accesses that expressed no compile-time link back
1219    /// to the typed slot. A future extension of the `:bibliotecas`
1220    /// axis to a richer library surface — a per-`:bibliotecas`
1221    /// structured `BibliotecaEntry { path, edition, exports }` at the
1222    /// storage layer once the substrate absorbs the per-library
1223    /// language-edition + explicit-exports tuple the tatara-lisp
1224    /// module-system roadmap acknowledges, a per-registry
1225    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1226    /// per-CR (the "cluster policy demands every biblioteca declare
1227    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1228    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1229    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1230    /// [`crate::render::is_sandboxed_relative_path`] +
1231    /// [`crate::render::is_lisp_extension`] predicates already resolve
1232    /// through — would have had to be threaded through all three
1233    /// open-coded copies in lockstep or the layout gate, the shape
1234    /// validator, and the `feira build` phase-1 parse walk would
1235    /// silently disagree on which library paths a given [`Caixa`]
1236    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1237    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1238    /// silently parsed a drifted other list, or vice versa). Lifting
1239    /// the resolution to a typed method on the substrate primitive
1240    /// means every downstream consumer of the caixa's per-`Caixa`
1241    /// library-source surface reaches for exactly one typed dispatch
1242    /// — the resolver's accept-set migrates as a unit on any future
1243    /// axis addition.
1244    ///
1245    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1246    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1247    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1248    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1249    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1250    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1251    /// `:children` / `:membros` / `:contratos`) fold onto the same
1252    /// pattern in future lifts. Sibling in shape to the peer
1253    /// per-`:supervisor`
1254    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1255    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1256    /// (a6e18d7), per-`:membros`
1257    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1258    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1259    /// (0dcc926), and per-`:upgrade-from :instructions`
1260    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1261    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1262    /// typed-slot list axes, extended here to the outer top-level
1263    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1264    /// `&Vec<String>`) because every downstream consumer of the
1265    /// library-source list treats it as a read-only sequence — the
1266    /// slice-view is the narrowest borrow that supports every
1267    /// present + roadmapped consumer (`.iter()`, `.len()`,
1268    /// `.is_empty()`) without leaking the backing `Vec`'s
1269    /// grow/push/reserve surface no consumer of the typed view
1270    /// reaches for (the storage-side `Vec` remains reachable through
1271    /// the `pub bibliotecas` field for the mutation-carrying serde
1272    /// round-trip and per-test fixture-mutation paths). Named
1273    /// `bibliotecas()` to match the storage field's name; the
1274    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1275    /// vocabulary the slot's docstring already carries.
1276    #[must_use]
1277    pub fn bibliotecas(&self) -> &[String] {
1278        self.bibliotecas.as_slice()
1279    }
1280
1281    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1282    /// nix-built-executable-entry-path-list slice-accessor every consumer
1283    /// of the top-level manifest's Binario-executable axis keys off —
1284    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1285    /// slice-view over the same backing buffer the raw
1286    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1287    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1288    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1289    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1290    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1291    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1292    /// degenerates to an empty slice on that arm without any silent
1293    /// `None` collapse).
1294    ///
1295    /// The `:exe` slot carries the universal-axis nix-built executable
1296    /// entry-path list every `:kind Binario` caixa emits under
1297    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1298    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1299    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1300    /// downstream flake-build-facing consumer keys off) — the typed
1301    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1302    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1303    /// non-sandboxed-relative-shape rejected through
1304    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1305    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1306    /// directory paths rejected past the layout's
1307    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1308    /// onto every load-bearing downstream consumer the substrate carries
1309    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1310    /// per-entry file-exists + `exe/`-directory-fence loop at
1311    /// caixa-core/src/layout.rs that gates each entry through
1312    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1313    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1314    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1315    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1316    /// that fences code-surface slots off from the two no-code kinds,
1317    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1318    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1319    /// fences the `:exe` code surface off from every non-Binario code-
1320    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1321    /// that walks each entry through the sandbox-relative / cross-entry
1322    /// duplicate gates, every future per-`Caixa` executable-facing
1323    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1324    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1325    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1326    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1327    /// future `feira nix` per-executable Binario-target emit path).
1328    ///
1329    /// Prior to this lift the `.exe` field was accessed inline at three
1330    /// production sites — the compound-code-path `has_code =
1331    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1332    /// !caixa.servicos.is_empty()` OR-fold on the
1333    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1334    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1335    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1336    /// gate, the per-entry `for p in &caixa.exe`
1337    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1338    /// [`Self::declared_foreign_code_slots`]'s
1339    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1340    /// open-coded field-accesses that expressed no compile-time link
1341    /// back to the typed slot. A future extension of the `:exe` axis
1342    /// to a richer executable surface — a per-`:exe` structured
1343    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1344    /// layer once the substrate absorbs the per-executable
1345    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1346    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1347    /// the M4 CR materializer enforces per-CR (the "cluster policy
1348    /// demands every Binario declare an explicit `:wrapper`" arm), a
1349    /// promotion of the plain `Vec<String>` byte-string list to a
1350    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1351    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1352    /// fence already resolves through — would have had to be threaded
1353    /// through all four open-coded copies in lockstep or the layout
1354    /// gate, the shape validator, and the `feira nix` emit path would
1355    /// silently disagree on which executable paths a given [`Caixa`]
1356    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1357    /// satisfy layout while `feira nix` silently packaged a drifted
1358    /// other list, or vice versa). Lifting the resolution to a typed
1359    /// method on the substrate primitive means every downstream
1360    /// consumer of the caixa's per-`Caixa` executable-source surface
1361    /// reaches for exactly one typed dispatch — the resolver's accept-
1362    /// set migrates as a unit on any future axis addition.
1363    ///
1364    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1365    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1366    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1367    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1368    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1369    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1370    /// future lift closes onto (per the trio of code-surface list slots
1371    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1372    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1373    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1374    /// last unlifted code-surface slot). Sibling in shape to the peer
1375    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1376    /// (bc92bce), per-`:placement`
1377    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1378    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1379    /// (6c77e36), per-`:contratos`
1380    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1381    /// per-`:upgrade-from :instructions`
1382    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1383    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1384    /// typed-slot list axes, extended here to the outer top-level
1385    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1386    /// `&Vec<String>`) because every downstream consumer of the
1387    /// executable-source list treats it as a read-only sequence — the
1388    /// slice-view is the narrowest borrow that supports every
1389    /// present + roadmapped consumer (`.iter()`, `.len()`,
1390    /// `.is_empty()`) without leaking the backing `Vec`'s
1391    /// grow/push/reserve surface no consumer of the typed view
1392    /// reaches for (the storage-side `Vec` remains reachable through
1393    /// the `pub exe` field for the mutation-carrying serde
1394    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1395    /// to match the storage field's name; the accessor's identity
1396    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1397    /// docstring already carries.
1398    #[must_use]
1399    pub fn exe(&self) -> &[String] {
1400        self.exe.as_slice()
1401    }
1402
1403    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1404    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1405    /// of the top-level manifest's Servico-component axis keys off —
1406    /// returns the author-declared `:servicos` list verbatim as a
1407    /// `&[String]` slice-view over the same backing buffer the raw
1408    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1409    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1410    /// form supplies with an empty `()` when unset; the
1411    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1412    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1413    /// definitionally carries a `Vec<String>` slot — possibly empty —
1414    /// and the returned `&[String]` degenerates to an empty slice on
1415    /// that arm without any silent `None` collapse).
1416    ///
1417    /// The `:servicos` slot carries the universal-axis
1418    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1419    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1420    /// author-facing surface every `defcaixa` form supplies alongside
1421    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1422    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1423    /// Servico-facing renderer keys off) — the typed slot's
1424    /// `Vec<String>` accept-set (empty-per-entry rejected through
1425    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1426    /// non-sandboxed-relative-shape rejected through
1427    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1428    /// extension rejected through
1429    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1430    /// entry duplicate rejected through
1431    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1432    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1433    /// renderer entry-points, out-of-`servicos/`-directory paths
1434    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1435    /// `starts_with` fence) maps onto every load-bearing downstream
1436    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1437    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1438    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1439    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1440    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1441    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1442    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1443    /// that fences code-surface slots off from the two no-code kinds,
1444    /// [`Self::declared_foreign_code_slots`]'s
1445    /// `!self.servicos.is_empty()` arm on the
1446    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1447    /// `:servicos` code surface off from every non-Servico code-running
1448    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1449    /// walks each entry through the sandbox-relative / `.computeunit.
1450    /// yaml`-extension / cross-entry duplicate gates, the
1451    /// [`crate::require_single_servico`] V0 singularity gate every
1452    /// per-Servico renderer entry-point runs through
1453    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1454    /// `feira deploy` per-verb `first_servico_path` walk at
1455    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1456    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1457    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1458    /// per-Servico OCI packager, the future M4
1459    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1460    /// per-Servico OTel collector-config emit).
1461    ///
1462    /// Prior to this lift the `.servicos` field was accessed inline at
1463    /// five production sites — the compound-code-path `has_code =
1464    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1465    /// !caixa.servicos.is_empty()` OR-fold on the
1466    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1467    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1468    /// `caixa.servicos.is_empty()`
1469    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1470    /// per-entry `for p in &caixa.servicos`
1471    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1472    /// [`Self::declared_foreign_code_slots`]'s
1473    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1474    /// and the [`crate::require_single_servico`] V0 count gate's
1475    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1476    /// projection (both the accept-arm predicate and the
1477    /// diagnostic-carrying `ServicoCountMismatch { count }`
1478    /// projection) — five open-coded field-accesses across three
1479    /// crates that expressed no compile-time link back to the typed
1480    /// slot. A future extension of the `:servicos` axis to a richer
1481    /// component surface — a per-`:servicos` structured
1482    /// `ServicoEntry { path, world, capabilities }` at the storage
1483    /// layer once the substrate absorbs the per-component WIT-world +
1484    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1485    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1486    /// materializer enforces per-CR (the "cluster policy demands every
1487    /// Servico declare an explicit `:world`" arm), a promotion of the
1488    /// plain `Vec<String>` byte-string list to a richer
1489    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1490    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1491    /// `starts_with(servicos_dir)` fence and the
1492    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1493    /// already resolve through, a promotion of the V0 singleton
1494    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1495    /// component-model multi-world boundary — would have had to be
1496    /// threaded through all five open-coded copies in lockstep or the
1497    /// layout gate, the shape validator, the V0 count gate, and the
1498    /// `feira chart` / `feira deploy` entry-point walks would silently
1499    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1500    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1501    /// yaml")` would satisfy layout while `feira chart` silently
1502    /// packaged a drifted other list, or vice versa). Lifting the
1503    /// resolution to a typed method on the substrate primitive means
1504    /// every downstream consumer of the caixa's per-`Caixa`
1505    /// ComputeUnit-CR-source surface reaches for exactly one typed
1506    /// dispatch — the resolver's accept-set migrates as a unit on any
1507    /// future axis addition.
1508    ///
1509    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1510    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1511    /// projection pattern [`Self::autores`] (b5d813f) opened,
1512    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1513    /// (8a36c23) closed the universal-axis text-tag family of, and
1514    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1515    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1516    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1517    /// a substrate-canonical slice accessor, the trio of code-surface
1518    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1519    /// tuple carries is complete on the typed dispatch surface (the
1520    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1521    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1522    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1523    /// per-element accessor swap in isolation — a future companion lift
1524    /// promotes the tuple's element type to `&[String]` and threads the
1525    /// triple of typed dispatches through as a unit). Sibling in shape
1526    /// to the peer per-`:supervisor`
1527    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1528    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1529    /// (a6e18d7), per-`:membros`
1530    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1531    /// per-`:contratos`
1532    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1533    /// per-`:upgrade-from :instructions`
1534    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1535    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1536    /// typed-slot list axes, extended here to the outer top-level
1537    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1538    /// `&Vec<String>`) because every downstream consumer of the
1539    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1540    /// the slice-view is the narrowest borrow that supports every
1541    /// present + roadmapped consumer (`.iter()`, `.len()`,
1542    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1543    /// grow/push/reserve surface no consumer of the typed view reaches
1544    /// for (the storage-side `Vec` remains reachable through the
1545    /// `pub servicos` field for the mutation-carrying serde round-trip
1546    /// and per-test fixture-mutation paths, and for the
1547    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1548    /// homogeneous-element-type shape carries the raw field access
1549    /// until the trio-closure lift promotes the tuple as a unit).
1550    /// Named `servicos()` to match the storage field's name; the
1551    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1552    /// vocabulary the slot's docstring already carries.
1553    #[must_use]
1554    pub fn servicos(&self) -> &[String] {
1555        self.servicos.as_slice()
1556    }
1557
1558    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
1559    /// runtime-dependency-declaration-list slice-accessor every consumer
1560    /// of the top-level manifest's runtime-dep-graph axis keys off —
1561    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
1562    /// slice-view over the same backing buffer the raw
1563    /// `self.deps.as_slice()` field access borrows from. Empty-list-
1564    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
1565    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1566    /// derive folds an omitted `:deps` through `#[serde(default)]` to
1567    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1568    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
1569    /// degenerates to an empty slice on that arm without any silent
1570    /// `None` collapse).
1571    ///
1572    /// The `:deps` slot carries the universal-axis runtime dependency
1573    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1574    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1575    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
1576    /// every downstream resolver-facing artifact emits under) — the
1577    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
1578    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
1579    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
1580    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
1581    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
1582    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
1583    /// maps onto every load-bearing downstream consumer the substrate
1584    /// carries — the [`Self::validate_deps`] per-entry
1585    /// [`Dep::validate`] + within-list dedup walk at
1586    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
1587    /// cross-list self-reference gate at caixa-core/src/layout.rs that
1588    /// checks each entry against the caixa's own `:nome`, the
1589    /// caixa-resolver `for dep in &root.deps` closure walk at
1590    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
1591    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
1592    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
1593    /// caixa-crd/src/conversion.rs that materializes each entry into the
1594    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
1595    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1596    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
1597    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
1598    /// closure emit walk the caixa-resolver docstring roadmaps).
1599    ///
1600    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1601    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1602    /// sibling `:deps-dev` future lift closes on. Peer of the closed
1603    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
1604    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
1605    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
1606    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
1607    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
1608    /// pattern onto a novel element-type axis (`Dep` composite vs the
1609    /// prior sibling family's `String` scalar). Sibling in shape to the
1610    /// peer per-`:supervisor`
1611    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1612    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1613    /// (a6e18d7), per-`:membros`
1614    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1615    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1616    /// (0dcc926), and per-`:upgrade-from :instructions`
1617    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1618    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1619    /// typed-slot list axes, extended here to the outer top-level
1620    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
1621    /// (not `&Vec<Dep>`) because every downstream consumer of the
1622    /// runtime-dep list treats it as a read-only sequence — the slice-
1623    /// view is the narrowest borrow that supports every present +
1624    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1625    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1626    /// of the typed view reaches for (the storage-side `Vec` remains
1627    /// reachable through the `pub deps` field for the mutation-carrying
1628    /// serde round-trip and per-test fixture-mutation paths). Named
1629    /// `deps()` to match the storage field's name; the accessor's
1630    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1631    /// slot's docstring already carries.
1632    #[must_use]
1633    pub fn deps(&self) -> &[Dep] {
1634        self.deps.as_slice()
1635    }
1636
1637    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
1638    /// development-only-dependency-declaration-list slice-accessor every
1639    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
1640    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
1641    /// slice-view over the same backing buffer the raw
1642    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
1643    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
1644    /// form supplies with an empty `()` when unset; the
1645    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
1646    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1647    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
1648    /// the returned `&[Dep]` degenerates to an empty slice on that arm
1649    /// without any silent `None` collapse).
1650    ///
1651    /// The `:deps-dev` slot carries the universal-axis dev-only
1652    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
1653    /// the author-facing sibling of `:deps` that every `defcaixa` form
1654    /// supplies to declare tests / lint / bench closures the runtime
1655    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
1656    /// axis every downstream test-facing artifact emits under, matching
1657    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
1658    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
1659    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
1660    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
1661    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
1662    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
1663    /// within-list duplicate `:nome` rejected through
1664    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
1665    /// load-bearing downstream consumer the substrate carries — the
1666    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
1667    /// dedup walk at caixa-core/src/manifest.rs, the
1668    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
1669    /// gate at caixa-core/src/layout.rs that checks each entry against
1670    /// the caixa's own `:nome`, the caixa-resolver
1671    /// `for dep in &root.deps_dev` closure walk at
1672    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
1673    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
1674    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
1675    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
1676    /// overlay the M4 CR materializer resolves per-CR, the future
1677    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
1678    /// roadmaps).
1679    ///
1680    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1681    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1682    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
1683    /// jointly close the two-list dep-graph surface every downstream
1684    /// resolver-facing consumer keys off (runtime `:deps` +
1685    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
1686    /// pair the [`Self::validate_deps`] gate already walks in canonical
1687    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
1688    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
1689    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
1690    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
1691    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
1692    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
1693    /// dev-dep composite-element axis (`Dep` composite, matching the
1694    /// [`Self::deps`] element type). Sibling in shape to the peer
1695    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1696    /// (bc92bce), per-`:placement`
1697    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1698    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1699    /// (6c77e36), per-`:contratos`
1700    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1701    /// per-`:upgrade-from :instructions`
1702    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1703    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1704    /// typed-slot list axes, folded here to the outer top-level
1705    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
1706    /// (not `&Vec<Dep>`) because every downstream consumer of the
1707    /// dev-dep list treats it as a read-only sequence — the slice-view
1708    /// is the narrowest borrow that supports every present +
1709    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1710    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1711    /// of the typed view reaches for (the storage-side `Vec` remains
1712    /// reachable through the `pub deps_dev` field for the mutation-
1713    /// carrying serde round-trip and per-test fixture-mutation paths).
1714    /// Named `deps_dev()` to match the storage field's `snake_case` name;
1715    /// the kebab-case author-surface tag `:deps-dev` is the same axis
1716    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
1717    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1718    /// docstring already carries.
1719    #[must_use]
1720    pub fn deps_dev(&self) -> &[Dep] {
1721        self.deps_dev.as_slice()
1722    }
1723
1724    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
1725    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
1726    /// composite-reference accessor every consumer of the top-level
1727    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
1728    /// off — returns the author-declared `:limits` typed composite
1729    /// verbatim as an `Option<&LimitsSpec>` reference over the same
1730    /// backing storage the raw `self.limits.as_ref()` field access
1731    /// borrows from, with `None` naming the "no `:limits` block
1732    /// authored — every per-axis Lunatic-sandbox cap defers to the
1733    /// wasm-engine-default arm named on the per-axis
1734    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
1735    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
1736    /// docstrings" partition every downstream Servico-M2-overlay
1737    /// emitter treats as "emit nothing" and the sibling
1738    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
1739    /// treats as "skip the per-axis
1740    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
1741    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
1742    ///
1743    /// The outer `:limits` slot carries the M2 Servico-runtime typed
1744    /// composite — the load-bearing container of every Lunatic-shaped
1745    /// per-process wasm32-sandbox cap axis every long-running wasm
1746    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
1747    /// Lunatic per-process linear-memory / fuel / wall-clock /
1748    /// millicore cap primitives translated onto pleme-io's typed
1749    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
1750    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
1751    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
1752    /// chart both fan on). Every per-`:limits` axis threads through a
1753    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
1754    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
1755    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
1756    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
1757    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
1758    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
1759    /// consumer that reaches for a limits axis first passes through
1760    /// this outer accessor onto the composite and then dispatches
1761    /// onto the per-axis accessor — the two-level dispatch means
1762    /// every per-`:limits` reader now routes through a typed dispatch
1763    /// on the substrate primitive at both altitudes.
1764    ///
1765    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
1766    /// was accessed inline at three production sites — the
1767    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
1768    /// `if let Some(l) = &caixa.limits { … }` traversal head
1769    /// (caixa-core/src/layout.rs:882, which drives the per-axis
1770    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
1771    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
1772    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
1773    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
1774    /// [`LimitsSpec::validate`] fans onto), the
1775    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
1776    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
1777    /// head (caixa-core/src/render.rs:18504, which drives the
1778    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
1779    /// projection every `caixa-helm` / `caixa-flux` Servico values-
1780    /// block emitter fans on), and the
1781    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
1782    /// set enumerator's `self.limits.is_some()` presence probe
1783    /// (caixa-core/src/manifest.rs:1788, which drives the
1784    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
1785    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
1786    /// gate reads) — three open-coded outer-field accesses that
1787    /// expressed no compile-time link back to the typed slot at the
1788    /// [`Caixa`] altitude. A future extension of the `:limits` outer
1789    /// axis to a richer author surface (a multi-`:limits` list the M4
1790    /// CR materializer resolves per-CR at admission time so a Servico
1791    /// can expose a compute-heavy + IO-heavy limits pair, a per-
1792    /// cluster `:limits-overrides` slot the operator pins so a
1793    /// cluster-specific policy can tighten a caixa-declared cap
1794    /// without re-authoring the `caixa.lisp`, a promotion of the
1795    /// plain `Option<LimitsSpec>` to a richer
1796    /// `{static, dynamic}` partition once the wasm-engine's runtime-
1797    /// resolved dynamic-cap surface lands) would have had to be
1798    /// threaded through all three open-coded copies in lockstep or
1799    /// one consumer would silently disagree with the peers on which
1800    /// limits composite a given Caixa resolves to — the layout gate's
1801    /// per-axis bracket-dispatch seed reading the raw slot while the
1802    /// peer `servico_m2_overlay` emitter read an operator-resolved
1803    /// slot would silently split the build-time sandbox-shape gate
1804    /// from the runtime `ComputeUnit` CR emission gate, a three-
1805    /// consumer split at the layout gate, the M2 overlay emitter, and
1806    /// the declared-slot enumerator far from the source `caixa.lisp`
1807    /// with no field naming the limits-drift root cause. Lifting the
1808    /// resolution rule to a typed method on the substrate primitive
1809    /// means every downstream consumer of the caixa's per-`Caixa`
1810    /// Lunatic-sandboxing outer-composite surface reaches for exactly
1811    /// one typed dispatch — the resolver's accept-set migrates as a
1812    /// unit on any future axis addition.
1813    ///
1814    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
1815    /// composite-reference accessor — opens the outer-`Caixa`
1816    /// `Option<&Composite>` composite-reference projection pattern the
1817    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
1818    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
1819    /// [`crate::aplicacao::Placement`] / `:entrada`
1820    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
1821    /// fold on. Peer of the M3 mesh-slot outer-composite family the
1822    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
1823    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
1824    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
1825    /// accessors already close on the outer [`crate::AplicacaoSpec`]
1826    /// altitude — extends that "one typed dispatch on the substrate
1827    /// primitive, thin projections at each consumer" discipline onto
1828    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
1829    /// runtime slot family's outer-composite axis. Returns
1830    /// `Option<&LimitsSpec>` (not the owning composite by copy or
1831    /// clone) because every downstream consumer of the limits
1832    /// composite treats it as a read-only per-axis dispatch source —
1833    /// the reference-view is the narrowest borrow that supports every
1834    /// present + roadmapped consumer (per-axis accessor dispatch,
1835    /// `.is_empty()`-gated overlay projection, presence-probe early
1836    /// return on the "author-omitted `:limits` ⇒ engine-default
1837    /// applies" partition) without cloning the composite through
1838    /// every consumer's fast path. The `Option` half of the return-
1839    /// type preserves the load-bearing "author-omitted `:limits` ⇒
1840    /// engine-default applies" partition (not a default composite the
1841    /// downstream must reject on emptiness) — the accessor projects
1842    /// the raw `Option<LimitsSpec>` slot's presence bit through the
1843    /// reference-return unchanged. Named `limits()` to match the
1844    /// storage field's name verbatim and the tatara-lisp author-
1845    /// surface term (`:limits`) the field's own docstring already
1846    /// carries.
1847    #[must_use]
1848    pub fn limits(&self) -> Option<&LimitsSpec> {
1849        self.limits.as_ref()
1850    }
1851
1852    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
1853    /// composite OTP-`gen_server`-shaped callback-table optional-
1854    /// composite-reference accessor every consumer of the top-level
1855    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
1856    /// keys off — returns the author-declared `:behavior` typed
1857    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
1858    /// the same backing storage the raw `self.behavior.as_ref()` field
1859    /// access borrows from, with `None` naming the "no `:behavior`
1860    /// block authored — every per-callback OTP-shaped hook defers to
1861    /// the wasm-engine's runtime default arm named on the per-axis
1862    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
1863    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
1864    /// [`BehaviorSpec::on_state_change`] /
1865    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
1866    /// partition every downstream Servico-M2-overlay emitter treats as
1867    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
1868    /// per-`:behavior` shape gate treats as "skip the per-arm
1869    /// [`crate::behavior::BehaviorError`] refusal cascade + the
1870    /// per-callback on-disk `MissingEntry` existence check".
1871    ///
1872    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
1873    /// composite — the load-bearing container of every OTP-shaped
1874    /// per-Servico lifecycle-callback path axis every long-running wasm
1875    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
1876    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
1877    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
1878    /// translated onto pleme-io's typed `:behavior :on-init` /
1879    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
1880    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
1881    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
1882    /// chart both fan on). Every per-`:behavior` axis threads through a
1883    /// lifted per-callback accessor on the [`BehaviorSpec`] type
1884    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
1885    /// Every downstream consumer that reaches for a behavior axis
1886    /// first passes through this outer accessor onto the composite
1887    /// and then dispatches onto the per-callback accessor — the
1888    /// two-level dispatch means every per-`:behavior` reader now
1889    /// routes through a typed dispatch on the substrate primitive at
1890    /// both altitudes.
1891    ///
1892    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
1893    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
1894    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
1895    /// keys the "per-version `:state-change` instruction must have a
1896    /// `:on-state-change` callback" precondition off this accessor's
1897    /// composite (the callback-side counterpart to the
1898    /// `:upgrade-from :instructions :state-change :script` refusal at
1899    /// the appup-side). Threading that gate's traversal input through
1900    /// this accessor closes the cross-slot invariant on the substrate
1901    /// primitive, not on the raw field.
1902    ///
1903    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
1904    /// composite was accessed inline at four production sites — the
1905    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
1906    /// `if let Some(b) = &caixa.behavior { … }` traversal head
1907    /// (caixa-core/src/layout.rs:896, which drives the per-arm
1908    /// `BehaviorError` refusal cascade + the per-callback on-disk
1909    /// [`crate::LayoutError::MissingEntry`] existence check under
1910    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
1911    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
1912    /// cross-slot composition gate's `caixa.behavior.as_ref()`
1913    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
1914    /// drives the `:state-change` ↔ `:on-state-change` precondition
1915    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
1916    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
1917    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
1918    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
1919    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
1920    /// Servico values-block emitter fans on), and the
1921    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
1922    /// set enumerator's `self.behavior.is_some()` presence probe
1923    /// (caixa-core/src/manifest.rs:1919, which drives the
1924    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
1925    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
1926    /// gate reads) — four open-coded outer-field accesses that
1927    /// expressed no compile-time link back to the typed slot at the
1928    /// [`Caixa`] altitude. A future extension of the `:behavior`
1929    /// outer axis to a richer author surface (a per-callback overlay
1930    /// resolver the operator materializes at admission time so a
1931    /// cluster-specific policy can inject a per-callback tracing
1932    /// interceptor without re-authoring the `caixa.lisp`, a promotion
1933    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
1934    /// dynamic}` partition once a runtime-resolved behavior-swap
1935    /// surface lands, the M4 per-callback middleware chain the
1936    /// caixa-operator's per-Servico admission webhook keys off) would
1937    /// have had to be threaded through all four open-coded copies in
1938    /// lockstep or one consumer would silently disagree with the
1939    /// peers on which behavior composite a given Caixa resolves to —
1940    /// the layout gate's per-callback existence-check seed reading
1941    /// the raw slot while the peer `servico_m2_overlay` emitter read
1942    /// an operator-resolved slot would silently split the build-time
1943    /// callback-shape gate from the runtime `ComputeUnit` CR emission
1944    /// gate from the cross-slot `:state-change` composition gate from
1945    /// the M2 declared-slot enumerator, a four-consumer split far
1946    /// from the source `caixa.lisp` with no field naming the
1947    /// behavior-drift root cause. Lifting the resolution rule to a
1948    /// typed method on the substrate primitive means every downstream
1949    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
1950    /// composite surface reaches for exactly one typed dispatch — the
1951    /// resolver's accept-set migrates as a unit on any future axis
1952    /// addition.
1953    ///
1954    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
1955    /// composite-reference accessor — sibling to the opening
1956    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
1957    /// `Option<&Composite>` composite-reference sub-family, extends
1958    /// the "one typed dispatch on the substrate primitive, thin
1959    /// projections at each consumer" discipline onto the second of
1960    /// the three M2 Servico-runtime slots. The remaining
1961    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
1962    /// altitude — the M3 mesh-slot family (`:politicas`,
1963    /// `:placement`, `:entrada` — already closed on the inner
1964    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
1965    /// d32111c) — remain the future sibling lifts on the outer
1966    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
1967    /// the owning composite by copy or clone) because every
1968    /// downstream consumer of the behavior composite treats it as a
1969    /// read-only per-callback dispatch source — the reference-view is
1970    /// the narrowest borrow that supports every present + roadmapped
1971    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
1972    /// overlay projection, presence-probe early return on the
1973    /// "author-omitted `:behavior` ⇒ runtime-default applies"
1974    /// partition, cross-slot `:state-change` composition input)
1975    /// without cloning the composite through every consumer's fast
1976    /// path. The `Option` half of the return-type preserves the
1977    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
1978    /// applies" partition (not a default composite the downstream
1979    /// must reject on emptiness) — the accessor projects the raw
1980    /// `Option<BehaviorSpec>` slot's presence bit through the
1981    /// reference-return unchanged. Named `behavior()` to match the
1982    /// storage field's name verbatim and the tatara-lisp author-
1983    /// surface term (`:behavior`) the field's own docstring already
1984    /// carries.
1985    #[must_use]
1986    pub fn behavior(&self) -> Option<&crate::BehaviorSpec> {
1987        self.behavior.as_ref()
1988    }
1989
1990    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
1991    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
1992    /// reference accessor every consumer of the top-level manifest's
1993    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
1994    /// reader keys off — returns the author-declared `:politicas` typed
1995    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
1996    /// same backing storage the raw `self.politicas.as_ref()` field
1997    /// access borrows from, with `None` naming the "no `:politicas`
1998    /// block authored — every per-axis mesh-policy scalar defers to the
1999    /// cluster-default arm named on the per-axis
2000    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2001    /// [`crate::aplicacao::MeshPolicy::retries`] /
2002    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2003    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2004    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2005    /// docstrings" partition every downstream caixa-mesh /
2006    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2007    /// "emit no per-`:politicas` overlay" and the sibling
2008    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2009    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2010    /// arm.
2011    ///
2012    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2013    /// Aplicacao typed composite — the load-bearing container of every
2014    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2015    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2016    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2017    /// composite; §V — the "no infinite blocking" per-call deadline +
2018    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2019    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2020    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2021    /// threads through a lifted per-slot accessor on the
2022    /// [`crate::aplicacao::MeshPolicy`] type: the
2023    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2024    /// mTLS-enforcement toggle, the
2025    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2026    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2027    /// (7073d0f) Gateway-API per-call deadline, the
2028    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2029    /// Envoy-outlier-detection composite. Every downstream consumer
2030    /// that reaches for a mesh-policy axis first passes through this
2031    /// outer accessor onto the composite and then dispatches onto the
2032    /// per-axis accessor — the two-level dispatch means every per-
2033    /// `:politicas` reader now routes through a typed dispatch on the
2034    /// substrate primitive at both altitudes.
2035    ///
2036    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2037    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2038    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2039    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2040    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2041    /// composite whether or not the author declared the outer slot.
2042    /// The outer accessor preserves the "author-omitted vs authored-
2043    /// empty" partition the inner accessor's `is_empty()`-gated
2044    /// renderer overlay collapses — routing the presence bit through
2045    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2046    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2047    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2048    ///
2049    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2050    /// composite was accessed inline at two production sites — the
2051    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2052    /// `self.politicas.clone().unwrap_or_default()` traversal head
2053    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2054    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2055    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2056    /// then observes), and the [`Self::declared_mesh_slots`] M3
2057    /// declared-slot-set enumerator's `self.politicas.is_some()`
2058    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2059    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2060    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2061    /// coherence gate reads) — two open-coded outer-field accesses
2062    /// that expressed no compile-time link back to the typed slot at
2063    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2064    /// outer axis to a richer author surface (a per-cluster
2065    /// `:politicas-overrides` slot the operator materializes at
2066    /// admission time so a cluster-specific policy can tighten the
2067    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2068    /// promotion of the plain `Option<MeshPolicy>` to a richer
2069    /// `{static, dynamic}` partition once the M4 per-edge
2070    /// contrato-scoped policy-override surface lands, the M5 traffic-
2071    /// shaping composition the caixa-operator's per-Aplicacao mesh
2072    /// admission webhook keys off) would have had to be threaded
2073    /// through both open-coded copies in lockstep or the Aplicacao-
2074    /// composition seed's default-fold arm would silently disagree
2075    /// with the M3 declared-slot enumerator on which policy composite
2076    /// a given Caixa resolves to — the seed reading an operator-
2077    /// resolved slot while the enumerator's presence probe read the
2078    /// raw slot would silently split the build-time mesh-artifact
2079    /// emission gate from the M3 declared-slot enumerator's kind-
2080    /// coherence gate, a two-consumer split far from the source
2081    /// `caixa.lisp` with no field naming the policy-drift root cause.
2082    /// Lifting the resolution rule to a typed method on the substrate
2083    /// primitive means every downstream consumer of the caixa's per-
2084    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2085    /// reaches for exactly one typed dispatch — the resolver's
2086    /// accept-set migrates as a unit on any future axis addition.
2087    ///
2088    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2089    /// composite-reference accessor — sibling to the opening
2090    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2091    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2092    /// reference sub-family, extends the "one typed dispatch on the
2093    /// substrate primitive, thin projections at each consumer"
2094    /// discipline onto the first of the three M3 mesh-slot axes.
2095    /// Peer of the closed inner mesh-slot outer-composite family the
2096    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2097    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2098    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2099    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2100    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2101    /// mesh-slot arm of the composite-reference family the remaining
2102    /// two axes (`:placement`, `:entrada`) fold onto in future
2103    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2104    /// composite by copy or clone) because every downstream consumer
2105    /// of the mesh-policy composite treats it as a read-only per-axis
2106    /// dispatch source — the reference-view is the narrowest borrow
2107    /// that supports every present + roadmapped consumer (per-axis
2108    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2109    /// presence-probe early return on the "author-omitted `:politicas`
2110    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2111    /// seed's default-fold arm) without cloning the composite through
2112    /// every consumer's fast path. The `Option` half of the return-
2113    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2114    /// cluster-default applies" partition (not a default composite
2115    /// the downstream must reject on emptiness) — the accessor
2116    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2117    /// through the reference-return unchanged. Named `politicas()` to
2118    /// match the storage field's name verbatim and the tatara-lisp
2119    /// author-surface term (`:politicas`) the field's own docstring
2120    /// already carries.
2121    #[must_use]
2122    pub fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2123        self.politicas.as_ref()
2124    }
2125
2126    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2127    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2128    /// reference accessor every consumer of the top-level manifest's
2129    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2130    /// reader keys off — returns the author-declared `:placement` typed
2131    /// composite verbatim as an `Option<&Placement>` reference over the
2132    /// same backing storage the raw `self.placement.as_ref()` field
2133    /// access borrows from, with `None` naming the "no `:placement`
2134    /// block authored — every per-axis placement scalar defers to the
2135    /// cluster-default arm named on the per-axis
2136    /// [`crate::aplicacao::Placement::estrategia`] /
2137    /// [`crate::aplicacao::Placement::clusters`] /
2138    /// [`crate::aplicacao::Placement::affinity`] /
2139    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2140    /// docstrings" partition every downstream caixa-mesh /
2141    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2142    /// "emit no per-`:placement` overlay" and the sibling
2143    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2144    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2145    ///
2146    /// The outer `:placement` slot carries the M3 mesh-slot per-
2147    /// Aplicacao typed distribution composite — the load-bearing
2148    /// container of every where-does-this-Aplicacao-run axis every
2149    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2150    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2151    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2152    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2153    /// Aplicacao's typed distribution composite; §V CSE invariants —
2154    /// "distribution is a first-class typed composite, not a runtime
2155    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2156    /// typed inter-Servico contrato-edge overlay the per-cluster
2157    /// mesh renderer keys off). Every per-`:placement` axis threads
2158    /// through a lifted per-slot accessor on the
2159    /// [`crate::aplicacao::Placement`] type: the
2160    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2161    /// MESH-COMPOSITION distribution-strategy scalar, the
2162    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2163    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2164    /// M3-Adaptive-compression-hint optional-scalar, and the
2165    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2166    /// sharding extractor-expression optional-scalar. Every downstream
2167    /// consumer that reaches for a placement axis first passes through
2168    /// this outer accessor onto the composite and then dispatches onto
2169    /// the per-axis accessor — the two-level dispatch means every per-
2170    /// `:placement` reader now routes through a typed dispatch on the
2171    /// substrate primitive at both altitudes.
2172    ///
2173    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2174    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2175    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2176    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2177    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2178    /// whether or not the author declared the outer slot. The outer
2179    /// accessor preserves the "author-omitted vs authored-empty" partition
2180    /// the inner accessor collapses at the cluster-default fold —
2181    /// routing the presence bit through this accessor keeps the
2182    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2183    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2184    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2185    /// dispatch.
2186    ///
2187    /// Prior to this lift the `.placement` `Option<Placement>`
2188    /// composite was accessed inline at two production sites — the
2189    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2190    /// `self.placement.clone().unwrap_or_default()` traversal head
2191    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2192    /// the [`crate::aplicacao::Placement::default`] cluster-default
2193    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2194    /// then observes), and the [`Self::declared_mesh_slots`] M3
2195    /// declared-slot-set enumerator's `self.placement.is_some()`
2196    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2197    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2198    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2199    /// coherence gate reads) — two open-coded outer-field accesses
2200    /// that expressed no compile-time link back to the typed slot at
2201    /// the [`Caixa`] altitude. A future extension of the `:placement`
2202    /// outer axis to a richer author surface (a per-cluster
2203    /// `:placement-overrides` slot the operator materializes at
2204    /// admission time so a cluster-specific placement can tighten the
2205    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2206    /// per-tenant placement-alias table the M4
2207    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2208    /// per-CR at admission time, a promotion of the plain
2209    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2210    /// once Orleans-style virtual-actor dynamic placement comes into
2211    /// typed scope) would have had to be threaded through both open-
2212    /// coded copies in lockstep or the Aplicacao-composition seed's
2213    /// default-fold arm would silently disagree with the M3 declared-
2214    /// slot enumerator on which distribution composite a given Caixa
2215    /// resolves to — the seed reading an operator-resolved slot while
2216    /// the enumerator's presence probe read the raw slot would
2217    /// silently split the build-time distribution-artifact emission
2218    /// gate from the M3 declared-slot enumerator's kind-coherence
2219    /// gate, a two-consumer split far from the source `caixa.lisp`
2220    /// with no field naming the distribution-drift root cause.
2221    /// Lifting the resolution rule to a typed method on the substrate
2222    /// primitive means every downstream consumer of the caixa's per-
2223    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2224    /// reaches for exactly one typed dispatch — the resolver's
2225    /// accept-set migrates as a unit on any future axis addition.
2226    ///
2227    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2228    /// composite-reference accessor — sibling to the opening
2229    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2230    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2231    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2232    /// composite-reference sub-family, folds on the "one typed
2233    /// dispatch on the substrate primitive, thin projections at each
2234    /// consumer" discipline extended onto the second of the three M3
2235    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2236    /// composite family the sibling
2237    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2238    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2239    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2240    /// accessor pins already close on the inner
2241    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2242    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2243    /// [`Self::politicas`] opened, extending the discipline onto the
2244    /// second of the three M3 mesh-slot axes. The remaining M3
2245    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2246    /// discipline in the final sibling lift, closing the outer top-
2247    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2248    /// Returns `Option<&Placement>` (not the owning composite by copy
2249    /// or clone) because every downstream consumer of the placement
2250    /// composite treats it as a read-only per-axis dispatch source —
2251    /// the reference-view is the narrowest borrow that supports every
2252    /// present + roadmapped consumer (per-axis accessor dispatch,
2253    /// serde composite-serialization on the programs.yaml overlay,
2254    /// presence-probe early return on the "author-omitted `:placement`
2255    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2256    /// seed's default-fold arm) without cloning the composite through
2257    /// every consumer's fast path. The `Option` half of the return-
2258    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2259    /// cluster-default applies" partition (not a default composite
2260    /// the downstream must reject on emptiness) — the accessor
2261    /// projects the raw `Option<Placement>` slot's presence bit
2262    /// through the reference-return unchanged. Named `placement()` to
2263    /// match the storage field's name verbatim and the tatara-lisp
2264    /// author-surface term (`:placement`) the field's own docstring
2265    /// already carries.
2266    #[must_use]
2267    pub fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2268        self.placement.as_ref()
2269    }
2270
2271    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2272    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2273    /// composite-reference accessor every consumer of the top-level
2274    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2275    /// composite reader keys off — returns the author-declared
2276    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2277    /// reference over the same backing storage the raw
2278    /// `self.entrada.as_ref()` field access borrows from, with `None`
2279    /// naming the "no `:entrada` block authored — this Aplicacao is
2280    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2281    /// partition every downstream caixa-mesh Gateway-API artifact
2282    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2283    /// backend for this Aplicacao" and the sibling
2284    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2285    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2286    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2287    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2288    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2289    /// the same `Option<&Entrada>` presence bit unchanged).
2290    ///
2291    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2292    /// Aplicacao typed external-gateway composite — the load-bearing
2293    /// container of every how-does-the-outside-world-reach-this-
2294    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2295    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2296    /// external-entry composite; §V CSE invariants — "the external
2297    /// gateway is a first-class typed composite, not a per-Servico
2298    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2299    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2300    /// API renderer keys off). Every per-`:entrada` axis threads
2301    /// through a lifted per-slot accessor on the
2302    /// [`crate::aplicacao::Entrada`] type: the
2303    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2304    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2305    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2306    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2307    /// backend `trigger.service.port` scalar, and the
2308    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2309    /// resolver every HTTPRoute-aware renderer consumes. Every
2310    /// downstream consumer that reaches for an entry axis first passes
2311    /// through this outer accessor onto the composite and then
2312    /// dispatches onto the per-axis accessor — the two-level dispatch
2313    /// means every per-`:entrada` reader now routes through a typed
2314    /// dispatch on the substrate primitive at both altitudes.
2315    ///
2316    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2317    /// seed: the Aplicacao-view builder forwards the outer `Option`
2318    /// arm verbatim (no default fold — `:entrada` is inherently
2319    /// optional; a cluster-internal Aplicacao has no external gateway
2320    /// at all, not "an external gateway that defaults to nothing"), so
2321    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2322    /// `Option<&Entrada>`-return accessor observes the same presence
2323    /// bit whether or not the author declared the outer slot. Routing
2324    /// the presence bit through this accessor keeps the
2325    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2326    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2327    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2328    /// hostname/backend/path emission dispatch.
2329    ///
2330    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2331    /// was accessed inline at two production sites — the
2332    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2333    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2334    /// which drives the forward onto the peer inner
2335    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2336    /// Gateway-API fan-out then observes), and the
2337    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2338    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2339    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2340    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2341    /// kind-coherence gate reads) — two open-coded outer-field
2342    /// accesses that expressed no compile-time link back to the typed
2343    /// slot at the [`Caixa`] altitude. A future extension of the
2344    /// `:entrada` outer axis to a richer author surface (a per-cluster
2345    /// `:entrada-overrides` slot the operator materializes at admission
2346    /// time so a cluster-specific hostname can pin the caixa-declared
2347    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2348    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2349    /// CR materializer resolves per-CR at admission time, a promotion
2350    /// of the plain `Option<Entrada>` to a richer
2351    /// `{public, private, internal}` partition once Cilium-identity-
2352    /// scoped internal gateways come into typed scope) would have had
2353    /// to be threaded through both open-coded copies in lockstep or the
2354    /// Aplicacao-composition seed's forward arm would silently
2355    /// disagree with the M3 declared-slot enumerator on which external-
2356    /// gateway composite a given Caixa resolves to — the seed reading
2357    /// an operator-resolved slot while the enumerator's presence probe
2358    /// read the raw slot would silently split the build-time gateway-
2359    /// artifact emission gate from the M3 declared-slot enumerator's
2360    /// kind-coherence gate, a two-consumer split far from the source
2361    /// `caixa.lisp` with no field naming the entry-drift root cause.
2362    /// Lifting the resolution rule to a typed method on the substrate
2363    /// primitive means every downstream consumer of the caixa's per-
2364    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2365    /// surface reaches for exactly one typed dispatch — the resolver's
2366    /// accept-set migrates as a unit on any future axis addition.
2367    ///
2368    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2369    /// return composite-reference accessor — closes the outer-`Caixa`
2370    /// `Option<&Composite>` composite-reference sub-family opened by
2371    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2372    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2373    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2374    /// folds on the "one typed dispatch on the substrate primitive,
2375    /// thin projections at each consumer" discipline extended onto the
2376    /// third and final M3 mesh-slot axis. Peer of the closed inner
2377    /// mesh-slot outer-composite family the sibling
2378    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2379    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2380    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2381    /// accessor pins already close on the inner
2382    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2383    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2384    /// altitudes of the outer-composite reference-return discipline
2385    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2386    /// slot presence) now carry the full five-arm accept-set behind a
2387    /// typed dispatch on the substrate primitive. Returns
2388    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2389    /// because every downstream consumer of the entrada composite
2390    /// treats it as a read-only per-axis dispatch source — the
2391    /// reference-view is the narrowest borrow that supports every
2392    /// present + roadmapped consumer (per-axis accessor dispatch,
2393    /// serde composite-serialization on the programs.yaml overlay,
2394    /// presence-probe early return on the "author-omitted `:entrada`
2395    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2396    /// seed's forward arm) without cloning the composite through every
2397    /// consumer's fast path. The `Option` half of the return-type
2398    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2399    /// cluster-internal Aplicacao" partition (not a default composite
2400    /// the downstream must reject on emptiness — a cluster-internal
2401    /// Aplicacao has no external gateway at all, not "a default gateway
2402    /// that emits nothing"); the accessor projects the raw
2403    /// `Option<Entrada>` slot's presence bit through the reference-
2404    /// return unchanged. Named `entrada()` to match the storage field's
2405    /// name verbatim and the tatara-lisp author-surface term
2406    /// (`:entrada`) the field's own docstring already carries.
2407    #[must_use]
2408    pub fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
2409        self.entrada.as_ref()
2410    }
2411
2412    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
2413    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
2414    /// an `Option<&CiRun>`, borrowed from the typed slot's own
2415    /// `Option<CiRun>` storage. `None` when the slot is absent (every
2416    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
2417    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
2418    /// not silently accepted).
2419    ///
2420    /// Named `ci()` to match the storage field's name and the
2421    /// tatara-lisp author surface (`:ci`); mirrors the sibling
2422    /// `Option<&Composite>` accessors on this same `Caixa` altitude
2423    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
2424    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
2425    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
2426    /// at every consumer.
2427    #[must_use]
2428    pub fn ci(&self) -> Option<&canteiro_types::CiRun> {
2429        self.ci.as_ref()
2430    }
2431
2432    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
2433    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
2434    /// accessor every consumer of the top-level manifest's per-Supervisor
2435    /// restart-strategy axis keys off — returns the author-declared
2436    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
2437    /// `Copy`-projected from the typed slot's own
2438    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
2439    /// (`:estrategia` is a flat-spread supervisor-only slot every
2440    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
2441    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2442    /// still omit to defer to [`RestartStrategy::default`] —
2443    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
2444    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
2445    /// [`SupervisorSpec::default`]-inherited strategy without any silent
2446    /// promotion to a fresh explicit variant at the accessor boundary).
2447    ///
2448    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
2449    /// restart-strategy discriminant every substrate-side per-Supervisor
2450    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
2451    /// closed-set `one_for_one | one_for_all | rest_for_one |
2452    /// simple_one_for_one` algebra translated onto pleme-io's typed
2453    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
2454    /// slot algebra the operator's hierarchical reconciliation scheduler
2455    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
2456    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
2457    /// supervisor slots are flat on Caixa (vs nested under a
2458    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
2459    /// level of nesting"), so the accessor's altitude is the outer
2460    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
2461    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
2462    /// (eafb619) accessor keys off. The two typed axes — the outer
2463    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
2464    /// (author-omitted arm carried as `None`) and the inner post-
2465    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
2466    /// (`Option` collapsed through the [`Self::supervisor_view`]
2467    /// `unwrap_or_default()` fold) — now share one accessor discipline for
2468    /// the shared substrate concept "the author-declared OTP-shaped
2469    /// sibling-restart-strategy variant that partitions the downstream
2470    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
2471    /// `None` arm is the pre-composition presence bit every declared-slot
2472    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
2473    /// inner-altitude non-`Option` `RestartStrategy` is the post-
2474    /// composition partition-dispatch input every strategy-arm consumer
2475    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
2476    /// Supervisor sibling-restart branch, the future M4
2477    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
2478    /// webhook) fans on.
2479    ///
2480    /// Prior to this lift the `.estrategia` field was accessed inline at
2481    /// two production sites in `caixa-core/src/manifest.rs` — the
2482    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
2483    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
2484    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2485    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
2486    /// `SupervisorSpec` construction site at `estrategia:
2487    /// self.estrategia.unwrap_or_default()` (which composes the flat-
2488    /// spread outer author-surface `Option<RestartStrategy>` onto the
2489    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
2490    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
2491    /// coded field-accesses that expressed no compile-time link back to
2492    /// the typed slot. A future extension of the outer `:estrategia` axis
2493    /// to a richer author surface (a per-cluster strategy override the
2494    /// operator pins through a future `:estrategia-overrides` overlay the
2495    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
2496    /// a per-tenant strategy-alias table the M4 CR materializer resolves
2497    /// per-CR, a per-Supervisor dynamic strategy derivation the future
2498    /// adaptive-supervision engine computes from child-failure-history
2499    /// topology, a per-child-cohort strategy split the future
2500    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
2501    /// absorption roadmap acknowledges, a promotion of the plain
2502    /// `Option<RestartStrategy>` to a richer
2503    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
2504    /// operator-resolved overlay lands) would have had to be threaded
2505    /// through both open-coded copies in lockstep or the enumerator's
2506    /// presence probe and the composition site's `unwrap_or_default()`
2507    /// fold would silently disagree on which strategy a given [`Caixa`]
2508    /// resolves to (an author's `:estrategia OneForAll` would satisfy
2509    /// the enumerator's presence probe while the composition site
2510    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
2511    /// the resolution rule to a typed method on the substrate primitive
2512    /// means every downstream consumer of the caixa's per-`Caixa` outer-
2513    /// altitude sibling-restart-strategy surface reaches for exactly one
2514    /// typed dispatch — the resolver's accept-set migrates as a unit on
2515    /// any future axis addition.
2516    ///
2517    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2518    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2519    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
2520    /// projection pattern the sibling per-`Caixa` `:max-restarts`
2521    /// `Option<u32>` and (through the future duration-newtype landing)
2522    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
2523    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
2524    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
2525    /// the post-composition [`SupervisorSpec`] altitude — same "one
2526    /// typed dispatch on the substrate primitive, thin projections at
2527    /// each consumer" discipline extended onto the pre-composition outer
2528    /// author-surface [`Caixa`] altitude for the same OTP-shaped
2529    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
2530    /// `Option<&Composite>` composite-reference family the sibling
2531    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
2532    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
2533    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
2534    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
2535    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
2536    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
2537    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2538    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
2539    /// pins on the inner-altitude per-`:placement` composite. Named
2540    /// `estrategia()` to match the storage field's name and the
2541    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
2542    /// / per-[`crate::aplicacao::Placement`] peer
2543    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
2544    /// verbatim; the accessor's identity name maps onto the canonical
2545    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
2546    /// docstring already carries.
2547    #[must_use]
2548    pub fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
2549        self.estrategia
2550    }
2551
2552    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
2553    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
2554    /// scalar accessor every consumer of the top-level manifest's per-
2555    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
2556    /// returns the author-declared `:max-restarts` typed `Option<u32>`
2557    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
2558    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
2559    /// accessor returns by value; no borrow of `&self` past the call).
2560    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
2561    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
2562    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2563    /// still omit to defer to the [`Self::supervisor_view`]
2564    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
2565    ///
2566    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
2567    /// `MaxIntensity` restart-budget count that pairs with the sibling
2568    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
2569    /// restart-intensity ratio the supervisor trips its own escalation on
2570    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
2571    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
2572    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
2573    /// reconciliation scheduler fans on). The slot is *flat-spread* on
2574    /// the outer top-level `Caixa` (per the field-shape docstring at
2575    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
2576    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
2577    /// accessor's altitude is the outer [`Caixa`] surface rather than the
2578    /// composed [`SupervisorSpec`] altitude the sibling
2579    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
2580    /// off. The two typed axes — the outer author-surface `Option<u32>`
2581    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
2582    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
2583    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
2584    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
2585    /// shared substrate concept "the author-declared OTP-shaped
2586    /// restart-budget count every downstream per-Supervisor consumer's
2587    /// restart-intensity budget-vs-count comparator fans on".
2588    ///
2589    /// Prior to this lift the `.max_restarts` field was accessed inline
2590    /// at two production sites in `caixa-core/src/manifest.rs` — the
2591    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
2592    /// presence-probe arm at `if self.max_restarts.is_some()` (which
2593    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
2594    /// kind-coherence gate's per-slot label push) and the
2595    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
2596    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
2597    /// flat-spread outer author-surface `Option<u32>` onto the inner
2598    /// post-composition [`SupervisorSpec`] `u32` field the
2599    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
2600    /// coded field-accesses that expressed no compile-time link back to
2601    /// the typed slot. A future extension of the outer `:max-restarts`
2602    /// axis to a richer author surface (a per-cluster restart-budget
2603    /// override the operator pins through a future `:max-restarts-overrides`
2604    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
2605    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
2606    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
2607    /// budget derivation the future adaptive-supervision engine computes
2608    /// from child-failure-history topology, a promotion of the plain
2609    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
2610    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
2611    /// per-child-cohort roadmap lands) would have had to be threaded
2612    /// through both open-coded copies in lockstep or the enumerator's
2613    /// presence probe and the composition site's `unwrap_or(5)` fold
2614    /// would silently disagree on which restart-budget a given [`Caixa`]
2615    /// resolves to (an author's `:max-restarts 10` would satisfy the
2616    /// enumerator's presence probe while the composition site silently
2617    /// composed the OTP-canonical `5`, or vice versa). Lifting the
2618    /// resolution rule to a typed method on the substrate primitive means
2619    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
2620    /// restart-budget-count surface reaches for exactly one typed dispatch
2621    /// — the resolver's accept-set migrates as a unit on any future axis
2622    /// addition.
2623    ///
2624    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2625    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2626    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
2627    /// projection pattern the sibling per-`Caixa`
2628    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
2629    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
2630    /// Peer of the inner-altitude
2631    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
2632    /// on the post-composition [`SupervisorSpec`] altitude — same "one
2633    /// typed dispatch on the substrate primitive, thin projections at
2634    /// each consumer" discipline extended onto the pre-composition outer
2635    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
2636    /// shaped restart-budget-count axis. Named `max_restarts()` to match
2637    /// the storage field's name and the per-[`SupervisorSpec`] peer
2638    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
2639    /// discipline verbatim; the accessor's identity maps onto the
2640    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
2641    /// field's docstring already carries.
2642    #[must_use]
2643    pub const fn max_restarts(&self) -> Option<u32> {
2644        self.max_restarts
2645    }
2646
2647    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
2648    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
2649    /// denominator raw-duration-string scalar accessor every consumer of
2650    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
2651    /// window axis keys off — returns the author-declared `:restart-window`
2652    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
2653    /// from the typed slot's own `Option<String>` storage. `None` when
2654    /// the slot is absent (the canonical "never reset — every restart
2655    /// across the supervisor's lifetime counts against the sibling
2656    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
2657    /// `defcaixa` carries by `#[serde(default)]` and every
2658    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
2659    /// [`Self::supervisor_view`] `restart_window: None` composition
2660    /// through the [`crate::supervisor::duration_codec::parse`] soft-
2661    /// swallow `.and_then(|s| … .ok())` fold).
2662    ///
2663    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
2664    /// shaped `Period` sliding-observation-interval duration string that
2665    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
2666    /// budget count to form the `MaxIntensity / Period` restart-intensity
2667    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
2668    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
2669    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
2670    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
2671    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
2672    /// holds an `Option<Duration>` routed through the shared
2673    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
2674    /// — so the outer altitude's accessor returns `Option<&str>` (raw
2675    /// authoring surface) while the inner altitude's
2676    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
2677    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
2678    /// is closed by the sibling [`Self::validate_restart_window`] gate
2679    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
2680    /// the offending value; the view-construction path
2681    /// [`Self::supervisor_view`] soft-swallows the same parse error to
2682    /// `None` to keep the view best-effort.
2683    ///
2684    /// Prior to this lift the `.restart_window` field was accessed inline
2685    /// at three production sites in `caixa-core/src/manifest.rs` — the
2686    /// [`Self::declared_supervisor_slots`]
2687    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
2688    /// `if self.restart_window.is_some()` (which drives the
2689    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2690    /// coherence gate's per-slot label push), the
2691    /// [`Self::validate_restart_window`] `let Some(s) =
2692    /// self.restart_window.as_deref()` empty-and-shape gate binding
2693    /// (which folds the raw string through the shared
2694    /// [`crate::supervisor::duration_codec::parse`] to surface
2695    /// [`ManifestError::RestartWindowMalformed`] naming the offending
2696    /// value), and the [`Self::supervisor_view`] `self.restart_window
2697    /// .as_deref().and_then(…)` view-construction fold (which composes
2698    /// the flat-spread outer author-surface `Option<String>` onto the
2699    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
2700    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
2701    /// three open-coded field-accesses that expressed no compile-time
2702    /// link back to the typed slot. A future extension of the outer
2703    /// `:restart-window` axis to a richer author surface (a per-cluster
2704    /// window override, a per-tenant window-alias table, a per-Supervisor
2705    /// dynamic window derivation the future adaptive-supervision engine
2706    /// computes from child-failure-history topology, a promotion of the
2707    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
2708    /// once the future author-surface parser lands at the [`Caixa`]
2709    /// altitude and the raw-string form is retired) would have had to be
2710    /// threaded through every open-coded copy in lockstep or the three
2711    /// consumers would silently disagree on which raw string a given
2712    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
2713    /// method on the substrate primitive means every downstream consumer
2714    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
2715    /// string surface reaches for exactly one typed dispatch — the
2716    /// resolver's accept-set migrates as a unit on any future axis
2717    /// addition.
2718    ///
2719    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
2720    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
2721    /// spread projection pattern the sibling per-`Caixa`
2722    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
2723    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
2724    /// the sub-family onto the sibling `Option<&str>` raw-duration-
2725    /// string arm (the outer altitude's raw-string form; the inner
2726    /// altitude's parsed [`Duration`] form is the peer
2727    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
2728    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
2729    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
2730    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
2731    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
2732    /// sub-family already carries — same "one typed dispatch on the
2733    /// substrate primitive, thin projections at each consumer"
2734    /// discipline extended onto the M2 supervisor-tree flat-spread
2735    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
2736    /// to match the storage field's name and the per-[`SupervisorSpec`]
2737    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
2738    /// method-name discipline verbatim; the accessor's identity maps
2739    /// onto the canonical OTP-shape supervision vocabulary the
2740    /// `:restart-window` field's docstring already carries.
2741    #[must_use]
2742    pub fn restart_window(&self) -> Option<&str> {
2743        self.restart_window.as_deref()
2744    }
2745
2746    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
2747    /// outer-composite OTP-appup-shaped per-prior-version migration-
2748    /// entry-list slice accessor every consumer of the top-level
2749    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
2750    /// slice-view keys off — returns the author-declared `:upgrade-from`
2751    /// typed `Vec<UpgradeFromEntry>` verbatim as a
2752    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
2753    /// the raw `self.upgrade_from.as_slice()` field access borrows
2754    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
2755    /// arm every `defcaixa` without an `:upgrade-from` block carries;
2756    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
2757    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
2758    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
2759    /// possibly empty — and the returned `&[UpgradeFromEntry]`
2760    /// degenerates to an empty slice on that arm without any silent
2761    /// `None` collapse).
2762    ///
2763    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
2764    /// migration block — the load-bearing container of every per-
2765    /// prior-`:versao` migration-instruction list the wasm-operator
2766    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
2767    /// `.appup` per-prior-version `LoadModule | StateChange |
2768    /// SoftPurge | Purge | Restart` instruction algebra translated
2769    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
2770    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
2771    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
2772    /// threads through a lifted per-entry accessor on the
2773    /// [`UpgradeFromEntry`] type: the
2774    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
2775    /// version scalar accessor and the
2776    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
2777    /// return per-entry instruction-list accessor (0137e5a). Every
2778    /// downstream consumer of the hot-upgrade path first passes
2779    /// through this outer accessor onto the slice and then dispatches
2780    /// per-entry through the inner accessors — the two-level dispatch
2781    /// means every per-`:upgrade-from` reader now routes through a
2782    /// typed dispatch on the substrate primitive at both altitudes.
2783    ///
2784    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
2785    /// slot was accessed inline at production sites across three
2786    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
2787    /// enumerator's `self.upgrade_from.is_empty()` presence probe
2788    /// (caixa-core/src/manifest.rs, which drives the
2789    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
2790    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2791    /// gate reads), the [`crate::StandardLayout::verify`] per-
2792    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
2793    /// layout.rs, which fans onto the
2794    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
2795    /// cross-entry duplicate gate, the
2796    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
2797    /// SemVer-precedence cross-slot gate, the
2798    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2799    /// `:state-change` ↔ `:on-state-change` cross-slot composition
2800    /// gate, and the per-instruction script-path existence-probe walk
2801    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
2802    /// resolve every declared migration script against the layout
2803    /// root), and the [`crate::render::servico_m2_overlay`] per-
2804    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
2805    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
2806    /// projection (caixa-core/src/render.rs, which drives the
2807    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
2808    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
2809    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
2810    /// A future extension of the outer `:upgrade-from` axis (a per-
2811    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
2812    /// resolves at admission time so a cluster-specific migration
2813    /// policy can tighten a caixa-declared step without re-authoring
2814    /// the `caixa.lisp`, promotion of the plain
2815    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
2816    /// partition once runtime-resolved hot-upgrade instructions land,
2817    /// per-entry priority annotation once multi-strategy fan-out
2818    /// lands) would have had to be threaded through all six open-
2819    /// coded copies in lockstep or one consumer would silently
2820    /// disagree with the peers on which upgrade slice a given Caixa
2821    /// resolves to — a six-consumer split at the enumerator, the
2822    /// three-stage validate pass, the script-path probe walk, and the
2823    /// M2 overlay emitter, far from the source `caixa.lisp` with no
2824    /// field naming the upgrade-drift root cause. Lifting the
2825    /// resolution rule to a typed method on the substrate primitive
2826    /// means every downstream consumer of the caixa's per-`Caixa`
2827    /// OTP-appup outer-slice surface reaches for exactly one typed
2828    /// dispatch — the resolver's accept-set migrates as a unit on any
2829    /// future axis addition.
2830    ///
2831    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
2832    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
2833    /// outer-`Caixa` `&[Composite]` composite-slice projection
2834    /// pattern the sibling `:children`
2835    /// [`crate::supervisor::ChildSpec`] / `:membros`
2836    /// [`crate::aplicacao::Membro`] / `:contratos`
2837    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
2838    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
2839    /// `Option<&Composite>` composite-reference family the sibling
2840    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
2841    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
2842    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
2843    /// `Option<&Composite>` altitude, extended here to the outer-
2844    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
2845    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
2846    /// (0137e5a) — same "one typed dispatch on the substrate
2847    /// primitive, thin projections at each consumer" discipline
2848    /// folded onto the outer top-level [`Caixa`] altitude, opening the
2849    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
2850    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
2851    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
2852    /// `&[String]`-return [`Self::autores`] (b5d813f) /
2853    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
2854    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
2855    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
2856    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
2857    /// slice" projection pattern onto the sibling M2 typed-composite-
2858    /// element axis (`UpgradeFromEntry` composite, matching the
2859    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
2860    /// different altitude).
2861    ///
2862    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
2863    /// because every downstream consumer of the hot-upgrade list
2864    /// treats it as a read-only sequence — the slice-view is the
2865    /// narrowest borrow that supports every present + roadmapped
2866    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
2867    /// serialization through
2868    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
2869    /// the backing `Vec`'s grow/push/reserve surface no consumer of
2870    /// the typed view reaches for (the storage-side `Vec` remains
2871    /// reachable through the `pub upgrade_from` field for the
2872    /// mutation-carrying serde round-trip and per-test fixture-
2873    /// mutation paths). Named `upgrade_from()` to match the storage
2874    /// field's `snake_case` name; the kebab-case author-surface tag
2875    /// `:upgrade-from` is the same axis after tatara-lisp's
2876    /// kebab↔snake fold and the accessor's identity maps onto the
2877    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
2878    /// already carries.
2879    #[must_use]
2880    pub fn upgrade_from(&self) -> &[UpgradeFromEntry] {
2881        self.upgrade_from.as_slice()
2882    }
2883
2884    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
2885    /// slot outer-composite OTP-shaped per-supervisor static-child-list
2886    /// slice accessor every consumer of the top-level manifest's per-
2887    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
2888    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
2889    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
2890    /// the same backing buffer the raw `self.children.as_slice()` field
2891    /// access borrows from. Empty-slice-carrying (the "no static children
2892    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
2893    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
2894    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
2895    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
2896    /// on those arms without any silent `None` collapse).
2897    ///
2898    /// The outer `:children` slot carries the M2 typed OTP-supervisor
2899    /// static-child list — the load-bearing container of every per-
2900    /// child `{caixa, versao, restart}` triple the wasm-operator's
2901    /// hierarchical reconciler dispatches on at supervisor-tree
2902    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
2903    /// static-child list translated onto pleme-io's typed
2904    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
2905    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
2906    /// dispatch fans on). Every per-child axis threads through a lifted
2907    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
2908    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
2909    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
2910    /// version-requirement scalar accessor, and the
2911    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
2912    /// per-child post-exit restart-decision-policy discriminant
2913    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
2914    /// tree path first passes through this outer accessor onto the
2915    /// slice and then dispatches per-child through the inner accessors
2916    /// — the two-level dispatch means every per-`:children` reader now
2917    /// routes through a typed dispatch on the substrate primitive at
2918    /// both altitudes.
2919    ///
2920    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
2921    /// accessed inline at three production sites across two files —
2922    /// the [`Self::declared_supervisor_slots`] supervisor-tree
2923    /// declared-slot enumerator's `!self.children.is_empty()` presence
2924    /// probe (caixa-core/src/manifest.rs, which drives the
2925    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
2926    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
2927    /// kind-coherence gate reads), the [`Self::supervisor_view`]
2928    /// per-supervisor typed-view composer's `self.children.clone()`
2929    /// per-child fold-in path (caixa-core/src/manifest.rs, which
2930    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
2931    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
2932    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
2933    /// `:children :caixa` self-parent refusal probe's
2934    /// `&caixa.children`-borrowed
2935    /// [`crate::supervisor::validate_no_self_supervision`] input
2936    /// (caixa-core/src/layout.rs, which pins the "no child names the
2937    /// supervisor's own `:nome`" cross-slot coherence gate). A future
2938    /// extension of the outer `:children` axis (a per-cluster
2939    /// `:children-overrides` overlay the wasm-engine operator resolves
2940    /// at admission time so a cluster-specific child-set can tighten
2941    /// a caixa-declared list without re-authoring the `caixa.lisp`,
2942    /// promotion of the plain `Vec<ChildSpec>` to a richer
2943    /// `{static, dynamic}` partition once Erlang/OTP's
2944    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
2945    /// axis, per-child priority annotation once multi-strategy fan-out
2946    /// lands) would have had to be threaded through all three open-
2947    /// coded copies in lockstep or one consumer would silently
2948    /// disagree with the peers on which child slice a given Caixa
2949    /// resolves to — the enumerator's presence probe reading the raw
2950    /// slot while the peer view-composer's fold-in path read an
2951    /// operator-resolved slot would silently split the paired
2952    /// declared-slot enumerator and typed-view composition, and the
2953    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
2954    /// refusal probe reading a third borrow would silently drift the
2955    /// cross-slot coherence gate's traversal input from the two peers,
2956    /// a three-consumer split at the enumerator, the view composer,
2957    /// and the self-parent gate far from the source `caixa.lisp` with
2958    /// no field naming the child-set-drift root cause. Lifting the
2959    /// resolution rule to a typed method on the substrate primitive
2960    /// means every downstream consumer of the caixa's per-`Caixa`
2961    /// OTP-supervisor outer-slice surface reaches for exactly one
2962    /// typed dispatch — the resolver's accept-set migrates as a unit
2963    /// on any future axis addition.
2964    ///
2965    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
2966    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
2967    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
2968    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
2969    /// at the outer altitude of the closed inner-`SupervisorSpec`
2970    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
2971    /// same OTP-supervisor static-child-list axis — same "byte-equal,
2972    /// borrow-shared" outer-accessor discipline extended onto the
2973    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
2974    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
2975    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
2976    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
2977    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
2978    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
2979    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
2980    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
2981    /// M2 typed-composite-element axis
2982    /// ([`crate::supervisor::ChildSpec`] composite, matching the
2983    /// per-inner [`crate::SupervisorSpec::children`] element type at a
2984    /// different altitude).
2985    ///
2986    /// Returns `&[crate::supervisor::ChildSpec]` (not
2987    /// `&Vec<ChildSpec>`) because every downstream consumer of the
2988    /// child list treats it as a read-only sequence — the slice-view
2989    /// is the narrowest borrow that supports every present +
2990    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
2991    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
2992    /// input, `serde` slice-serialization) without leaking the backing
2993    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
2994    /// reaches for (the storage-side `Vec` remains reachable through
2995    /// the `pub children` field for the mutation-carrying serde round-
2996    /// trip and per-test fixture-mutation paths, including the
2997    /// [`Self::supervisor_view`] fold-in path that clones the slot
2998    /// into the typed view). Named `children()` to match the storage
2999    /// field's name verbatim and the tatara-lisp author-surface term
3000    /// (`:children`) the field's own docstring already carries; the
3001    /// accessor's identity maps onto the canonical OTP supervision
3002    /// vocabulary the [`Caixa::children`] field's docstring already
3003    /// reaches for ("Static children of a supervisor").
3004    #[must_use]
3005    pub fn children(&self) -> &[crate::supervisor::ChildSpec] {
3006        self.children.as_slice()
3007    }
3008
3009    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3010    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3011    /// accessor every consumer of the top-level manifest's per-Aplicacao
3012    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3013    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3014    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3015    /// same backing buffer the raw `self.membros.as_slice()` field access
3016    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3017    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3018    /// and every partially-authored Aplicacao carries before the
3019    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3020    /// `&[Membro]` degenerates to an empty slice on those arms without any
3021    /// silent `None` collapse).
3022    ///
3023    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3024    /// per-Aplicacao member list — the load-bearing container of every
3025    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3026    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3027    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3028    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3029    /// the `:entrada :para` external-gateway destination validates
3030    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3031    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3032    /// threads through a lifted per-entry accessor on the
3033    /// [`crate::aplicacao::Membro`] type: the
3034    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3035    /// identity scalar accessor (4a32abf) and the peer
3036    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3037    /// version-requirement scalar accessor (a40b0e3). Every downstream
3038    /// consumer of the mesh-graph path first passes through this outer
3039    /// accessor onto the slice and then dispatches per-member through
3040    /// the inner accessors — the two-level dispatch means every per-
3041    /// `:membros` reader now routes through a typed dispatch on the
3042    /// substrate primitive at both altitudes.
3043    ///
3044    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3045    /// inline at three production sites across two files — the
3046    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3047    /// enumerator's `!self.membros.is_empty()` presence probe
3048    /// (caixa-core/src/manifest.rs, which drives the
3049    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3050    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3051    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3052    /// composer's `self.membros.clone()` per-member fold-in path
3053    /// (caixa-core/src/manifest.rs, which materializes the typed
3054    /// [`crate::aplicacao::AplicacaoSpec`] view every
3055    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3056    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3057    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3058    /// [`crate::aplicacao::validate_no_self_membership`] input
3059    /// (caixa-core/src/layout.rs, which pins the "no member names the
3060    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3061    /// extension of the outer `:membros` axis (a per-cluster
3062    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3063    /// admission time so a cluster-specific member-set can tighten a
3064    /// caixa-declared list without re-authoring the `caixa.lisp`,
3065    /// promotion of the plain `Vec<Membro>` to a richer
3066    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3067    /// members land as a typed axis, per-member priority annotation once
3068    /// multi-strategy fan-out lands) would have had to be threaded
3069    /// through all three open-coded copies in lockstep or one consumer
3070    /// would silently disagree with the peers on which member slice a
3071    /// given Caixa resolves to — the enumerator's presence probe reading
3072    /// the raw slot while the peer view-composer's fold-in path read an
3073    /// operator-resolved slot would silently split the paired
3074    /// declared-slot enumerator and typed-view composition, and the
3075    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3076    /// refusal probe reading a third borrow would silently drift the
3077    /// cross-slot coherence gate's traversal input from the two peers, a
3078    /// three-consumer split at the enumerator, the view composer, and
3079    /// the self-membership gate far from the source `caixa.lisp` with no
3080    /// field naming the member-set-drift root cause. Lifting the
3081    /// resolution rule to a typed method on the substrate primitive
3082    /// means every downstream consumer of the caixa's per-`Caixa`
3083    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3084    /// typed dispatch — the resolver's accept-set migrates as a unit on
3085    /// any future axis addition.
3086    ///
3087    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3088    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3089    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3090    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3091    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3092    /// altitude. Peer at the outer altitude of the closed inner-
3093    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3094    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3095    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3096    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3097    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3098    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3099    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3100    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3101    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3102    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3103    /// pattern onto the sibling M3 typed-composite-element axis
3104    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3105    /// [`crate::AplicacaoSpec::membros`] element type at a different
3106    /// altitude).
3107    ///
3108    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3109    /// because every downstream consumer of the member list treats it
3110    /// as a read-only sequence — the slice-view is the narrowest borrow
3111    /// that supports every present + roadmapped consumer (`.iter()`,
3112    /// `.len()`, `.is_empty()`, the
3113    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3114    /// input, `serde` slice-serialization) without leaking the backing
3115    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3116    /// reaches for (the storage-side `Vec` remains reachable through the
3117    /// `pub membros` field for the mutation-carrying serde round-trip
3118    /// and per-test fixture-mutation paths, including the
3119    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3120    /// the typed view). Named `membros()` to match the storage field's
3121    /// name verbatim and the tatara-lisp author-surface term
3122    /// (`:membros`) the field's own docstring already carries; the
3123    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3124    /// vocabulary the [`Caixa::membros`] field's docstring already
3125    /// reaches for ("Member Servicos that make up this Aplicacao").
3126    #[must_use]
3127    pub fn membros(&self) -> &[crate::aplicacao::Membro] {
3128        self.membros.as_slice()
3129    }
3130
3131    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3132    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3133    /// inter-Servico contract-list slice accessor every consumer of the
3134    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3135    /// slice-view keys off — returns the author-declared `:contratos`
3136    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3137    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3138    /// backing buffer the raw `self.contratos.as_slice()` field access
3139    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3140    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3141    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3142    /// single member with no inter-Servico edge carries; the returned
3143    /// `&[WitContract]` degenerates to an empty slice on those arms
3144    /// without any silent `None` collapse).
3145    ///
3146    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3147    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3148    /// container of every per-edge `{de, para, wit, endpoint | subject |
3149    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3150    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3151    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3152    /// adjacency-list seed dispatch on at mesh-artifact materialization
3153    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3154    /// `:membros` vertex set resolves against, closed by the
3155    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3156    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3157    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3158    /// per-edge axis threads through a lifted per-entry accessor on the
3159    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3160    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3161    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3162    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3163    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3164    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3165    /// and the WIT-world discriminant. Every downstream consumer of the
3166    /// mesh-graph edge path first passes through this outer accessor
3167    /// onto the slice and then dispatches per-contract through the
3168    /// inner accessors — the two-level dispatch means every
3169    /// per-`:contratos` reader now routes through a typed dispatch on
3170    /// the substrate primitive at both altitudes.
3171    ///
3172    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3173    /// accessed inline at two production sites in
3174    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3175    /// mesh-slot declared-slot enumerator's
3176    /// `!self.contratos.is_empty()` presence probe (which drives the
3177    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3178    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3179    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3180    /// typed-view composer's `self.contratos.clone()` per-contract
3181    /// fold-in path (which materializes the typed
3182    /// [`crate::aplicacao::AplicacaoSpec`] view every
3183    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3184    /// downstream `caixa-mesh` renderer dispatches on). A future
3185    /// extension of the outer `:contratos` axis (a per-cluster
3186    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3187    /// at admission time so a cluster-specific edge-set can tighten a
3188    /// caixa-declared list without re-authoring the `caixa.lisp`,
3189    /// promotion of the plain `Vec<WitContract>` to a richer
3190    /// `{static, dynamic}` partition once runtime-resolved contract
3191    /// edges land, per-edge policy annotation once the M4 per-edge
3192    /// policy overlay axis lands) would have had to be threaded through
3193    /// both open-coded copies in lockstep or one consumer would
3194    /// silently disagree with the peer on which edge slice a given
3195    /// Caixa resolves to — the enumerator's presence probe reading the
3196    /// raw slot while the peer view-composer's fold-in path read an
3197    /// operator-resolved slot would silently split the paired
3198    /// declared-slot enumerator and typed-view composition, a
3199    /// two-consumer split at the enumerator and the view composer far
3200    /// from the source `caixa.lisp` with no field naming the edge-set-
3201    /// drift root cause. Lifting the resolution rule to a typed method
3202    /// on the substrate primitive means every downstream consumer of
3203    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3204    /// reaches for exactly one typed dispatch — the resolver's
3205    /// accept-set migrates as a unit on any future axis addition.
3206    ///
3207    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3208    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3209    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3210    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3211    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3212    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3213    /// mesh-slot arm of the composite-slice sub-family the sibling
3214    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3215    /// Peer at the outer altitude of the closed inner-
3216    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3217    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3218    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3219    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3220    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3221    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3222    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3223    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3224    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3225    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3226    /// pattern onto the sibling M3 typed-composite-element axis
3227    /// ([`crate::aplicacao::WitContract`] composite, matching the
3228    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3229    /// different altitude).
3230    ///
3231    /// Returns `&[crate::aplicacao::WitContract]` (not
3232    /// `&Vec<WitContract>`) because every downstream consumer of the
3233    /// contract list treats it as a read-only sequence — the slice-view
3234    /// is the narrowest borrow that supports every present + roadmapped
3235    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3236    /// discriminant dispatch, `serde` slice-serialization) without
3237    /// leaking the backing `Vec`'s grow/push/reserve surface no
3238    /// consumer of the typed view reaches for (the storage-side `Vec`
3239    /// remains reachable through the `pub contratos` field for the
3240    /// mutation-carrying serde round-trip and per-test fixture-mutation
3241    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3242    /// clones the slot into the typed view). Named `contratos()` to
3243    /// match the storage field's name verbatim and the tatara-lisp
3244    /// author-surface term (`:contratos`) the field's own docstring
3245    /// already carries; the accessor's identity maps onto the canonical
3246    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3247    /// docstring already reaches for ("WIT-typed inter-Servico
3248    /// contracts").
3249    #[must_use]
3250    pub fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3251        self.contratos.as_slice()
3252    }
3253
3254    /// Compose the Aplicacao-related flat slots into a single typed
3255    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3256    /// downstream renderer consumption. Returns `None` when the
3257    /// caixa isn't a `:kind Aplicacao`.
3258    #[must_use]
3259    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3260        if !self.kind().is_aplicacao() {
3261            return None;
3262        }
3263        Some(crate::aplicacao::AplicacaoSpec {
3264            membros: self.membros().to_vec(),
3265            contratos: self.contratos().to_vec(),
3266            politicas: self.politicas().cloned().unwrap_or_default(),
3267            placement: self.placement().cloned().unwrap_or_default(),
3268            entrada: self.entrada().cloned(),
3269        })
3270    }
3271
3272    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3273    /// *declares* a value on, in canonical declaration order
3274    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3275    /// `:entrada`). A slot counts as declared when its backing field
3276    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3277    ///
3278    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3279    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3280    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3281    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3282    /// caixa-flux / caixa-helm renderers only emit them for an
3283    /// Aplicacao. On any *other* kind a declared mesh slot is the
3284    /// manifest field's documented "ignored otherwise" (see the
3285    /// `:membros` … `:entrada` field docs): it silently passes
3286    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3287    /// rendered — far from the source caixa.lisp.
3288    /// [`crate::StandardLayout::verify`] consults this to reject that
3289    /// silent-drop at caixa-build time
3290    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3291    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3292    /// a slot foreign to the kind is a build error, not a silent drop.
3293    ///
3294    /// Lifted as a typed method (rather than an inline disjunction at
3295    /// the verify call site) so the mesh-slot set lives in one place —
3296    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3297    /// overlay, distributed-app takeover config) is one push here, and
3298    /// every consumer reaching for "which mesh slots are set" (the
3299    /// verify gate, a future `feira lint` kind-coherence advisory)
3300    /// inherits the canonical order without rolling its own.
3301    ///
3302    /// Each per-arm kebab-case label is routed through the peer
3303    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3304    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3305    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3306    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3307    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3308    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3309    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3310    /// kebab-case label + renderer-side artifact key) route through one
3311    /// canonical declaration per arm — same discipline the peer
3312    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3313    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3314    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3315    /// axis, extended here to close the M3 mesh-slot author-facing-label
3316    /// axis so both altitudes of the typed-slot algebra
3317    /// (per-Servico M2 + per-Aplicacao M3) share the same
3318    /// "one canonical byte-string per arm, next to the axis" discipline.
3319    #[must_use]
3320    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3321        let mut slots = Vec::new();
3322        if !self.membros().is_empty() {
3323            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3324        }
3325        if !self.contratos().is_empty() {
3326            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3327        }
3328        if self.politicas().is_some() {
3329            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3330        }
3331        if self.placement().is_some() {
3332            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3333        }
3334        if self.entrada().is_some() {
3335            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3336        }
3337        slots
3338    }
3339
3340    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3341    /// caixa *declares* a value on, in canonical declaration order
3342    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3343    /// `:children`). A slot counts as declared when its backing field
3344    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3345    ///
3346    /// The supervisor-tree slots compose the typed OTP supervisor of a
3347    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3348    /// `:children` field docs above). [`Self::supervisor_view`] only
3349    /// folds them into a validatable [`SupervisorSpec`] when the kind
3350    /// matches (returns `None` otherwise), and the wasm-operator's
3351    /// hierarchical reconciler only consumes them for a Supervisor. On
3352    /// any *other* kind a declared supervisor slot is the manifest
3353    /// field's documented "ignored otherwise" (see the `:estrategia` …
3354    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3355    /// and then vanishes — never validated, never reconciled — far from
3356    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3357    /// this to reject that silent-drop at caixa-build time
3358    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3359    /// exact mirror of the [`Self::declared_mesh_slots`] /
3360    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3361    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3362    /// error, not a silent drop.
3363    #[must_use]
3364    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3365        let mut slots = Vec::new();
3366        if self.estrategia().is_some() {
3367            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3368        }
3369        if self.max_restarts().is_some() {
3370            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3371        }
3372        if self.restart_window().is_some() {
3373            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3374        }
3375        if !self.children().is_empty() {
3376            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3377        }
3378        slots
3379    }
3380
3381    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3382    /// caixa *declares* a value on, in canonical declaration order
3383    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3384    /// declared when its backing field carries a value — a `Some(...)`,
3385    /// or a non-empty `Vec`.
3386    ///
3387    /// The M2 slots configure the runtime of a long-running wasm
3388    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3389    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3390    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3391    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3392    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3393    /// emit these slots for a Servico; on any *other* kind a declared M2
3394    /// slot is the manifest field's documented "ignored otherwise": its
3395    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3396    /// but the value is never rendered into a chart / programs.yaml entry
3397    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
3398    /// vanishes, far from the source caixa.lisp.
3399    /// [`crate::StandardLayout::verify`] consults this to reject that
3400    /// silent-drop at caixa-build time
3401    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
3402    /// mirror of the [`Self::declared_mesh_slots`] /
3403    /// [`Self::declared_supervisor_slots`] gates on the peer
3404    /// kind-exclusive slot sets: a slot foreign to the kind is a build
3405    /// error, not a silent drop.
3406    ///
3407    /// Each per-arm kebab-case label is routed through the peer
3408    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
3409    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
3410    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
3411    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
3412    /// both halves of the M2 top-level slot's dual axis (author-facing
3413    /// kebab-case label + renderer-side camelCase overlay-container wire
3414    /// key) route through one canonical declaration per arm — same
3415    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
3416    /// author-label consts (889dc18) establish on the sibling
3417    /// per-callback axis inside the `:behavior` overlay block.
3418    #[must_use]
3419    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
3420        let mut slots = Vec::new();
3421        if self.limits().is_some() {
3422            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
3423        }
3424        if self.behavior().is_some() {
3425            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
3426        }
3427        if !self.upgrade_from().is_empty() {
3428            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
3429        }
3430        slots
3431    }
3432
3433    /// The kebab-case `:slot` tags of every code-surface slot this caixa
3434    /// declares a value on that its [`CaixaKind`] doesn't natively own,
3435    /// in canonical declaration order (`:exe` → `:servicos`). A
3436    /// code-surface slot is owned by exactly one kind: `:exe` by
3437    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
3438    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
3439    /// `ComputeUnit` daemon surface).
3440    ///
3441    /// Each is silently ignored when declared on the wrong kind: the
3442    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
3443    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
3444    /// code-running kind a declared `:exe` / `:servicos` is the manifest
3445    /// field's documented "ignored otherwise" — its path is checked for
3446    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
3447    /// (which run after [`Caixa::from_lisp`]), but the value is never
3448    /// rendered into a build target or programs.yaml entry. It silently
3449    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
3450    /// caixa.lisp, with no field naming which slot is foreign.
3451    ///
3452    /// [`crate::StandardLayout::verify`] consults this to reject that
3453    /// silent-drop at caixa-build time
3454    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
3455    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
3456    /// gates ([`Self::declared_servico_slots`] /
3457    /// [`Self::declared_supervisor_slots`] /
3458    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
3459    /// axis to be closed on the typed surface. The Supervisor /
3460    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
3461    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
3462    /// diagnostics — they fire ahead of this gate on the same `verify`
3463    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
3464    /// and this method is moot. For Biblioteca / Binario / Servico, this
3465    /// gate fires when a code-running kind declares another code-running
3466    /// kind's exclusive code surface.
3467    ///
3468    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
3469    /// may legitimately ship a `lib/` helper that the underlying
3470    /// substrate (the nix flake for Binario, the wasm component build
3471    /// for Servico) bundles into its build, so the slot's
3472    /// declared-on-wrong-kind cardinality isn't a structural error on
3473    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
3474    /// is the native case (the slot's owning kind). Supervisor /
3475    /// Aplicacao declaring `:bibliotecas` is gated upstream by
3476    /// [`crate::LayoutError::SupervisorOwnsCode`] /
3477    /// [`crate::LayoutError::AplicacaoOwnsCode`].
3478    ///
3479    /// Lifted as a typed method (rather than an inline disjunction at
3480    /// the verify call site) so the foreign-code-slot set lives in one
3481    /// place — a future kind that gains its own code-surface slot is
3482    /// one push here, and every consumer reaching for "which code
3483    /// surfaces are foreign to this kind" (the verify gate, a future
3484    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
3485    /// per-caixa build-target classifier) inherits the canonical order
3486    /// without rolling its own.
3487    #[must_use]
3488    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
3489        let mut slots = Vec::new();
3490        if !self.exe().is_empty() && !self.kind().requires_exe() {
3491            slots.push(":exe");
3492        }
3493        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
3494            slots.push(":servicos");
3495        }
3496        slots
3497    }
3498
3499    /// Validate every entry of `:deps` and `:deps-dev` through
3500    /// [`Dep::validate`] — closing the parity loop with the per-axis
3501    /// `:versao` gates already wired into the typed-graph
3502    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
3503    /// 9888b13) and typed supervisor tree
3504    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
3505    ///
3506    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
3507    /// were the only `:versao` axes still untyped past
3508    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
3509    /// as a String without parsing it, so a malformed-but-non-empty
3510    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
3511    /// silently passed parse and the `semver::Error` surfaced at
3512    /// lacre-resolve time, far from the source caixa.lisp, with no
3513    /// field naming which `:deps` entry carried the typo. Lifting the
3514    /// gate here makes the four `:versao` typed surfaces (`:deps`,
3515    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
3516    /// every requirement string past `validate_deps` is round-trippable
3517    /// through [`crate::parse_requirement`] without re-checking at the
3518    /// resolver layer.
3519    ///
3520    /// Both lists run through the same per-entry validator so a typo
3521    /// in `:deps-dev` surfaces with the same diagnostic as one in
3522    /// `:deps` — neither axis is a second-class citizen of the typed
3523    /// surface.
3524    ///
3525    /// Within each list, [`DepError::DuplicateNome`] closes the
3526    /// set-not-multiset discipline on the `:nome` axis: two entries
3527    /// naming the same caixa carry two `:versao` / `:fonte` / feature
3528    /// triples that the caixa-resolver's lacre pipeline collapses to one
3529    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
3530    /// silently overwrites the first at `concrete_versao`-resolve time
3531    /// (the same "second wins / one silently overwrites the other"
3532    /// shape the peer typed-graph duplicate gates already close on every
3533    /// other Vec-shaped authoring surface that keys by name). The
3534    /// duplicate check fires per-list and runs *after* each per-entry
3535    /// [`Dep::validate`] call so a malformed-and-duplicated entry
3536    /// surfaces its narrower per-entry diagnostic
3537    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
3538    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
3539    /// diagnostic — the canonical "per-entry shape before cross-entry
3540    /// uniqueness" precedence the peer `:children :caixa`
3541    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
3542    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
3543    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
3544    /// ([`crate::AplicacaoSpec::validate_placement`]),
3545    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
3546    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
3547    /// and the within-`:upgrade-from`-entry per-instruction-class
3548    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
3549    /// [`crate::UpgradeError::DuplicateStateChange`],
3550    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
3551    ///
3552    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
3553    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
3554    /// same name in both tables (the dev table's pin overrides the
3555    /// runtime table's pin in test/dev contexts), and caixa's surface
3556    /// mirrors that convention until a deliberate choice retires the
3557    /// override pattern. Only within-list duplicates are structurally
3558    /// incoherent — those are what this gate closes.
3559    pub fn validate_deps(&self) -> Result<(), DepError> {
3560        let mut seen = std::collections::HashSet::new();
3561        for dep in self.deps() {
3562            dep.validate()?;
3563            crate::render::insert_first_seen(&mut seen, dep.nome(), || DepError::DuplicateNome {
3564                nome: dep.nome().to_string(),
3565                list: crate::render::DEP_AUTHOR_KEY_DEPS,
3566            })?;
3567        }
3568        let mut seen_dev = std::collections::HashSet::new();
3569        for dep in self.deps_dev() {
3570            dep.validate()?;
3571            crate::render::insert_first_seen(&mut seen_dev, dep.nome(), || {
3572                DepError::DuplicateNome {
3573                    nome: dep.nome().to_string(),
3574                    list: crate::render::DEP_AUTHOR_KEY_DEPS_DEV,
3575                }
3576            })?;
3577        }
3578        Ok(())
3579    }
3580
3581    /// Reject `:nome` values the K8s apiserver would refuse at admission
3582    /// time. The top-level Caixa identity flows directly into every
3583    /// substrate-side artifact's `metadata.name` axis: the
3584    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
3585    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
3586    /// aggregator keys ComputeUnit derivation off
3587    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
3588    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
3589    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
3590    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
3591    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
3592    /// ([`caixa-mesh::lib::cilium_network_policies`],
3593    /// [`caixa-mesh::lib::gateway_routes`]), and the default
3594    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
3595    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
3596    /// schema enforces the DNS-1123 label rule on admission; a
3597    /// structurally invalid `:nome` (`"MyApp"` — the canonical
3598    /// "I copied the display name verbatim" footgun, `"my_app"` — the
3599    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
3600    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
3601    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
3602    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
3603    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
3604    /// failure surfaced at `kubectl apply` time as a `metadata.name:
3605    /// Invalid value` rejection on whichever derived artifact admitted
3606    /// first, far from the source `caixa.lisp` and without any field
3607    /// naming the offending `:nome`.
3608    ///
3609    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
3610    /// substrate-side predicate the per-axis name gates already share:
3611    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
3612    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
3613    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
3614    /// diagnostic is self-locating (the offending `:nome` is named
3615    /// verbatim) and the author can grep their `caixa.lisp` for
3616    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
3617    /// every per-axis sibling gate already exposes
3618    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
3619    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
3620    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
3621    ///
3622    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
3623    /// derive macro stores the raw String) is gated by the narrower
3624    /// [`ManifestError::NomeEmpty`] arm before the predicate is
3625    /// consulted, mirroring the empty-first cascade every per-axis
3626    /// name gate already uses (e.g. `MembroCaixaEmpty` before
3627    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
3628    pub fn validate_nome(&self) -> Result<(), ManifestError> {
3629        // Routes through the shared
3630        // [`crate::render::require_valid_dns_1123_label`] gate the peer
3631        // name axes each land on so drift between the eight axes'
3632        // accepted DNS-1123-label sets is structurally impossible.
3633        let nome = self.nome();
3634        crate::render::require_valid_dns_1123_label(
3635            nome,
3636            || ManifestError::NomeEmpty,
3637            |reason| ManifestError::NomeInvalid {
3638                nome: nome.to_string(),
3639                reason,
3640            },
3641        )
3642    }
3643
3644    /// Reject `:nome` values whose joint length with the canonical
3645    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
3646    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
3647    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
3648    /// substrate carries materializes the caixa's `:nome` through the
3649    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
3650    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
3651    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
3652    /// `ChartDir.name` + `Chart.yaml::name`
3653    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
3654    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
3655    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
3656    /// `oci://<registry>/lareira-<nome>` chart ref
3657    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
3658    /// admission rule strict-parses against DNS-1123-label, the Helm
3659    /// operator's tracking-secret name is derived from `release_name`
3660    /// and is itself DNS-1123-label-bounded, and the rendered chart's
3661    /// K8s object `metadata.name` axes embed the chart name as a
3662    /// prefix — every one fails admission on a > 63-byte chart name.
3663    ///
3664    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
3665    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
3666    /// `:nome` of 56–63 bytes silently passed validate (the inner
3667    /// DNS-1123 check accepts the bare `:nome`) but produced a
3668    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
3669    /// rejected at admission — far from the source `caixa.lisp`, with
3670    /// no field naming the overflow root cause. The
3671    /// [`lareira_chart_name`] helper's own doc comment
3672    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
3673    /// "the M4 admission webhook will pin the joint-length invariant
3674    /// when it lands". This gate lands the invariant at the
3675    /// manifest-validate layer rather than waiting for the apiserver
3676    /// — the same fail-at-the-source posture every peer per-axis
3677    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
3678    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
3679    /// `:edicao`, etc.) takes.
3680    ///
3681    /// Thin wrapper around
3682    /// [`crate::render::is_lareira_chart_name_shape`] (the
3683    /// substrate-side predicate that composes [`lareira_chart_name`] +
3684    /// [`is_dns_1123_label`] via the lifted
3685    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
3686    /// shared parser-shaped reason into the
3687    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
3688    /// diagnostic is self-locating (the offending `:nome` is named
3689    /// verbatim alongside the rendered chart name and the budget) and
3690    /// the author can shorten in one edit. The gate runs across every
3691    /// `:kind` — `:nome` is the substrate-wide identity axis any
3692    /// future renderer the substrate adds can derive a
3693    /// `lareira-<nome>` artifact from, and uniform enforcement closes
3694    /// the drift footgun where a future kind grows a chart-emitting
3695    /// render path while the validate cascade doesn't catch it.
3696    ///
3697    /// Runs *after* [`Self::validate_nome`] so the narrower
3698    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
3699    /// structurally-malformed `:nome` (empty, uppercase, underscore,
3700    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
3701    /// specific shape error rather than the chart-name-budget error,
3702    /// preserving the legitimate "well-shaped `:nome` that happens to
3703    /// overflow the joint cap" arm for this gate.
3704    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
3705        let nome = self.nome();
3706        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
3707            ManifestError::NomeChartNameBudgetExceeded {
3708                nome: nome.to_string(),
3709                reason,
3710            }
3711        })
3712    }
3713
3714    /// Reject `:versao` values that don't parse as [`semver::Version`].
3715    /// The top-level Caixa version flows directly into every
3716    /// substrate-side artifact that carries a "this is which version of
3717    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
3718    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
3719    /// SemVer-2-strict at `helm template` / `helm install` time per
3720    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
3721    /// `feira publish` Zig-style `v<versao>` git tag
3722    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
3723    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
3724    /// `versao:` value the `lareira-fleet-programs` aggregator carries
3725    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
3726    /// `:latest` tags the substrate's `wasi-service-flake` builds with
3727    /// `skopeo push`, the lacre closure's pinned versions
3728    /// ([`caixa-resolver`] keys `concrete_versao`), and the
3729    /// `:upgrade-from :from` references peers in this exact `versao`
3730    /// shape (`semver::Version`, not `VersionReq`). Each consumer
3731    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
3732    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
3733    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
3734    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
3735    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
3736    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
3737    /// into the version field a peer `:deps :versao` accepts;
3738    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
3739    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
3740    /// derive macro stores the raw String) and the failure surfaced at
3741    /// the *first* downstream consumer that strict-parses it: at
3742    /// `helm install` time as a chart-version rejection, at
3743    /// `feira publish` time as a malformed git tag, at lacre-resolve
3744    /// time as a `semver::Error` not naming the offending caixa, at
3745    /// `feira upgrade --to <versao>` time as an unresolvable
3746    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
3747    /// and without any field naming the offending `:versao`.
3748    ///
3749    /// Thin wrapper around [`semver::Version::parse`] — the same parser
3750    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
3751    /// and [`crate::UpgradeFromEntry::validate`] (the peer
3752    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
3753    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
3754    /// variant, carrying the offending `:versao` verbatim + a
3755    /// parser-shaped reason naming the specific violation, so the
3756    /// diagnostic is self-locating (the author can grep their
3757    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
3758    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
3759    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
3760    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
3761    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
3762    /// now structurally equivalent (every value past validate is
3763    /// round-trippable through [`semver::Version::parse`] without
3764    /// re-checking at the renderer, resolver, or operator hot-upgrade
3765    /// layer), peer with the four `:versao` requirement axes (`:deps`,
3766    /// `:deps-dev`, `:membros`, `:children`) the prior commits
3767    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
3768    ///
3769    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
3770    /// the derive macro stores the raw String) is gated by the
3771    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
3772    /// consulted, mirroring the empty-first cascade every per-axis
3773    /// version gate already uses (e.g. `MembroVersaoEmpty` before
3774    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
3775    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
3776    pub fn validate_versao(&self) -> Result<(), ManifestError> {
3777        let versao = self.versao();
3778        if versao.is_empty() {
3779            return Err(ManifestError::VersaoEmpty);
3780        }
3781        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
3782            versao: versao.to_string(),
3783            reason: e.to_string(),
3784        })?;
3785        Ok(())
3786    }
3787
3788    /// Reject `:restart-window` values the shared
3789    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
3790    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
3791    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
3792    /// `Option<Duration>` routed through the shared codec via `with =
3793    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
3794    /// view-construction path ([`Self::supervisor_view`]) folds the
3795    /// raw string through the same shared codec and soft-swallows the
3796    /// parse error as `None` to keep the view best-effort. Without
3797    /// this gate a malformed `:restart-window` (`"1.5s"` — the
3798    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
3799    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
3800    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
3801    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
3802    /// edge case) silently produced a `SupervisorSpec` with
3803    /// `restart_window: None`, indistinguishable from the canonical
3804    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
3805    /// `MaxIntensity / Period` invariant turns into a never-reset
3806    /// supervisor far from the source `caixa.lisp`, with no field
3807    /// naming the offending `:restart-window`. Lifting the gate to a
3808    /// Caixa-level validator mirrors the trajectory of the peer
3809    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
3810    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
3811    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
3812    /// (line 196: "reject invalid `:restart-window` (non-duration)").
3813    ///
3814    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
3815    /// (the shared codec backing `:supervisor :restart-window` as
3816    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
3817    /// `:politicas :circuit-breaker :window` — all three covered by
3818    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
3819    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
3820    /// variant, carrying the offending raw string + a parser-shaped
3821    /// reason naming the canonical authoring form, so the diagnostic
3822    /// is self-locating (the author can grep their `caixa.lisp` for
3823    /// `:restart-window "<value>"` and fix it in one edit) and
3824    /// uniform with every other manifest-level validate diagnostic.
3825    /// With this gate the four `:restart-window`-shaped surfaces (the
3826    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
3827    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
3828    /// now structurally equivalent — every value past the codec is in
3829    /// one accepted set, by construction.
3830    ///
3831    /// `None` (the canonical "omit the slot to express no reset"
3832    /// shape) is accepted trivially — the gate is a no-op when the
3833    /// author didn't author a window. The empty string is rejected by
3834    /// the shared codec (its digit-only gate refuses an empty
3835    /// magnitude), surfacing the same `RestartWindowMalformed`
3836    /// diagnostic as every other rejected non-canonical shape.
3837    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
3838        let Some(s) = self.restart_window() else {
3839            return Ok(());
3840        };
3841        crate::supervisor::duration_codec::parse(s)
3842            .map(|_| ())
3843            .map_err(|reason| ManifestError::RestartWindowMalformed {
3844                restart_window: s.to_string(),
3845                reason,
3846            })
3847    }
3848
3849    /// Reject per-entry values on the three Caixa-level code-surface
3850    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
3851    /// layout checker's `root.join(p)` sandbox would silently subvert.
3852    /// Same three structural footguns the peer
3853    /// [`BehaviorSpec::validate`] (b0c8389) and
3854    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
3855    /// (26da2c7) already close on the M2 `:behavior :on-*` and
3856    /// `:upgrade-from :state-change :script` axes, here lifted onto
3857    /// the three top-level code-path axes through the shared
3858    /// [`is_sandboxed_relative_path`] predicate:
3859    ///
3860    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
3861    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
3862    ///     [`Path::join`] as the base itself — `root.join("")` ==
3863    ///     `root`, so the existence check (`self.exists(&root)`)
3864    ///     trivially passes (the project root exists), and the layout
3865    ///     silently treats the project root as a biblioteca / exe /
3866    ///     servico entry. The `:bibliotecas` loop then hands the root
3867    ///     to `tatara_lisp::read` at `feira build` time as if the root
3868    ///     directory itself were a Lisp source file — a parse error
3869    ///     far from the source `caixa.lisp` with no field naming the
3870    ///     offending entry.
3871    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
3872    ///     [`Path::join`] *replaces* the base when the right-hand side
3873    ///     is absolute, so `root.join("/etc/passwd")` resolves to
3874    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
3875    ///     The existence check then silently consults whatever the
3876    ///     escaped path resolves to — for `:bibliotecas`, the layout
3877    ///     has no `starts_with`-fence (only `:exe` is fenced under
3878    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
3879    ///     `:bibliotecas` entry that happens to resolve on disk
3880    ///     silently passes. For `:exe` / `:servicos` the fence catches
3881    ///     the absolute case downstream as `ExeOutsideDir` /
3882    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
3883    ///     doesn't exist), but with a downstream-shaped diagnostic
3884    ///     that names the resolved escape path rather than the
3885    ///     authoring footgun at the source.
3886    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
3887    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
3888    ///     [`std::path::Component::ParentDir`] anywhere round-trips
3889    ///     through [`Path::join`] as a traversal above the caixa root.
3890    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
3891    ///     *component-aware* (not canonical-path-aware), so
3892    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
3893    ///     is **true** even though the canonical resolution
3894    ///     `{parent of root}/escape.lisp` lives outside the caixa root
3895    ///     — the fence silently lets the parent-escape through, and
3896    ///     the existence check passes if that escape-target happens
3897    ///     to exist. Caught regardless of where the `..` sits
3898    ///     (leading, mid-path, trailing) so the gate matches the peer
3899    ///     predicate's full coverage.
3900    ///
3901    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
3902    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
3903    /// same per-slot diagnostic shape every peer per-axis path-gate
3904    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
3905    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
3906    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
3907    /// order [`Caixa::declared_foreign_code_slots`] uses for its
3908    /// canonical foreign-code-slot diagnostic, so a manifest with
3909    /// multiple malformed slots surfaces the lexicographically-earliest
3910    /// slot's diagnostic deterministically.
3911    ///
3912    /// Lifted to the typed surface as a Caixa-level validator (peer
3913    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
3914    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
3915    /// and wired into [`crate::StandardLayout::verify`] before the
3916    /// existence-check loops so the diagnostic names the offending
3917    /// slot at the source caixa.lisp rather than reporting a
3918    /// downstream `MissingEntry` / `ExeOutsideDir` /
3919    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
3920    /// The fourth typed code-path surface — every author-supplied
3921    /// path on the manifest — is now structurally accept-shaped
3922    /// past validate, peer with `:behavior :on-*` and
3923    /// `:upgrade-from :state-change :script`.
3924    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
3925        /// Per-slot file-type contract for the three Caixa-level
3926        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
3927        /// Each variant names the predicate the per-entry file-type
3928        /// gate consults; [`Self::None`] opts the slot out of any
3929        /// file-type contract. Lifted as a typed local enum so the
3930        /// per-slot dispatch is exhaustive at the `match` — adding a
3931        /// future axis to the typed-substrate `:` slot set (the
3932        /// future `:assets` resource axis the M5 roadmap names, the
3933        /// future `:nix-flake` derivation axis the caixa-flake
3934        /// emitter consults) lands as one variant + one `match` arm,
3935        /// not a coordinated rewrite of every per-slot bool flag.
3936        ///
3937        /// Peer of the typed-substrate per-slot variant disciplines
3938        /// already established on this surface
3939        /// ([`crate::supervisor::RestartStrategy`] +
3940        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
3941        /// supervision-tree axis,
3942        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
3943        /// placement axis, [`crate::aplicacao::WitTarget`] on the
3944        /// `:contratos` payload-target axis): the typed `enum` is
3945        /// the substrate's single source of truth for the per-axis
3946        /// dispatch, and every consumer (the per-arm body here, the
3947        /// future feira-lint per-slot diagnostic renderer, the M4
3948        /// per-axis admission webhook) reaches for the same typed
3949        /// surface rather than re-deriving the partition from inline
3950        /// flag combinations.
3951        enum CodePathFileType {
3952            /// `:exe` — nix-build derivation output, no terminating-
3953            /// extension contract (the canonical `"exe/<name>"`
3954            /// fixtures the layout's `ExeOutsideDir` error message
3955            /// documents carry no extension by convention).
3956            None,
3957            /// `:bibliotecas` — tatara-lisp source files the
3958            /// `feira build` loop reads through `tatara_lisp::read`
3959            /// at parse time. Routes to [`is_lisp_extension`].
3960            LispSource,
3961            /// `:servicos` — ComputeUnit-CR YAML files the
3962            /// caixa-helm / caixa-flux renderers consume through
3963            /// `serde_yaml::from_str`. Routes to
3964            /// [`is_computeunit_yaml_extension`].
3965            ComputeUnitYaml,
3966        }
3967
3968        // The per-slot [`CodePathFileType`] selects which axes carry the
3969        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
3970        // source axis (the `feira build` loop at
3971        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
3972        // `tatara_lisp::read` at parse time) — the lifted
3973        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
3974        // `:exe` is the nix-built executable surface (per the canonical
3975        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
3976        // error message documents and every in-tree
3977        // `caixa_with_code_paths` positive control uses) — its file-type
3978        // contract is "nix-build derivation output", not a typed source
3979        // file, so [`CodePathFileType::None`] opts the slot out of any
3980        // file-type gate. `:servicos` is the `.computeunit.yaml`
3981        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
3982        // renderers consume each entry through `serde_yaml::from_str` as
3983        // a typed `ComputeUnit` CR) — the lifted
3984        // [`is_computeunit_yaml_extension`] predicate gates the compound
3985        // `.computeunit.yaml` suffix. All three axes are surfaced through
3986        // the same iteration so the sandbox-shape + duplicate gates
3987        // apply uniformly; the typed file-type dispatch fires per-slot
3988        // exactly where the downstream consumer's accepted set demands
3989        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
3990        // compounding lift on the peer 64772a9 `:bibliotecas`
3991        // `.lisp`-gate trajectory — the second of the three code-path
3992        // axes to land on a typed compound-suffix gate, with the same
3993        // self-locating per-slot diagnostic shape every peer per-axis
3994        // file-type lift uses (`*NonLispExtension { slot, path }` /
3995        // `*NonComputeUnitYamlExtension { slot, path }`).
3996        for (slot, list, file_type) in [
3997            (
3998                ":bibliotecas",
3999                &self.bibliotecas,
4000                CodePathFileType::LispSource,
4001            ),
4002            (":exe", &self.exe, CodePathFileType::None),
4003            (
4004                ":servicos",
4005                &self.servicos,
4006                CodePathFileType::ComputeUnitYaml,
4007            ),
4008        ] {
4009            // Per-slot set-not-multiset gate on the typed code-path axis.
4010            // Every peer Vec-shaped author-supplied list past validate is
4011            // a set, not a multiset: `:membros :caixa`
4012            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
4013            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
4014            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
4015            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
4016            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
4017            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
4018            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
4019            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
4020            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
4021            // the three code-path lists are the last Vec-shaped author-
4022            // supplied slots on the typed Caixa surface still admitting a
4023            // duplicate entry silently. Scope is per-list (`:bibliotecas`
4024            // duplicates are flagged within `:bibliotecas`, not across
4025            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
4026            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
4027            // legitimate dev-vs-runtime shape on the dep axis, fenced
4028            // separately by [`crate::dep::validate_no_self_dep`]). On the
4029            // code-path axis a cross-slot collision is structurally
4030            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
4031            // fence — `:exe` and `:servicos` entries are confined to their
4032            // own directory trees, so the only way a string could appear
4033            // on two code-path lists is the (rare, structurally invalid)
4034            // case where `:bibliotecas` carries an `"exe/<x>"` or
4035            // `"servicos/<x>.yaml"`-shaped path.
4036            //
4037            // Without the gate three authoring footguns silently passed:
4038            //
4039            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
4040            //     canonical copy-paste-the-wrong-file footgun. `feira
4041            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
4042            //     list and re-parses the same file twice, wasting work
4043            //     and silently masking the author's intent to declare a
4044            //     *second* biblioteca.
4045            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
4046            //     Binario surface. The future `caixa-flake` `nix flake`
4047            //     emitter that materializes each `:exe` entry as a flake
4048            //     `packages.<exe-name>` derivation would collide on the
4049            //     duplicate package name and surface a flake-eval error
4050            //     far from the source `caixa.lisp`.
4051            //   - `:servicos ("servicos/x.computeunit.yaml"
4052            //     "servicos/x.computeunit.yaml")` — the same footgun on
4053            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
4054            //     renderers already refuse `:servicos.len() != 1` with
4055            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
4056            //     that diagnostic surfaces "too many servicos" without
4057            //     naming "duplicate entry" — the typed self-locating
4058            //     "which entry is the duplicate" framing only lands at
4059            //     this gate.
4060            //
4061            // Same `seen.insert(entry.as_str())` shape every peer per-list
4062            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
4063            // 86c769b, `:deps` 359fba5) and the same "structural shape
4064            // checks fire before the duplicate check on the same entry"
4065            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
4066            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
4067            // empty entry first, not the duplicate on the later pair).
4068            let mut seen = std::collections::HashSet::new();
4069            for entry in list {
4070                let path = Path::new(entry);
4071                match is_sandboxed_relative_path(path) {
4072                    Ok(()) => {}
4073                    Err(PathShapeViolation::Empty) => {
4074                        return Err(ManifestError::CodePathEmpty { slot });
4075                    }
4076                    Err(PathShapeViolation::Absolute) => {
4077                        return Err(ManifestError::CodePathAbsolute {
4078                            slot,
4079                            path: path.to_path_buf(),
4080                        });
4081                    }
4082                    Err(PathShapeViolation::ParentEscape) => {
4083                        return Err(ManifestError::CodePathParentEscape {
4084                            slot,
4085                            path: path.to_path_buf(),
4086                        });
4087                    }
4088                }
4089                // The per-slot file-type gate dispatched through the
4090                // typed [`CodePathFileType`] selector above. Each variant
4091                // routes to the lifted predicate the downstream consumer
4092                // demands:
4093                //
4094                //   - [`LispSource`] → [`is_lisp_extension`] for
4095                //     `:bibliotecas` (the `feira build` loop's
4096                //     `tatara_lisp::read` consumer);
4097                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
4098                //     for `:servicos` (the caixa-helm / caixa-flux
4099                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
4100                //     accepted set);
4101                //   - [`None`] for `:exe` — the nix-build derivation-
4102                //     output axis has no terminating-extension contract.
4103                //
4104                // Fires after the sandbox-shape arms so a path that is
4105                // *both* sandbox-escaping and wrong-extension surfaces
4106                // the more fundamental sandbox-shape diagnostic first
4107                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
4108                // `ParentEscape` → `NonLispExtension` arm-ordering on
4109                // `:behavior :on-*` c97815a, and `EmptyScript` →
4110                // `AbsoluteScript` → `ParentEscapeScript` →
4111                // `NonLispExtensionScript` on
4112                // `:upgrade-from :state-change :script` 33cc830), and
4113                // before the duplicate gate so the narrower per-entry
4114                // file-type shape dominates the cross-entry uniqueness
4115                // diagnostic (a
4116                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
4117                // `:servicos` surfaces
4118                // `CodePathNonComputeUnitYamlExtension` on the first
4119                // entry rather than `CodePathDuplicate` on the pair —
4120                // peer with the 64772a9 `:bibliotecas`
4121                // `("lib/x.txt" "lib/x.txt")` ordering).
4122                match file_type {
4123                    CodePathFileType::None => {}
4124                    CodePathFileType::LispSource => {
4125                        if !is_lisp_extension(path) {
4126                            return Err(ManifestError::CodePathNonLispExtension {
4127                                slot,
4128                                path: path.to_path_buf(),
4129                            });
4130                        }
4131                    }
4132                    CodePathFileType::ComputeUnitYaml => {
4133                        if !is_computeunit_yaml_extension(path) {
4134                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
4135                                slot,
4136                                path: path.to_path_buf(),
4137                            });
4138                        }
4139                    }
4140                }
4141                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
4142                    ManifestError::CodePathDuplicate {
4143                        slot,
4144                        path: path.to_path_buf(),
4145                    }
4146                })?;
4147            }
4148        }
4149        Ok(())
4150    }
4151
4152    /// Reject `:etiquetas` lists with an empty entry or with two entries
4153    /// agreeing on the same string. `:etiquetas` is the universal
4154    /// registry-search-tag axis on [`Caixa`] (every kind carries the
4155    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4156    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
4157    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
4158    /// a [`std::collections::BTreeSet`] alongside the four substrate-
4159    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
4160    /// Two authoring footguns silently passed validate without this gate:
4161    ///
4162    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
4163    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
4164    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
4165    ///     `chart.metadata.keywords` admits the value without a strict
4166    ///     parser-side gate, but the empty keyword has no operational
4167    ///     meaning — it indexes nothing in the future caixa-registry
4168    ///     search axis and clutters the rendered chart with a no-op tag.
4169    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
4170    ///     copy-paste-the-wrong-tag footgun) silently passed validate
4171    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
4172    ///     at chart render — a "second wins / one silently disappears"
4173    ///     shape divergent from every peer typed-graph set gate
4174    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
4175    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
4176    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4177    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4178    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4179    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
4180    ///     on `:upgrade-from`, the per-instruction-class singularity
4181    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
4182    ///     [`crate::UpgradeError::DuplicateStateChange`] /
4183    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
4184    ///     discipline is uniform: every Vec-shaped author-supplied list
4185    ///     past validate is set-not-multiset, by construction.
4186    ///
4187    /// Past the empty arm the gate enforces the chart-keyword shape
4188    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
4189    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
4190    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
4191    /// continuation. Closes the canonical paste-from-doc footguns the
4192    /// bare empty + duplicate arms left open: paste-from-aligned-doc
4193    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
4194    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
4195    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
4196    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
4197    /// — the author meant three separate list entries), path-separator
4198    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
4199    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
4200    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
4201    /// control bytes that would silently land as malformed search tags
4202    /// in the rendered Chart.yaml `keywords:` array and break the
4203    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
4204    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
4205    /// established on the sibling universal-axis `Vec<String>` surface
4206    /// — the second universal-axis Vec<String> surface to land the
4207    /// empty-first-then-shape-then-duplicate per-entry cascade.
4208    ///
4209    /// Same empty-first cascade discipline every peer per-axis gate
4210    /// uses: the per-entry empty arm fires before the per-entry shape
4211    /// arm fires before the cross-entry duplicate arm, so an
4212    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
4213    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
4214    /// has no value" defect) before either the shape or the duplicate
4215    /// diagnostic. Walks the list in declaration order so the
4216    /// first-collision diagnostic surfaces the lexicographically-
4217    /// earliest offending position, peer with every other duplicate
4218    /// gate on this surface.
4219    ///
4220    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
4221    /// caixa-build gate alongside the peer universal gates
4222    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4223    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
4224    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
4225    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4226    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4227    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4228    /// slot sets. The future caixa-registry search axis can reach for
4229    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
4230    /// chart-keyword-shaped string without re-deriving the precondition.
4231    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
4232        let mut seen = std::collections::HashSet::new();
4233        for etiqueta in self.etiquetas() {
4234            if etiqueta.is_empty() {
4235                return Err(ManifestError::EtiquetaEmpty);
4236            }
4237            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
4238                ManifestError::EtiquetaInvalid {
4239                    etiqueta: etiqueta.clone(),
4240                    reason,
4241                }
4242            })?;
4243            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
4244                ManifestError::EtiquetaDuplicate {
4245                    etiqueta: etiqueta.clone(),
4246                }
4247            })?;
4248        }
4249        Ok(())
4250    }
4251
4252    /// Reject `:autores` lists with an empty entry or with two entries
4253    /// agreeing on the same string. `:autores` is the universal
4254    /// maintainer-axis on [`Caixa`] (every kind carries the
4255    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4256    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
4257    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
4258    /// to a `Maintainer { name, email: None }` without dedup). Two
4259    /// authoring footguns silently passed validate without this gate:
4260    ///
4261    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
4262    ///     blank-doc footgun) rendered as
4263    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
4264    ///     empty maintainer name has no operational meaning — it
4265    ///     identifies no one in the substrate's authorship index and
4266    ///     clutters the rendered chart with a no-op maintainer.
4267    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
4268    ///     the copy-paste-the-wrong-author footgun) silently passed
4269    ///     validate and rendered as two identical maintainer entries.
4270    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
4271    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
4272    ///     rendered `keywords:` array at chart-render time), the
4273    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
4274    ///     entries stack verbatim in the chart, divergent from every
4275    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
4276    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
4277    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4278    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4279    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4280    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
4281    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
4282    ///     `:etiquetas`).
4283    ///
4284    /// Past the empty arm the gate enforces the chart-maintainer-name
4285    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
4286    /// the structural single-line printable-UTF-8 floor every realistic
4287    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
4288    /// or trailing whitespace, no ASCII control characters anywhere,
4289    /// Unicode bytes accepted. Closes the canonical paste-from-doc
4290    /// footguns the bare empty + duplicate arms left open:
4291    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
4292    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
4293    /// pasted a multi-line block of author records into one `:autores`
4294    /// entry instead of splitting into one entry per author),
4295    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
4296    /// and the paste-from-binary-blob control bytes that would silently
4297    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
4298    /// `maintainers:` array. Mirrors the shape-predicate cascade
4299    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4300    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
4301    /// establish past their own empty arms on the sibling universal-axis
4302    /// `Option<String>` surfaces — the first universal-axis Vec<String>
4303    /// surface to land the empty-first-then-shape-then-duplicate per-entry
4304    /// cascade.
4305    ///
4306    /// Same empty-first cascade discipline every peer per-axis gate
4307    /// uses: the per-entry empty arm fires before the per-entry shape
4308    /// arm before the cross-entry duplicate arm. Walks the list in
4309    /// declaration order so the first-collision diagnostic surfaces the
4310    /// lexicographically-earliest offending position, peer with every
4311    /// other duplicate gate on this surface.
4312    ///
4313    /// Universal-axis (every kind carries `:autores`), so wired at the
4314    /// caixa-build gate alongside the peer universal gates
4315    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4316    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4317    /// [`Self::validate_code_paths`] — before the kind-coherence gates
4318    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4319    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4320    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4321    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4322    /// slot sets.
4323    pub fn validate_autores(&self) -> Result<(), ManifestError> {
4324        let mut seen = std::collections::HashSet::new();
4325        for autor in self.autores() {
4326            if autor.is_empty() {
4327                return Err(ManifestError::AutorEmpty);
4328            }
4329            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
4330                ManifestError::AutorInvalid {
4331                    autor: autor.clone(),
4332                    reason,
4333                }
4334            })?;
4335            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
4336                ManifestError::AutorDuplicate {
4337                    autor: autor.clone(),
4338                }
4339            })?;
4340        }
4341        Ok(())
4342    }
4343
4344    /// Reject `:repositorio` values whose shape the shared
4345    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
4346    /// `repositorio: Option<String>` slot on [`Caixa`] is the
4347    /// universal git-shaped homepage axis every kind carries — the
4348    /// substrate routes the same string through two load-bearing
4349    /// consumers:
4350    ///
4351    ///   - [`caixa-helm`] folds it verbatim into the rendered
4352    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
4353    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
4354    ///     the chart `README.md` `repo = …` interpolation
4355    ///     (`caixa-helm/src/lib.rs:359`).
4356    ///   - [`caixa-flux`] folds it verbatim into the standalone
4357    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
4358    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
4359    ///     `GitRepository.spec.url` the cluster's source-controller
4360    ///     polls — the load-bearing deploy-time axis.
4361    ///
4362    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4363    /// substitute a placeholder when the slot is absent (`None` → the
4364    /// fallback fires); a `Some("")` *skips the fallback* and silently
4365    /// passes the empty string through to `Chart.yaml home: ""` /
4366    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
4367    /// controller both reject the empty URL far from the source
4368    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
4369    /// Similarly a malformed `:repositorio` (whitespace, control char,
4370    /// missing `:` separator, leading `-`) silently lands in the
4371    /// rendered artifacts and breaks at `git clone` / `helm template`
4372    /// / `flux reconcile` time.
4373    ///
4374    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
4375    /// same shared predicate the peer [`crate::DepSource::validate`]
4376    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
4377    /// gate the two `git URL`-shaped surfaces on the typed Caixa
4378    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
4379    /// structurally equivalent: every value past validate is
4380    /// guaranteed-acceptable by the predicate's union of constraints
4381    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
4382    /// control chars, ASCII only, no leading `:`, contains a `:`
4383    /// separator). The predicate accepts every documented authoring
4384    /// shape — `github:org/repo` shorthand, `https://host/path`,
4385    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
4386    /// scp-style SSH, `file:///path` — and refuses the canonical
4387    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
4388    /// injection footguns at validate time. Maps the predicate's
4389    /// `String` reason verbatim into the
4390    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
4391    /// offending value + parser-shaped reason so the diagnostic is
4392    /// self-locating (the author can grep their `caixa.lisp` for
4393    /// `:repositorio "<value>"` and fix it in one edit).
4394    ///
4395    /// `None` (the canonical "omit the slot to express no published
4396    /// homepage" shape) is accepted trivially — the gate is a no-op
4397    /// when the author didn't declare a value. `Some("")` is gated by
4398    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
4399    /// shape predicate is consulted, mirroring the empty-first cascade
4400    /// every peer per-axis identity gate uses
4401    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
4402    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
4403    /// [`crate::DepError::FonteRepoEmpty`] →
4404    /// [`crate::DepError::FonteRepoInvalid`]).
4405    ///
4406    /// Universal-axis (every kind carries `:repositorio`), so wired at
4407    /// the caixa-build gate alongside the peer universal gates
4408    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4409    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4410    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
4411    /// before the kind-coherence gates
4412    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4413    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4414    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4415    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4416    /// specific slot sets.
4417    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
4418        let Some(s) = self.repositorio() else {
4419            return Ok(());
4420        };
4421        if s.is_empty() {
4422            return Err(ManifestError::RepositorioEmpty);
4423        }
4424        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
4425            repositorio: s.to_string(),
4426            reason,
4427        })
4428    }
4429
4430    /// Reject `:descricao` values that are the empty string. The flat
4431    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
4432    /// free-form-prose homepage axis every kind carries — the
4433    /// substrate routes the same string through two load-bearing
4434    /// consumers in the [`caixa-helm`] renderer:
4435    ///
4436    ///   - `build_chart_yaml` folds it verbatim into the rendered
4437    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
4438    ///     field (`caixa-helm/src/lib.rs:232-235`).
4439    ///   - `build_readme` folds it verbatim into the rendered chart
4440    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
4441    ///
4442    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4443    /// substitute a `caixa.nome`-derived placeholder when the slot is
4444    /// absent (`None` → the fallback fires); a `Some("")` *skips the
4445    /// fallback* and silently passes the empty string through to
4446    /// `Chart.yaml description: ""` / a blank chart `README.md`
4447    /// header. Helm's chart spec requires a non-empty `description:`
4448    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
4449    /// `WARNING [chart.metadata.description]: description is required`),
4450    /// so the empty `Some("")` silently lands in the rendered
4451    /// artifacts and breaks at `helm lint` / `helm install` time far
4452    /// from the source `caixa.lisp`, with no field naming the
4453    /// offending `:descricao`.
4454    ///
4455    /// `None` (the canonical "omit the slot to defer to the renderer's
4456    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
4457    /// the gate is a no-op when the author didn't declare a value.
4458    /// `Some("")` is gated by the narrower
4459    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
4460    /// shape every peer per-axis empty gate uses
4461    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4462    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4463    /// [`ManifestError::RepositorioEmpty`]).
4464    ///
4465    /// Universal-axis (every kind carries `:descricao`), so wired at
4466    /// the caixa-build gate alongside the peer universal gates
4467    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4468    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4469    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4470    /// [`Self::validate_code_paths`] — before the kind-coherence
4471    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4472    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4473    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4474    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4475    /// specific slot sets.
4476    ///
4477    /// Past the empty arm the gate enforces the chart-description
4478    /// shape predicate via [`crate::render::is_chart_description_shape`]:
4479    /// the structural single-line UTF-8 floor every realistic chart
4480    /// description in the wild matches — 1..=512 bytes, no leading
4481    /// or trailing whitespace, no ASCII control characters anywhere
4482    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
4483    /// carriage return, and every other control byte), Unicode
4484    /// continuation bytes accepted (the canonical fixtures carry
4485    /// `→` and `—`). Closes the canonical paste-from-doc footguns
4486    /// the bare empty-arm gate left open: paste-from-aligned-doc
4487    /// leading / trailing whitespace (`" Checkout flow."`,
4488    /// `"Checkout flow. "`), paste-from-multiline-doc newline
4489    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
4490    /// (`"Checkout\rflow."`), tab-from-aligned-doc
4491    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
4492    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
4493    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
4494    /// [`Self::validate_edicao`] establish past their own empty arms
4495    /// on the sibling universal-axis `Option<String>` Caixa-level
4496    /// value-shape surfaces.
4497    ///
4498    /// The empty-first cascade discipline mirrors every peer per-axis
4499    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
4500    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
4501    /// diagnostic surfaces on `Some("")` rather than the broader
4502    /// shape-predicate diagnostic — peer with how
4503    /// [`ManifestError::LicencaEmpty`] runs before
4504    /// [`ManifestError::LicencaInvalid`],
4505    /// [`ManifestError::EdicaoEmpty`] runs before
4506    /// [`ManifestError::EdicaoInvalid`],
4507    /// [`ManifestError::RepositorioEmpty`] runs before
4508    /// [`ManifestError::RepositorioInvalid`].
4509    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
4510        let Some(s) = self.descricao() else {
4511            return Ok(());
4512        };
4513        if s.is_empty() {
4514            return Err(ManifestError::DescricaoEmpty);
4515        }
4516        crate::render::is_chart_description_shape(s).map_err(|reason| {
4517            ManifestError::DescricaoInvalid {
4518                descricao: s.to_string(),
4519                reason,
4520            }
4521        })?;
4522        Ok(())
4523    }
4524
4525    /// Reject `:licenca` values that are the empty string. The flat
4526    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
4527    /// SPDX-shaped license-expression axis every kind carries — the
4528    /// substrate routes the same string through the [`caixa-helm`]
4529    /// renderer's `build_readme` which folds it verbatim into the
4530    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
4531    /// section (`caixa-helm/src/lib.rs:361`) via
4532    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
4533    /// fallback only fires on `None`; a `Some("")` *skips the
4534    /// fallback* and silently passes the empty string through to a
4535    /// chart `README.md` whose `License` section renders as the bare
4536    /// trailing period (`.\n`) — peer footgun with the
4537    /// `Some("")`-skips-`unwrap_or_else` shape the
4538    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
4539    /// gates close on the sibling free-form-prose and git-URL axes.
4540    ///
4541    /// `None` (the canonical "omit the slot to defer to the
4542    /// renderer's `MIT` fallback" shape every existing fixture
4543    /// carries) is accepted trivially — the gate is a no-op when the
4544    /// author didn't declare a value. `Some("")` is gated by the
4545    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
4546    /// empty-arm shape every peer per-axis empty gate uses
4547    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4548    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4549    /// [`ManifestError::RepositorioEmpty`],
4550    /// [`ManifestError::DescricaoEmpty`]).
4551    ///
4552    /// Universal-axis (every kind carries `:licenca`), so wired at
4553    /// the caixa-build gate alongside the peer universal gates
4554    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4555    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4556    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4557    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
4558    /// — before the kind-coherence gates
4559    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4560    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4561    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4562    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4563    /// specific slot sets.
4564    ///
4565    /// Past the empty arm the gate enforces the SPDX-expression shape
4566    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
4567    /// structural alphabet floor every realistic SPDX expression in
4568    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
4569    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
4570    /// single ASCII space (token separator). Closes the canonical
4571    /// paste-from-doc footguns the bare empty-arm gate left open:
4572    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
4573    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
4574    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
4575    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
4576    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
4577    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
4578    /// Apache-2.0"`), and semicolon-list-separator confusion
4579    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
4580    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
4581    /// establish past their own empty arms.
4582    ///
4583    /// The empty-first cascade discipline mirrors every peer per-axis
4584    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
4585    /// [`ManifestError::LicencaInvalid`], so the narrower empty
4586    /// diagnostic surfaces on `Some("")` rather than the broader
4587    /// shape-predicate diagnostic — peer with how
4588    /// [`ManifestError::EdicaoEmpty`] runs before
4589    /// [`ManifestError::EdicaoInvalid`],
4590    /// [`ManifestError::RepositorioEmpty`] runs before
4591    /// [`ManifestError::RepositorioInvalid`].
4592    ///
4593    /// A future tightening on this axis can extend the alphabet
4594    /// floor into a full SPDX expression parser + license-id
4595    /// allowlist (rejecting alphabet-valid values that don't name a
4596    /// real SPDX license identifier — e.g., `"NotAReal"` is
4597    /// alphabet-valid but no `NotAReal` license-id exists). That
4598    /// parser only becomes meaningful past a real SPDX-spec
4599    /// dependency; this gate establishes the structural floor by
4600    /// refusing every non-SPDX-alphabet value at validate time.
4601    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
4602        let Some(s) = self.licenca() else {
4603            return Ok(());
4604        };
4605        if s.is_empty() {
4606            return Err(ManifestError::LicencaEmpty);
4607        }
4608        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
4609            ManifestError::LicencaInvalid {
4610                licenca: s.to_string(),
4611                reason,
4612            }
4613        })?;
4614        Ok(())
4615    }
4616
4617    /// Reject `:edicao` values that are the empty string. The flat
4618    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
4619    /// language-edition axis every kind carries — it determines the
4620    /// tatara-lisp macro surface + compatibility flags the substrate
4621    /// applies when building a caixa, and lands verbatim in the
4622    /// `Caixa::template` author-time scaffold (the canonical
4623    /// `:edicao "2026"` line every `feira init` emits via
4624    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
4625    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
4626    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
4627    /// `caixa-core/src/render.rs:2510`) via
4628    /// `edicao: Some("2026".into())`.
4629    ///
4630    /// `None` (the canonical "omit the slot to defer to the
4631    /// substrate's default edition" shape every existing
4632    /// [`caixa-resolver`] integration test fixture carries via
4633    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
4634    /// is accepted trivially — the gate is a no-op when the author
4635    /// didn't declare a value. `Some("")` is gated by the narrower
4636    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
4637    /// shape every peer per-axis empty gate uses
4638    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4639    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4640    /// [`ManifestError::RepositorioEmpty`],
4641    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
4642    ///
4643    /// Universal-axis (every kind carries `:edicao`), so wired at
4644    /// the caixa-build gate alongside the peer universal gates
4645    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4646    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4647    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4648    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4649    /// [`Self::validate_code_paths`] — before the kind-coherence
4650    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4651    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4652    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4653    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4654    /// specific slot sets.
4655    ///
4656    /// Past the empty arm the gate enforces the canonical year-shape
4657    /// predicate: every documented tatara-lisp edition is a 4-digit
4658    /// ASCII decimal year (`"2026"` is the only edition currently
4659    /// minted; future-introduced siblings will follow the same
4660    /// shape, peer with Cargo's `[package] edition` grammar which
4661    /// every value Cargo has ever accepted matches — `"2015"`,
4662    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
4663    /// 4 ASCII decimal bytes is rejected with the narrower
4664    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
4665    /// shape-predicate cascade [`Self::validate_repositorio`]
4666    /// establishes past its own empty arm
4667    /// ([`ManifestError::RepositorioEmpty`] →
4668    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
4669    /// paste-from-doc footguns the bare empty-arm gate left open:
4670    ///
4671    ///   - leading / trailing whitespace from a paste-from-doc
4672    ///     (`"2026 "`, `" 2026"`)
4673    ///   - control characters / CRLF from a paste-from-multiline-doc
4674    ///     (`"2026\n"`)
4675    ///   - non-ASCII look-alikes from a fullwidth keyboard
4676    ///     (`"2026"`) which would silently land as a non-ASCII
4677    ///     string in the rendered caixa.lisp
4678    ///   - free-form non-year values (`"x"`, `"latest"`,
4679    ///     `"nightly"`) that have no operational meaning on the
4680    ///     substrate's build-time edition selector
4681    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
4682    ///     `"r2026"`) — common version-tag idioms that don't apply
4683    ///     to the year-shaped edition axis
4684    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
4685    ///     edition is a year, not a fractional version
4686    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
4687    ///     `"00026"`) that don't name a year
4688    ///
4689    /// `None` (the canonical "omit the slot to defer to the
4690    /// substrate's default edition" shape every existing
4691    /// [`caixa-resolver`] integration test fixture carries via
4692    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
4693    /// is accepted trivially — the gate is a no-op when the author
4694    /// didn't declare a value. The empty-first cascade discipline
4695    /// mirrors every peer per-axis identity gate:
4696    /// [`ManifestError::EdicaoEmpty`] runs before
4697    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
4698    /// diagnostic surfaces on `Some("")` rather than the broader
4699    /// shape-predicate diagnostic — peer with how
4700    /// [`ManifestError::NomeEmpty`] runs before
4701    /// [`ManifestError::NomeInvalid`],
4702    /// [`ManifestError::VersaoEmpty`] runs before
4703    /// [`ManifestError::VersaoInvalid`],
4704    /// [`ManifestError::RepositorioEmpty`] runs before
4705    /// [`ManifestError::RepositorioInvalid`].
4706    ///
4707    /// A future tightening on this axis can extend the shape
4708    /// predicate into a known-edition allowlist (rejecting
4709    /// year-shaped values that don't name a tatara-lisp edition
4710    /// the substrate actually understands — e.g., `"1999"` is
4711    /// year-shaped but no `1999` edition exists). That allowlist
4712    /// only becomes meaningful past the introduction of a sibling
4713    /// edition to `"2026"`; this gate establishes the structural
4714    /// floor by refusing every non-year-shaped value at validate
4715    /// time.
4716    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
4717        let Some(s) = self.edicao() else {
4718            return Ok(());
4719        };
4720        if s.is_empty() {
4721            return Err(ManifestError::EdicaoEmpty);
4722        }
4723        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
4724            return Err(ManifestError::EdicaoInvalid {
4725                edicao: s.to_string(),
4726                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
4727            });
4728        }
4729        Ok(())
4730    }
4731
4732    /// Compose the supervisor-related flat slots into a single
4733    /// [`SupervisorSpec`] for validation. Returns `None` when the
4734    /// caixa isn't a `:kind Supervisor`.
4735    ///
4736    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
4737    /// simple (one form, no nested `:supervisor (…)` block); this view
4738    /// is the "typed shape" the operator + supervisor reconciler
4739    /// consume.
4740    #[must_use]
4741    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
4742        if !self.kind().is_supervisor() {
4743            return None;
4744        }
4745        // Fold through the shared `supervisor::duration_codec::parse`
4746        // — the same parser the serde-routed `with = "duration_codec"`
4747        // on `SupervisorSpec::restart_window`, the `:politicas
4748        // :timeout` codec, and the `:politicas :circuit-breaker
4749        // :window` codec all consume. The prior inline f64-shaped
4750        // duplicate (`parse_window_inline`) admitted every magnitude
4751        // the integer-magnitude gate (1c55a2a) rejects on the three
4752        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
4753        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
4754        // `None` (i.e. "no reset"), divergent from the shared codec's
4755        // integer-magnitude discipline by construction. The fold
4756        // closes the divergence: every value the typed
4757        // `SupervisorSpec` carries past `supervisor_view` is in the
4758        // shared codec's accepted set. The `.ok()` here preserves the
4759        // existing soft-swallow shape on this view-construction path;
4760        // the new [`Caixa::validate_restart_window`] (sibling of
4761        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
4762        // the offending raw string at build time so authoring tools
4763        // (`feira lint`, the future layout-side wire-up) surface a
4764        // self-locating diagnostic instead of a silently dropped
4765        // window.
4766        let restart_window = self
4767            .restart_window()
4768            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
4769        Some(SupervisorSpec {
4770            estrategia: self.estrategia().unwrap_or_default(),
4771            max_restarts: self.max_restarts().unwrap_or(5),
4772            restart_window,
4773            children: self.children().to_vec(),
4774        })
4775    }
4776
4777    /// A minimal starter manifest emitted by `feira init`.
4778    #[must_use]
4779    pub fn template(nome: &str) -> String {
4780        format!(
4781            "(defcaixa\n  \
4782               :nome        {nome:?}\n  \
4783               :versao      \"0.1.0\"\n  \
4784               :kind        Biblioteca\n  \
4785               :edicao      \"2026\"\n  \
4786               :descricao   \"FIXME — describe this caixa\"\n  \
4787               :autores     ()\n  \
4788               :etiquetas   ()\n  \
4789               :deps        ()\n  \
4790               :deps-dev    ()\n  \
4791               :bibliotecas (\"lib/{nome}.lisp\"))\n"
4792        )
4793    }
4794
4795    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
4796    /// back after mutation (e.g. `feira add`).
4797    ///
4798    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
4799    /// The derive-macro `compile_from_sexp` path is the inverse, so any
4800    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
4801    #[must_use]
4802    pub fn to_lisp(&self) -> String {
4803        let json = serde_json::to_value(self).expect("Caixa serialize");
4804        let sexp = tatara_lisp::domain::json_to_sexp(&json);
4805        let tatara_lisp::Sexp::List(items) = sexp else {
4806            return format!("(defcaixa {sexp})\n");
4807        };
4808        let mut out = String::from("(defcaixa");
4809        let mut i = 0;
4810        while i + 1 < items.len() {
4811            out.push_str("\n  ");
4812            out.push_str(&items[i].to_string());
4813            out.push(' ');
4814            out.push_str(&items[i + 1].to_string());
4815            i += 2;
4816        }
4817        out.push_str(")\n");
4818        out
4819    }
4820}
4821
4822/// Errors raised by top-level [`Caixa`] validators that don't fit
4823/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
4824/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
4825/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
4826/// through every substrate-side artifact's `metadata.name` /
4827/// version derivation.
4828///
4829/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
4830/// doc-comment anticipates) can hold one of each per-axis error
4831/// family without reshaping individual diagnostics; this enum is
4832/// the first such per-Caixa-identity family.
4833#[derive(Debug, Error, PartialEq, Eq)]
4834pub enum ManifestError {
4835    #[error(
4836        ":nome is empty (every caixa must name itself; the value flows \
4837         into every K8s artifact's `metadata.name` derivation and into \
4838         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
4839    )]
4840    NomeEmpty,
4841    #[error(
4842        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
4843         apiserver enforces this rule on every `metadata.name` the \
4844         caixa's substrate-side renderers derive from `:nome` — the \
4845         `lareira-<nome>` Helm chart name, the programs.yaml entry \
4846         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
4847         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
4848         name; use a lowercase alphanumeric + hyphen identifier like \
4849         `\"checkout\"` or `\"cart-v2\"`)"
4850    )]
4851    NomeInvalid { nome: String, reason: String },
4852    #[error(
4853        ":nome {nome:?} overflows the joint-length budget on the canonical \
4854         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
4855         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
4856         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
4857         `chart:` slot, `caixa-tatara`'s `release_name` + \
4858         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
4859         joint name through the canonical `lareira_chart_name` helper, and \
4860         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
4861         DNS-1123 label cap on every chart-name-derived `metadata.name` \
4862         reject any joint name exceeding 63 bytes; the narrower \
4863         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
4864         arm gates the chart-name budget downstream renderers inherit)"
4865    )]
4866    NomeChartNameBudgetExceeded { nome: String, reason: String },
4867    #[error(
4868        ":versao is empty (every caixa must pin its own version; the value flows \
4869         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
4870         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
4871         `:latest` tags, the lacre closure's `concrete_versao`, and the \
4872         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
4873    )]
4874    VersaoEmpty,
4875    #[error(
4876        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
4877         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
4878         with optional `-prerelease` and `+build` — across every artifact derived \
4879         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
4880         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
4881         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
4882         and the `:upgrade-from :from` peers that match against this exact shape; \
4883         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
4884         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
4885         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
4886    )]
4887    VersaoInvalid { versao: String, reason: String },
4888    #[error(
4889        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
4890         substrate consumes this string through the shared \
4891         `supervisor::duration_codec` — the same parser routed via `with = \
4892         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
4893         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
4894         the canonical authoring form is `<integer><unit>` where the unit is one \
4895         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
4896         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
4897         Without this gate a malformed `:restart-window` silently produced a \
4898         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
4899         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
4900         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
4901         layer with the offending value named verbatim. Omit the slot entirely to \
4902         express \"no reset\"; carry a positive integer duration to express the \
4903         sliding window)"
4904    )]
4905    RestartWindowMalformed {
4906        restart_window: String,
4907        reason: String,
4908    },
4909    #[error(
4910        "{slot} entry is an empty path string — every {slot} entry must name \
4911         a file relative to the caixa root; omit the entry to omit the file \
4912         (the layout checker's `root.join(\"\")` resolves to the caixa root \
4913         itself, so an empty entry silently aliases the project root as a \
4914         declared {slot} file, then fails downstream at parse / existence \
4915         time with a diagnostic that names the root rather than the offending \
4916         entry)"
4917    )]
4918    CodePathEmpty { slot: &'static str },
4919    #[error(
4920        "{slot} entry {} is an absolute path — entries must be relative to \
4921         the caixa root, since `Path::join` replaces the base with an absolute \
4922         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
4923         outside the caixa root sandbox; rewrite the entry as a relative path \
4924         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
4925         `\"servicos/<name>.computeunit.yaml\"`)",
4926        path.display()
4927    )]
4928    CodePathAbsolute { slot: &'static str, path: PathBuf },
4929    #[error(
4930        "{slot} entry {} contains a `..` component — entries must not traverse \
4931         above the caixa root (the layout's `starts_with(<dir>)` fence on \
4932         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
4933         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
4934         has no such fence, so a leading `..` escapes unconditionally if the \
4935         resolved target happens to exist)",
4936        path.display()
4937    )]
4938    CodePathParentEscape { slot: &'static str, path: PathBuf },
4939    #[error(
4940        "{slot} entry {} does not terminate in the `.lisp` extension — every \
4941         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
4942         loop reads through `tatara_lisp::read` at parse time, so any other \
4943         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
4944         structurally a parser error far from the source caixa.lisp, with \
4945         no field naming the offending `:bibliotecas` entry. Pin a relative \
4946         path under the caixa root whose terminating extension is \
4947         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
4948         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
4949         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
4950         (33cc830) axes already carry through the same lifted \
4951         `is_lisp_extension` predicate",
4952        path.display()
4953    )]
4954    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
4955    #[error(
4956        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
4957         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
4958         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
4959         through `serde_yaml::from_str` at chart / FluxCD bundle render \
4960         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
4961         off-by-one-segment `.computeunit-yaml`, the editor-backup \
4962         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
4963         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
4964         source caixa.lisp, with no field naming the offending `:servicos` \
4965         entry. Pin a relative path under the caixa root whose terminating \
4966         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
4967         `\"servicos/<name>.computeunit.yaml\"`, \
4968         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
4969         contract the sibling `:bibliotecas` axis (64772a9) already carries \
4970         on the tatara-lisp-source axis through the peer lifted \
4971         `is_lisp_extension` predicate, here on the compound-suffix axis \
4972         `Path::extension` can't express on its own through the lifted \
4973         `is_computeunit_yaml_extension` predicate",
4974        path.display()
4975    )]
4976    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
4977    #[error(
4978        "{slot} entry {} appears more than once (the code-path list is \
4979         a set, not a multiset; every peer Vec-shaped author-supplied \
4980         list past validate is set-not-multiset — `:membros :caixa`, \
4981         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
4982         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
4983         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
4984         code-path lists are the last Vec-shaped author-supplied slots on \
4985         the typed Caixa surface still admitting a duplicate entry. \
4986         `:bibliotecas` duplicates re-parse the same file at \
4987         `feira build` time and silently mask the author's intent to \
4988         declare a *second* biblioteca; `:exe` duplicates collide on the \
4989         flake `packages.<name>` derivation key at the future \
4990         `caixa-flake` materializer; `:servicos` duplicates surface as the \
4991         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
4992         rejection far from the source `caixa.lisp`. Drop the duplicate \
4993         or rename it to the actual second file intended)",
4994        path.display()
4995    )]
4996    CodePathDuplicate { slot: &'static str, path: PathBuf },
4997    #[error(
4998        ":etiquetas entry is empty (every tag must carry a non-empty \
4999         registry-search identifier; the empty entry has no operational \
5000         meaning — it indexes nothing in the future caixa-registry search \
5001         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
5002         with a no-op tag; omit the entry to express \"no tag on this \
5003         position\")"
5004    )]
5005    EtiquetaEmpty,
5006    #[error(
5007        ":etiquetas entry {etiqueta:?} appears more than once (the \
5008         registry-search tag set is a set, not a multiset; duplicate \
5009         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
5010         at chart render — a \"second wins / one silently disappears\" \
5011         shape divergent from every peer typed-graph set gate \
5012         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
5013         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
5014         duplicate or rename it to the actual tag intended)"
5015    )]
5016    EtiquetaDuplicate { etiqueta: String },
5017    #[error(
5018        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
5019         {reason} (the substrate consumes this string through the shared \
5020         `crate::render::is_chart_keyword_shape` predicate — the same \
5021         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
5022         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
5023         continuation. The canonical authoring shapes are short kebab-case \
5024         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
5025         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
5026         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
5027         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
5028         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
5029         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
5030         `\"mesh,http,grpc\"` — the author meant to author three separate \
5031         list entries; path-separator confusion `\"caixa/servico\"`; \
5032         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
5033         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
5034         `\"café\"` — every legitimate search tag is strict ASCII; \
5035         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
5036         passed `from_lisp` + `validate_etiquetas` + \
5037         `StandardLayout::verify` and landed in the rendered \
5038         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
5039         malformed search tag — Artifact Hub's keyword index + the future \
5040         caixa-registry's keyword index would either silently drop the \
5041         tag or fail to index it far from the source caixa.lisp; the gate \
5042         moves the diagnostic to the manifest layer with the offending \
5043         value named verbatim)"
5044    )]
5045    EtiquetaInvalid { etiqueta: String, reason: String },
5046    #[error(
5047        ":autores entry is empty (every maintainer must carry a non-empty \
5048         identifier; the empty entry has no operational meaning — it \
5049         identifies no one in the substrate's authorship index and renders \
5050         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
5051         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
5052         omit the entry to express \"no maintainer on this position\")"
5053    )]
5054    AutorEmpty,
5055    #[error(
5056        ":autores entry {autor:?} appears more than once (the maintainer \
5057         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
5058         `maintainers:` rendering does *no* dedup — duplicate entries \
5059         stack verbatim in `Chart.yaml` as two identical \
5060         `Maintainer {{ name, email: None }}` records, divergent from every \
5061         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
5062         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5063         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
5064         rename it to the actual author intended)"
5065    )]
5066    AutorDuplicate { autor: String },
5067    #[error(
5068        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
5069         {reason} (the substrate consumes this string through the shared \
5070         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
5071         single-line-UTF-8 floor every realistic chart maintainer name carries: \
5072         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
5073         characters anywhere, Unicode bytes accepted. The canonical authoring \
5074         shapes are short single-line identifiers like `\"pleme-io\"`, \
5075         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
5076         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
5077         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
5078         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
5079         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
5080         records into one entry instead of splitting into one entry per author; \
5081         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
5082         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
5083         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5084         `validate_autores` + `StandardLayout::verify` and landed in the \
5085         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
5086         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
5087         round-trip — every chart-aware UI (`helm list`, `helm search`, \
5088         Artifact Hub maintainer index) would render the maintainer name in a \
5089         single-line column far from the source caixa.lisp; the gate moves the \
5090         diagnostic to the manifest layer with the offending value named \
5091         verbatim)"
5092    )]
5093    AutorInvalid { autor: String, reason: String },
5094    #[error(
5095        ":repositorio is the empty string (every published caixa names its \
5096         git source via a non-empty `:repositorio` locator — the value \
5097         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
5098         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
5099         `GitRepository.spec.url` via `caixa-flux`'s \
5100         `ClusterBundleOpts::for_caixa`; both consumers' \
5101         `Option::unwrap_or_else` fallbacks only fire when the slot is \
5102         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
5103         `url: \"\"` in the rendered artifacts and breaks at `helm \
5104         template` / FluxCD source-controller reconcile time far from the \
5105         source caixa.lisp; omit the slot entirely to defer to the \
5106         renderer's `https://github.com/pleme-io/<nome>` / \
5107         `caixa.nome`-derived fallback, or carry a canonical authoring \
5108         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
5109         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
5110         `\"file:///path\"`)"
5111    )]
5112    RepositorioEmpty,
5113    #[error(
5114        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
5115         (the substrate consumes this string through the shared \
5116         `crate::render::is_git_repo_url` predicate — the same parser the \
5117         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
5118         value through via `DepSource::validate`; the canonical authoring \
5119         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
5120         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
5121         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
5122         scp-style SSH form. Without this gate a malformed `:repositorio` \
5123         (whitespace from a paste-from-doc; control characters / CRLF \
5124         from a paste-from-multiline-doc; a leading `-` from a \
5125         CLI-argument-injection footgun; a missing `:` separator from a \
5126         bare `org/repo` shape git treats as a relative filesystem path) \
5127         silently landed in the rendered `Chart.yaml home:` and the \
5128         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
5129         FluxCD reconcile time far from the source caixa.lisp; the gate \
5130         moves the diagnostic to the manifest layer with the offending \
5131         value named verbatim)"
5132    )]
5133    RepositorioInvalid { repositorio: String, reason: String },
5134    #[error(
5135        ":descricao is the empty string (every published caixa names \
5136         its purpose via a non-empty `:descricao` summary — the value \
5137         flows verbatim into the rendered `lareira-<nome>` Helm \
5138         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
5139         `build_chart_yaml` and into the chart `README.md` header via \
5140         `build_readme`; both consumers' `Option::unwrap_or_else` \
5141         `caixa.nome`-derived fallbacks only fire when the slot is \
5142         `None`, so an empty `Some(\"\")` silently lands as \
5143         `description: \"\"` / a blank `README.md` header in the \
5144         rendered artifacts and breaks at `helm lint` time \
5145         (`WARNING [chart.metadata.description]: description is \
5146         required` on `apiVersion: v2` charts) far from the source \
5147         caixa.lisp; omit the slot entirely to defer to the \
5148         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
5149         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
5150         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
5151         Servico.\"`)"
5152    )]
5153    DescricaoEmpty,
5154    #[error(
5155        ":descricao {descricao:?} is not a valid chart-description shape: \
5156         {reason} (the substrate consumes this string through the shared \
5157         `crate::render::is_chart_description_shape` predicate — the same \
5158         single-line-UTF-8 floor every realistic chart description carries: \
5159         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
5160         characters anywhere, Unicode prose bytes accepted. The canonical \
5161         authoring shapes are short single-line summaries like `\"Canonical \
5162         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
5163         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
5164         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
5165         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
5166         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
5167         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
5168         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
5169         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5170         `validate_descricao` + `StandardLayout::verify` and landed in the \
5171         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
5172         field + `README.md` header paragraph as a YAML-illegal multi-line \
5173         scalar or a silently-trimmed whitespace round-trip — every \
5174         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
5175         render the description in a single-line column far from the source \
5176         caixa.lisp; the gate moves the diagnostic to the manifest layer \
5177         with the offending value named verbatim)"
5178    )]
5179    DescricaoInvalid { descricao: String, reason: String },
5180    #[error(
5181        ":licenca is the empty string (every published caixa names \
5182         its license via a non-empty `:licenca` SPDX expression — the \
5183         value flows verbatim into the rendered `lareira-<nome>` Helm \
5184         chart's `README.md` `## License` section via `caixa-helm`'s \
5185         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
5186         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
5187         only fires when the slot is `None`, so an empty `Some(\"\")` \
5188         silently lands as a bare trailing period in the rendered \
5189         chart `README.md` `License` section far from the source \
5190         caixa.lisp; omit the slot entirely to defer to the \
5191         renderer's `MIT` fallback, or carry a canonical SPDX \
5192         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
5193         `\"Apache-2.0 OR MIT\"`)"
5194    )]
5195    LicencaEmpty,
5196    #[error(
5197        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
5198         (the substrate consumes this string through the shared \
5199         `crate::render::is_spdx_expression_shape` predicate — the same \
5200         alphabet-floor parser every peer per-axis value-shape gate routes \
5201         its value through; the canonical authoring shapes are single \
5202         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
5203         compound expressions like `\"Apache-2.0 OR MIT\"`, \
5204         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
5205         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
5206         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
5207         like `\"LicenseRef-MyLicense\"` / \
5208         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
5209         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
5210         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
5211         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
5212         a smart-quote paste; underscore-instead-of-hyphen typo \
5213         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
5214         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
5215         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
5216         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
5217         `README.md` `## License` section + a future SPDX-aware \
5218         `Chart.yaml license:` emitter would refuse the value at \
5219         `helm lint` time far from the source caixa.lisp; the gate moves \
5220         the diagnostic to the manifest layer with the offending value \
5221         named verbatim)"
5222    )]
5223    LicencaInvalid { licenca: String, reason: String },
5224    #[error(
5225        ":edicao is the empty string (every published caixa names \
5226         its language edition via a non-empty `:edicao` value — the \
5227         edition determines the tatara-lisp macro surface + \
5228         compatibility flags the substrate applies when building \
5229         the caixa; the canonical `Caixa::template` scaffold every \
5230         `feira init` emits carries `:edicao \"2026\"` verbatim and \
5231         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
5232         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
5233         construction, so an empty `Some(\"\")` silently lands as a \
5234         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
5235         a future renderer-side consumer that folds it through \
5236         `Option::unwrap_or_else` will skip the fallback and pass the \
5237         empty edition through to the substrate's build-time edition \
5238         selector far from the source caixa.lisp; omit the slot \
5239         entirely to defer to the substrate's default edition, or \
5240         carry a canonical edition like `\"2026\"`)"
5241    )]
5242    EdicaoEmpty,
5243    #[error(
5244        ":edicao {edicao:?} is not a valid edition: {reason} (every \
5245         documented tatara-lisp edition is a 4-digit ASCII decimal \
5246         year — `\"2026\"` is the only edition currently minted; \
5247         future-introduced siblings will follow the same shape, peer \
5248         with Cargo's `[package] edition` grammar which every value \
5249         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
5250         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
5251         paste-from-doc footguns silently passed: a trailing space \
5252         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
5253         from a paste-from-multiline-doc, a fullwidth-keyboard \
5254         look-alike (`\"2026\"`), a free-form non-year value \
5255         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
5256         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
5257         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
5258         wrong-length numeric value (`\"26\"`, `\"202\"`, \
5259         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
5260         rendered caixa.lisp and broke at the substrate's \
5261         build-time edition selector far from the source caixa.lisp; \
5262         omit the slot entirely to defer to the substrate's default \
5263         edition, or carry a canonical 4-digit ASCII decimal year \
5264         like `\"2026\"`)"
5265    )]
5266    EdicaoInvalid { edicao: String, reason: String },
5267}
5268
5269#[cfg(test)]
5270mod tests {
5271    use super::*;
5272
5273    #[test]
5274    fn template_round_trips() {
5275        let src = Caixa::template("demo");
5276        let c = Caixa::from_lisp(&src).expect("template must parse");
5277        assert_eq!(c.nome, "demo");
5278        assert_eq!(c.versao, "0.1.0");
5279        assert_eq!(c.kind, CaixaKind::Biblioteca);
5280        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
5281        assert!(c.deps.is_empty());
5282        assert!(c.deps_dev.is_empty());
5283    }
5284
5285    #[test]
5286    fn register_populates_registry() {
5287        Caixa::register();
5288        let kws = tatara_lisp::domain::registered_keywords();
5289        assert!(kws.contains(&"defcaixa"));
5290    }
5291
5292    #[test]
5293    fn to_lisp_round_trips() {
5294        let src = Caixa::template("demo");
5295        let c1 = Caixa::from_lisp(&src).unwrap();
5296        let emitted = c1.to_lisp();
5297        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
5298        assert_eq!(c1, c2);
5299    }
5300
5301    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
5302
5303    #[test]
5304    fn limits_round_trip_via_json() {
5305        use crate::LimitsSpec;
5306        use std::time::Duration;
5307        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5308        c.limits = Some(LimitsSpec {
5309            memory: Some(64 * 1024 * 1024),
5310            fuel: Some(1_000_000),
5311            wall_clock: Some(Duration::from_secs(30)),
5312            cpu: Some(500),
5313        });
5314        let json = serde_json::to_string(&c).unwrap();
5315        assert!(json.contains("\"limits\""));
5316        assert!(json.contains("\"64MiB\""));
5317        assert!(json.contains("\"30s\""));
5318        assert!(json.contains("\"500m\""));
5319        let back: Caixa = serde_json::from_str(&json).unwrap();
5320        assert_eq!(c.limits, back.limits);
5321    }
5322
5323    #[test]
5324    fn behavior_round_trip_via_json() {
5325        use crate::BehaviorSpec;
5326        use std::path::PathBuf;
5327        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5328        c.behavior = Some(BehaviorSpec {
5329            on_init: Some(PathBuf::from("lib/init.lisp")),
5330            on_call: Some(PathBuf::from("lib/handlers.lisp")),
5331            ..Default::default()
5332        });
5333        let json = serde_json::to_string(&c).unwrap();
5334        let back: Caixa = serde_json::from_str(&json).unwrap();
5335        assert_eq!(c.behavior, back.behavior);
5336    }
5337
5338    #[test]
5339    fn upgrade_from_round_trip_via_json() {
5340        use crate::{UpgradeFromEntry, UpgradeInstruction};
5341        use std::path::PathBuf;
5342        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5343        c.upgrade_from = vec![UpgradeFromEntry {
5344            from: "0.1.0".into(),
5345            instructions: vec![
5346                UpgradeInstruction::LoadModule {
5347                    module: "demo".into(),
5348                },
5349                UpgradeInstruction::StateChange {
5350                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
5351                },
5352                UpgradeInstruction::SoftPurge {
5353                    module: "demo-old".into(),
5354                },
5355            ],
5356        }];
5357        let json = serde_json::to_string(&c).unwrap();
5358        let back: Caixa = serde_json::from_str(&json).unwrap();
5359        assert_eq!(c.upgrade_from, back.upgrade_from);
5360    }
5361
5362    #[test]
5363    fn supervisor_view_returns_typed_shape() {
5364        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
5365        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
5366        c.kind = CaixaKind::Supervisor;
5367        c.bibliotecas.clear();
5368        c.estrategia = Some(RestartStrategy::OneForOne);
5369        c.max_restarts = Some(5);
5370        c.restart_window = Some("60s".into());
5371        c.children = vec![ChildSpec {
5372            caixa: "worker".into(),
5373            versao: "^0.1".into(),
5374            restart: RestartPolicy::Permanent,
5375        }];
5376        let view = c.supervisor_view().expect("Supervisor kind has a view");
5377        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
5378        assert_eq!(view.max_restarts, 5);
5379        assert_eq!(
5380            view.restart_window,
5381            Some(std::time::Duration::from_secs(60))
5382        );
5383        assert_eq!(view.children.len(), 1);
5384        view.validate().unwrap();
5385    }
5386
5387    #[test]
5388    fn supervisor_view_none_for_non_supervisor_kinds() {
5389        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5390        assert!(c.supervisor_view().is_none());
5391    }
5392
5393    #[test]
5394    fn declared_mesh_slots_empty_for_bare_caixa() {
5395        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5396        assert!(c.declared_mesh_slots().is_empty());
5397    }
5398
5399    #[test]
5400    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
5401        use crate::{Entrada, Membro};
5402        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5403        // Set a non-adjacent pair (:membros + :entrada) to pin that the
5404        // canonical declaration order is preserved regardless of which
5405        // subset is populated.
5406        c.membros = vec![Membro {
5407            caixa: "a".into(),
5408            versao: "^0.1".into(),
5409        }];
5410        c.entrada = Some(Entrada {
5411            host: "x.example.com".into(),
5412            para: "a".into(),
5413            paths: vec![],
5414            port: 8080,
5415        });
5416        assert_eq!(
5417            c.declared_mesh_slots(),
5418            vec![
5419                crate::render::M3_AUTHOR_KEY_MEMBROS,
5420                crate::render::M3_AUTHOR_KEY_ENTRADA,
5421            ]
5422        );
5423    }
5424
5425    #[test]
5426    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
5427        // Scalar-value pin: the five author-facing kebab-case labels the
5428        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
5429        // mesh slot axis, one arm per typed slot. Mirrors the peer
5430        // scalar-value pin the sibling
5431        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
5432        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
5433        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
5434        // carry (f49c8b0), so both altitudes of the typed-slot algebra
5435        // (per-Servico M2 + per-Aplicacao M3) share the same
5436        // "one canonical byte-string per arm" discipline. A future
5437        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
5438        // `:politicas` → `:policies`, `:placement` → `:distribution`,
5439        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
5440        // and every consumer that reaches for the label picks it up at
5441        // build time rather than at runtime as a downstream mismatch.
5442        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
5443        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
5444        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
5445        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
5446        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
5447    }
5448
5449    #[test]
5450    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
5451        // Production-through-const pin: the five per-arm labels the
5452        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
5453        // `Vec` route through the lifted
5454        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
5455        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
5456        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
5457        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
5458        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
5459        // declaration order. A future re-order or drift at the tagger
5460        // (a rename that reaches the tagger but not the const, or vice
5461        // versa) surfaces here at build time rather than at runtime as
5462        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
5463        // `slots: <stale-kebab-case>` diagnostic far from the rename's
5464        // commit. Mirror of the peer
5465        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
5466        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
5467        // axis.
5468        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
5469        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5470        c.membros = vec![Membro {
5471            caixa: "a".into(),
5472            versao: "^0.1".into(),
5473        }];
5474        c.contratos = vec![WitContract {
5475            de: "a".into(),
5476            para: "a".into(),
5477            wit: "wasi:http/proxy".into(),
5478            endpoint: Some("/x".into()),
5479            subject: None,
5480            slot: None,
5481        }];
5482        c.politicas = Some(MeshPolicy::default());
5483        c.placement = Some(Placement {
5484            estrategia: PlacementStrategy::Replicated,
5485            clusters: vec!["rio".into()],
5486            affinity: None,
5487            shard_key: None,
5488        });
5489        c.entrada = Some(Entrada {
5490            host: "x.example.com".into(),
5491            para: "a".into(),
5492            paths: vec![],
5493            port: 8080,
5494        });
5495        assert_eq!(
5496            c.declared_mesh_slots(),
5497            vec![
5498                crate::render::M3_AUTHOR_KEY_MEMBROS,
5499                crate::render::M3_AUTHOR_KEY_CONTRATOS,
5500                crate::render::M3_AUTHOR_KEY_POLITICAS,
5501                crate::render::M3_AUTHOR_KEY_PLACEMENT,
5502                crate::render::M3_AUTHOR_KEY_ENTRADA,
5503            ]
5504        );
5505    }
5506
5507    #[test]
5508    fn declared_supervisor_slots_empty_for_bare_caixa() {
5509        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5510        assert!(c.declared_supervisor_slots().is_empty());
5511    }
5512
5513    #[test]
5514    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
5515        use crate::RestartStrategy;
5516        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5517        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
5518        // that the canonical declaration order is preserved regardless
5519        // of which subset is populated.
5520        c.estrategia = Some(RestartStrategy::OneForOne);
5521        c.restart_window = Some("60s".into());
5522        assert_eq!(
5523            c.declared_supervisor_slots(),
5524            vec![
5525                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
5526                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
5527            ]
5528        );
5529    }
5530
5531    #[test]
5532    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
5533        // Scalar-value pin: the four author-facing kebab-case labels the
5534        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
5535        // supervision-tree slot axis, one arm per typed slot. Mirrors the
5536        // peer scalar-value pins the sibling
5537        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
5538        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
5539        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
5540        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
5541        // top-level M3 slot consts carry, so all three kind-scoped
5542        // typed-slot-family author-facing-label axes route through one
5543        // canonical per-arm declaration. A future rebrand
5544        // (`:estrategia` → `:strategy` for English uniformity,
5545        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
5546        // `MaxIntensity` name, `:restart-window` → `:period` matching
5547        // OTP's `Period` name, `:children` → `:workers` matching Elixir
5548        // idiom) lands as an edit to exactly one const, and every
5549        // consumer that reaches for the label picks it up at build time
5550        // rather than at runtime as a downstream mismatch.
5551        assert_eq!(
5552            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
5553            ":estrategia"
5554        );
5555        assert_eq!(
5556            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
5557            ":max-restarts"
5558        );
5559        assert_eq!(
5560            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
5561            ":restart-window"
5562        );
5563        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
5564    }
5565
5566    #[test]
5567    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
5568        // Production-through-const pin: the four per-arm labels the
5569        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
5570        // return `Vec` route through the lifted
5571        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
5572        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
5573        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
5574        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
5575        // canonical declaration order. A future re-order or drift at the
5576        // tagger (a rename that reaches the tagger but not the const, or
5577        // vice versa) surfaces here at build time rather than at runtime
5578        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
5579        // `slots: <stale-kebab-case>` diagnostic far from the rename's
5580        // commit. Mirror of the peer
5581        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
5582        // (f49c8b0) and
5583        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
5584        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
5585        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
5586        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5587        c.estrategia = Some(RestartStrategy::OneForOne);
5588        c.max_restarts = Some(5);
5589        c.restart_window = Some("60s".into());
5590        c.children = vec![ChildSpec {
5591            caixa: "worker".into(),
5592            versao: "^0.1".into(),
5593            restart: RestartPolicy::Permanent,
5594        }];
5595        assert_eq!(
5596            c.declared_supervisor_slots(),
5597            vec![
5598                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
5599                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
5600                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
5601                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
5602            ]
5603        );
5604    }
5605
5606    #[test]
5607    fn declared_servico_slots_empty_for_bare_caixa() {
5608        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5609        assert!(c.declared_servico_slots().is_empty());
5610    }
5611
5612    #[test]
5613    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
5614        use crate::{UpgradeFromEntry, UpgradeInstruction};
5615        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5616        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
5617        // the canonical declaration order is preserved regardless of
5618        // which subset is populated.
5619        c.limits = Some(crate::LimitsSpec {
5620            fuel: Some(1_000_000),
5621            ..Default::default()
5622        });
5623        c.upgrade_from = vec![UpgradeFromEntry {
5624            from: "0.1.0".into(),
5625            instructions: vec![UpgradeInstruction::Restart],
5626        }];
5627        assert_eq!(
5628            c.declared_servico_slots(),
5629            vec![
5630                crate::render::M2_AUTHOR_KEY_LIMITS,
5631                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
5632            ]
5633        );
5634    }
5635
5636    #[test]
5637    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
5638        // Scalar-value pin: the three author-facing kebab-case labels
5639        // the `(defcaixa … :<slot> (…))` surface admits on the M2
5640        // top-level slot axis, one arm per typed slot. Mirrors the peer
5641        // scalar-value pin the sibling renderer-side
5642        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
5643        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
5644        // consts carry, so both halves of the M2 top-level slot dual
5645        // axis (author-facing kebab-case label + renderer-side
5646        // camelCase overlay-container wire key) route through one
5647        // canonical per-arm declaration. A future rebrand
5648        // (`:limits` → `:sandbox` matching Lunatic per-process
5649        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
5650        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
5651        // matching Erlang's verbatim appup name) lands as an edit to
5652        // exactly one const, and every consumer that reaches for the
5653        // label picks it up at build time rather than at runtime as a
5654        // downstream mismatch.
5655        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
5656        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
5657        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
5658    }
5659
5660    #[test]
5661    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
5662        // Production-through-const pin: the three per-arm labels the
5663        // [`Caixa::declared_servico_slots`] tagger pushes onto its
5664        // return `Vec` route through the lifted
5665        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
5666        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
5667        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
5668        // declaration order. A future re-order or drift at the tagger
5669        // (a rename that reaches the tagger but not the const, or vice
5670        // versa) surfaces here at build time rather than at runtime as
5671        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
5672        // `slots: <stale-kebab-case>` diagnostic far from the rename's
5673        // commit. Mirror of the peer
5674        // [`crate::behavior::BehaviorSpec::declared_slots`] production
5675        // tagger pin (889dc18) on the sibling per-callback axis.
5676        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
5677        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5678        c.limits = Some(crate::LimitsSpec {
5679            fuel: Some(1_000_000),
5680            ..Default::default()
5681        });
5682        c.behavior = Some(BehaviorSpec {
5683            on_init: Some(PathBuf::from("lib/init.lisp")),
5684            ..Default::default()
5685        });
5686        c.upgrade_from = vec![UpgradeFromEntry {
5687            from: "0.1.0".into(),
5688            instructions: vec![UpgradeInstruction::Restart],
5689        }];
5690        assert_eq!(
5691            c.declared_servico_slots(),
5692            vec![
5693                crate::render::M2_AUTHOR_KEY_LIMITS,
5694                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
5695                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
5696            ]
5697        );
5698    }
5699
5700    #[test]
5701    fn existing_manifests_unaffected_by_new_optional_slots() {
5702        // Regression test: a caixa.lisp authored before M2 typed slots
5703        // should still parse + serialize cleanly. The bare `defcaixa`
5704        // emitted by `Caixa::template` has none of the new fields.
5705        let src = Caixa::template("legacy");
5706        let c = Caixa::from_lisp(&src).unwrap();
5707        assert!(c.limits.is_none());
5708        assert!(c.behavior.is_none());
5709        assert!(c.upgrade_from.is_empty());
5710        assert!(c.estrategia.is_none());
5711        assert!(c.children.is_empty());
5712
5713        // And to_lisp emits a manifest with the new slots in the
5714        // empty/default state — round-trippable.
5715        let emitted = c.to_lisp();
5716        let back = Caixa::from_lisp(&emitted).unwrap();
5717        assert_eq!(c, back);
5718    }
5719
5720    #[test]
5721    fn validate_deps_accepts_canonical_caixa() {
5722        // Positive control: the bare template — zero deps, zero
5723        // deps_dev — passes the gate trivially. A future axis added to
5724        // `Dep::validate` mustn't regress an empty-deps caixa to a
5725        // build error.
5726        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5727        c.validate_deps().unwrap();
5728    }
5729
5730    #[test]
5731    fn validate_deps_rejects_invalid_versao_in_deps() {
5732        // Fail-before-pass-after pin: a malformed `:deps :versao`
5733        // surfaces at validate_deps() time, not at lacre-resolve time.
5734        // Mirrors `rejects_invalid_membro_versao_requirement` and
5735        // `validate_rejects_invalid_child_versao_requirement` on the
5736        // other two `:versao` axes.
5737        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5738        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
5739        let err = c.validate_deps().unwrap_err();
5740        assert!(
5741            matches!(
5742                err,
5743                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
5744                    if nome == "caixa-teia" && versao == "^bad-version"
5745            ),
5746            "got {err:?}"
5747        );
5748    }
5749
5750    #[test]
5751    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
5752        // Parity pin: `:deps-dev` must run through the same per-entry
5753        // validator as `:deps` — a typo in either axis surfaces the
5754        // same diagnostic. Without this leg, `:deps-dev` would be a
5755        // second-class citizen of the typed surface and an author
5756        // could land a build that passes validate_deps but fails at
5757        // `feira lock`-time when the dev-dep is resolved for a test
5758        // build.
5759        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5760        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
5761        let err = c.validate_deps().unwrap_err();
5762        assert!(
5763            matches!(
5764                err,
5765                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
5766                    if nome == "tatara-check" && versao == "^^0.1"
5767            ),
5768            "got {err:?}"
5769        );
5770    }
5771
5772    #[test]
5773    fn validate_deps_runs_deps_before_deps_dev() {
5774        // Order pin: when both lists carry typos, the `:deps`
5775        // diagnostic surfaces first. The author's mental model is
5776        // "runtime deps are load-bearing; dev deps are scaffolding";
5777        // surfacing the runtime axis first matches that hierarchy.
5778        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5779        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
5780        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
5781        let err = c.validate_deps().unwrap_err();
5782        assert!(
5783            matches!(
5784                err,
5785                crate::dep::DepError::VersaoInvalid { ref nome, .. }
5786                    if nome == "runtime-dep"
5787            ),
5788            "expected `:deps` typo to surface first, got {err:?}"
5789        );
5790    }
5791
5792    #[test]
5793    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
5794        // Positive control sweep across both lists. Pin every
5795        // canonical Cargo-shaped form so a future tightening of the
5796        // accepted set surfaces here as a test failure (parity with
5797        // `accepts_canonical_membro_versao_forms` and
5798        // `validate_accepts_canonical_child_versao_forms`).
5799        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5800        c.deps = vec![
5801            Dep::simple("caret", "^0.1"),
5802            Dep::simple("tilde", "~0.1.2"),
5803            Dep::simple("exact", "0.1.0"),
5804            Dep::simple("wildcard", "*"),
5805            Dep::simple("multi-range", ">=0.1, <2"),
5806        ];
5807        c.deps_dev = vec![
5808            Dep::simple("dev-caret", "^0.1"),
5809            Dep::simple("dev-wildcard", "*"),
5810        ];
5811        c.validate_deps().unwrap();
5812    }
5813
5814    #[test]
5815    fn validate_deps_diagnostic_carries_offending_dep() {
5816        // Diagnostic-shape pin: the error names the offending entry's
5817        // `:nome` + `:versao` verbatim and carries a non-empty
5818        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
5819        // run can render the diagnostic without re-parsing.
5820        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5821        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
5822        let err = c.validate_deps().unwrap_err();
5823        let crate::dep::DepError::VersaoInvalid {
5824            nome,
5825            versao,
5826            reason,
5827        } = err
5828        else {
5829            panic!("expected VersaoInvalid, got other variant");
5830        };
5831        assert_eq!(nome, "caixa-teia");
5832        assert_eq!(versao, "not-a-req");
5833        assert!(
5834            !reason.is_empty(),
5835            "VersaoInvalid `reason` must carry the parser's wording verbatim"
5836        );
5837    }
5838
5839    #[test]
5840    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
5841        // Cross-axis pin: `validate_deps` walks both :deps and
5842        // :deps-dev through `Dep::validate`, and the new fonte gate
5843        // (`:tag` + `:branch` both set — the canonical "pin drift"
5844        // footgun) must surface from the :deps-dev arm with the
5845        // offending entry's :nome named. Pin the :deps-dev arm
5846        // explicitly so a future shortcut that only walks :deps
5847        // surfaces here as a regression.
5848        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5849        c.deps_dev = vec![Dep {
5850            nome: "dev-only".into(),
5851            versao: "^0.1".into(),
5852            fonte: Some(crate::DepSource::Git {
5853                repo: "github:p/x".into(),
5854                tag: Some("v1".into()),
5855                rev: None,
5856                branch: Some("main".into()),
5857            }),
5858            opcional: false,
5859            caracteristicas: vec![],
5860        }];
5861        let err = c.validate_deps().unwrap_err();
5862        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
5863            panic!("expected FontePinAmbiguous from :deps-dev walk");
5864        };
5865        assert_eq!(nome, "dev-only");
5866        assert!(pins.contains(":tag") && pins.contains(":branch"));
5867    }
5868
5869    #[test]
5870    fn validate_deps_rejects_empty_repo_in_deps() {
5871        // Parity pin on the :deps arm: an empty :repo on the runtime
5872        // deps list surfaces the same FonteRepoEmpty diagnostic the
5873        // dep.rs per-entry tests pin, naming the offending entry.
5874        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5875        c.deps = vec![Dep {
5876            nome: "runtime".into(),
5877            versao: "^0.1".into(),
5878            fonte: Some(crate::DepSource::Git {
5879                repo: String::new(),
5880                tag: Some("v1".into()),
5881                rev: None,
5882                branch: None,
5883            }),
5884            opcional: false,
5885            caracteristicas: vec![],
5886        }];
5887        let err = c.validate_deps().unwrap_err();
5888        assert!(
5889            matches!(
5890                err,
5891                crate::dep::DepError::FonteRepoEmpty { ref nome }
5892                    if nome == "runtime"
5893            ),
5894            "got {err:?}"
5895        );
5896    }
5897
5898    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
5899
5900    #[test]
5901    fn validate_deps_rejects_duplicate_nome_in_deps() {
5902        // Fail-before-pass-after pin: two `:deps` entries naming the same
5903        // caixa carry two `:versao` / `:fonte` / feature triples that the
5904        // caixa-resolver's lacre pipeline collapses (the second silently
5905        // overwrites the first at `concrete_versao`-resolve time). The
5906        // gate surfaces the duplicate at validate-time, naming the
5907        // offending caixa + the list, before the resolver-side silent
5908        // drop. Mirrors the peer typed-graph duplicate gates
5909        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
5910        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5911        c.deps = vec![
5912            Dep::simple("caixa-teia", "^0.1"),
5913            Dep::simple("caixa-teia", "^0.2"),
5914        ];
5915        let err = c.validate_deps().unwrap_err();
5916        assert!(
5917            matches!(
5918                err,
5919                crate::dep::DepError::DuplicateNome { ref nome, list }
5920                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
5921            ),
5922            "got {err:?}"
5923        );
5924    }
5925
5926    #[test]
5927    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
5928        // Parity pin: `:deps-dev` runs through the same per-list
5929        // duplicate check as `:deps` — neither axis is a second-class
5930        // citizen of the set-not-multiset discipline.
5931        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5932        c.deps_dev = vec![
5933            Dep::simple("tatara-check", "*"),
5934            Dep::simple("tatara-check", "^0.1"),
5935        ];
5936        let err = c.validate_deps().unwrap_err();
5937        assert!(
5938            matches!(
5939                err,
5940                crate::dep::DepError::DuplicateNome { ref nome, list }
5941                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
5942            ),
5943            "got {err:?}"
5944        );
5945    }
5946
5947    #[test]
5948    fn validate_deps_accepts_cross_list_same_nome() {
5949        // The Cargo `[dependencies]` + `[dev-dependencies]` override
5950        // convention is preserved: a name appearing in *both* lists is
5951        // valid (the dev-pin overrides at test/dev time). Only
5952        // within-list duplicates are structurally incoherent — pin the
5953        // permissive cross-list semantics so a future shortcut that
5954        // collapses the two seen-sets into one surfaces here as a test
5955        // failure.
5956        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5957        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
5958        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
5959        c.validate_deps().unwrap();
5960    }
5961
5962    #[test]
5963    fn validate_deps_accepts_distinct_nome_in_both_lists() {
5964        // Positive control: distinct names within each list pass — the
5965        // gate's identity element on the canonical authoring shape.
5966        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5967        c.deps = vec![
5968            Dep::simple("caixa-teia", "^0.1"),
5969            Dep::simple("pleme-mesh", "*"),
5970        ];
5971        c.deps_dev = vec![
5972            Dep::simple("tatara-check", "*"),
5973            Dep::simple("dev-shim", "^0.1"),
5974        ];
5975        c.validate_deps().unwrap();
5976    }
5977
5978    #[test]
5979    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
5980        // Diagnostic-precedence pin: a malformed `:versao` on the
5981        // duplicating entry surfaces its narrower `VersaoInvalid`
5982        // diagnostic first, before the cross-entry duplicate gate fires
5983        // — the canonical "per-entry shape before cross-entry uniqueness"
5984        // precedence every peer set-not-multiset gate establishes
5985        // (`*_invalid_fires_before_duplicate_check` pins on
5986        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
5987        // `validate_upgrade_from`).
5988        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5989        c.deps = vec![
5990            Dep::simple("caixa-teia", "^0.1"),
5991            Dep::simple("caixa-teia", "^bad-version"),
5992        ];
5993        let err = c.validate_deps().unwrap_err();
5994        assert!(
5995            matches!(
5996                err,
5997                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
5998                    if nome == "caixa-teia" && versao == "^bad-version"
5999            ),
6000            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
6001        );
6002    }
6003
6004    #[test]
6005    fn validate_deps_duplicate_diagnostic_names_first_collision() {
6006        // First-collision determinism pin: with three entries naming the
6007        // same caixa, the first colliding pair surfaces — not the last.
6008        // Mirrors the peer first-collision posture on every
6009        // duplicate-target gate
6010        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
6011        // — the second entry is the first collision; this gate uses the
6012        // same shape: the second entry's `:nome` lands in the diagnostic
6013        // because `seen.insert(first.nome)` already populated the set).
6014        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6015        c.deps = vec![
6016            Dep::simple("caixa-teia", "^0.1"),
6017            Dep::simple("caixa-teia", "^0.2"),
6018            Dep::simple("caixa-teia", "^0.3"),
6019        ];
6020        let err = c.validate_deps().unwrap_err();
6021        // The diagnostic carries the offending caixa name; the
6022        // implementation surfaces on the *second* entry (the first
6023        // collision), so the test pins the `:nome` value.
6024        assert!(
6025            matches!(
6026                err,
6027                crate::dep::DepError::DuplicateNome { ref nome, list }
6028                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6029            ),
6030            "got {err:?}"
6031        );
6032    }
6033
6034    #[test]
6035    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
6036        // Cross-list precedence pin: when both lists carry duplicates,
6037        // the `:deps` diagnostic surfaces first — same author-mental-
6038        // model ordering the `validate_deps_runs_deps_before_deps_dev`
6039        // pin establishes for malformed `:versao` (runtime axis before
6040        // dev axis).
6041        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6042        c.deps = vec![
6043            Dep::simple("runtime-dep", "^0.1"),
6044            Dep::simple("runtime-dep", "^0.2"),
6045        ];
6046        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
6047        let err = c.validate_deps().unwrap_err();
6048        assert!(
6049            matches!(
6050                err,
6051                crate::dep::DepError::DuplicateNome { ref nome, list }
6052                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6053            ),
6054            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
6055        );
6056    }
6057
6058    #[test]
6059    fn validate_deps_empty_lists_pass_duplicate_gate() {
6060        // Empty-set identity pin: the bare template (zero deps, zero
6061        // deps_dev) passes the duplicate gate as the gate's identity
6062        // element. A future tighten that conflates "empty" with
6063        // "missing" would regress this baseline.
6064        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6065        c.validate_deps().unwrap();
6066    }
6067
6068    #[test]
6069    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
6070        // Diagnostic-shape pin: the `list:` field tags which list the
6071        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
6072        // `feira lint` run can route the author to the right block in
6073        // their caixa.lisp without re-deriving the list from context.
6074        // Same self-locating shape every peer per-axis diagnostic
6075        // already exposes.
6076        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6077        c.deps_dev = vec![
6078            Dep::simple("dev-thing", "*"),
6079            Dep::simple("dev-thing", "^0.1"),
6080        ];
6081        let err = c.validate_deps().unwrap_err();
6082        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
6083            panic!("expected DuplicateNome from :deps-dev walk");
6084        };
6085        assert_eq!(nome, "dev-thing");
6086        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
6087    }
6088
6089    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
6090
6091    #[test]
6092    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
6093        // Thread-through pin on `:deps`: the per-entry
6094        // `Dep::validate_caracteristicas` gate fires inside
6095        // `Caixa::validate_deps`'s linear walk, so a malformed feature
6096        // list on any `:deps` entry surfaces as a `DepError` from
6097        // `validate_deps` — the same reachability shape every per-entry
6098        // `Dep::validate` arm threads through. Without this pin a future
6099        // shortcut that skips the per-entry `Dep::validate` call on the
6100        // cross-entry-uniqueness path would mask the within-entry
6101        // `:caracteristicas` gates.
6102        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6103        c.deps = vec![Dep {
6104            nome: "caixa-teia".into(),
6105            versao: "^0.1".into(),
6106            fonte: None,
6107            opcional: false,
6108            caracteristicas: vec!["http".into(), "http".into()],
6109        }];
6110        let err = c.validate_deps().unwrap_err();
6111        let crate::dep::DepError::CaracteristicaDuplicate {
6112            nome,
6113            caracteristica,
6114        } = err
6115        else {
6116            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
6117        };
6118        assert_eq!(nome, "caixa-teia");
6119        assert_eq!(caracteristica, "http");
6120    }
6121
6122    #[test]
6123    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
6124        // Peer thread-through pin on `:deps-dev`: same reachability as
6125        // the `:deps` arm above, on the dev-only authoring axis. Pins
6126        // that the `validate_deps` walk visits both lists' per-entry
6127        // gates uniformly. The empty-feature arm carries here so both
6128        // new `:caracteristicas` arms are surfaced via at least one
6129        // `validate_deps` thread-through.
6130        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6131        c.deps_dev = vec![Dep {
6132            nome: "caixa-teia".into(),
6133            versao: "^0.1".into(),
6134            fonte: None,
6135            opcional: false,
6136            caracteristicas: vec![String::new()],
6137        }];
6138        let err = c.validate_deps().unwrap_err();
6139        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
6140            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
6141        };
6142        assert_eq!(nome, "caixa-teia");
6143    }
6144
6145    #[test]
6146    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
6147        // Thread-through pin on `:deps`: the per-entry
6148        // `Dep::validate_caracteristicas` value-shape gate (lifted via
6149        // `crate::render::is_cargo_feature_name`) fires inside
6150        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
6151        // a structurally invalid feature name on any `:deps` entry
6152        // surfaces as `DepError::CaracteristicaInvalid` from
6153        // `validate_deps` — the same reachability shape every per-entry
6154        // `Dep::validate` arm threads through. Without this pin a
6155        // future shortcut that skips the per-entry `Dep::validate` call
6156        // on the cross-entry-uniqueness path would mask the within-
6157        // entry `:caracteristicas` value-shape gate.
6158        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6159        c.deps = vec![Dep {
6160            nome: "caixa-teia".into(),
6161            versao: "^0.1".into(),
6162            fonte: None,
6163            opcional: false,
6164            caracteristicas: vec!["+http".into()],
6165        }];
6166        let err = c.validate_deps().unwrap_err();
6167        let crate::dep::DepError::CaracteristicaInvalid {
6168            nome,
6169            caracteristica,
6170            ..
6171        } = err
6172        else {
6173            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
6174        };
6175        assert_eq!(nome, "caixa-teia");
6176        assert_eq!(caracteristica, "+http");
6177    }
6178
6179    #[test]
6180    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
6181        // Peer thread-through pin on `:deps-dev`: same reachability as
6182        // the `:deps` arm above, on the dev-only authoring axis. The
6183        // `http/json` shape carries here so the segment-separator
6184        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
6185        // confusion footgun) is surfaced via the cross-entry walk too —
6186        // pinning that the `:deps-dev` list visits the same per-entry
6187        // value-shape gate as the `:deps` list.
6188        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6189        c.deps_dev = vec![Dep {
6190            nome: "caixa-teia".into(),
6191            versao: "^0.1".into(),
6192            fonte: None,
6193            opcional: false,
6194            caracteristicas: vec!["http/json".into()],
6195        }];
6196        let err = c.validate_deps().unwrap_err();
6197        let crate::dep::DepError::CaracteristicaInvalid {
6198            nome,
6199            caracteristica,
6200            ..
6201        } = err
6202        else {
6203            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
6204        };
6205        assert_eq!(nome, "caixa-teia");
6206        assert_eq!(caracteristica, "http/json");
6207    }
6208
6209    #[test]
6210    fn to_lisp_preserves_deps() {
6211        let src = r#"
6212(defcaixa
6213  :nome "x"
6214  :versao "0.1.0"
6215  :kind Biblioteca
6216  :deps ((:nome "a" :versao "^0.1")
6217         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
6218"#;
6219        let c1 = Caixa::from_lisp(src).unwrap();
6220        let emitted = c1.to_lisp();
6221        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
6222        assert_eq!(c1.deps, c2.deps);
6223    }
6224
6225    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
6226
6227    fn caixa_with_nome(nome: &str) -> Caixa {
6228        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
6229        c.nome = nome.to_string();
6230        c
6231    }
6232
6233    #[test]
6234    fn validate_nome_accepts_canonical_template() {
6235        // Positive control: the bare `feira init`-style template's
6236        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
6237        // not regress this baseline shape. A future tightening of the
6238        // accepted set surfaces here as a test failure first.
6239        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6240        c.validate_nome().unwrap();
6241    }
6242
6243    #[test]
6244    fn validate_nome_accepts_canonical_forms() {
6245        // Positive-set sweep: each realistic caixa-name shape the K8s
6246        // apiserver accepts as a `metadata.name` label must pass —
6247        // single-word, hyphen-joined, version-suffixed, single-char,
6248        // two-char, digit-start (DNS-1123 allows this; the stricter
6249        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
6250        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
6251        // the peer member-name axis.
6252        for nome in [
6253            "checkout",
6254            "cart-v2",
6255            "a",
6256            "db",
6257            "3rd-party-shim",
6258            "payment-retry",
6259            "0",
6260        ] {
6261            caixa_with_nome(nome)
6262                .validate_nome()
6263                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
6264        }
6265    }
6266
6267    #[test]
6268    fn validate_nome_rejects_empty() {
6269        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
6270        // an empty `:nome` (the derive macro stores the raw String);
6271        // the gate's empty arm names the offending axis with a narrower
6272        // diagnostic than the `NomeInvalid` parse arm would emit.
6273        let c = caixa_with_nome("");
6274        let err = c.validate_nome().unwrap_err();
6275        assert_eq!(err, ManifestError::NomeEmpty);
6276    }
6277
6278    #[test]
6279    fn validate_nome_rejects_uppercase() {
6280        // The canonical "I copied the TitleCase display name verbatim"
6281        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
6282        // admission on every derived artifact (Helm chart, ComputeUnit,
6283        // CNP, HTTPRoute, label values); the gate moves the diagnostic
6284        // to the source `caixa.lisp` and the reason suggests the
6285        // lowercased fix verbatim.
6286        let c = caixa_with_nome("MyApp");
6287        let err = c.validate_nome().unwrap_err();
6288        let ManifestError::NomeInvalid { nome, reason } = err else {
6289            panic!("expected NomeInvalid for uppercase :nome");
6290        };
6291        assert_eq!(nome, "MyApp");
6292        assert!(
6293            reason.contains("uppercase") && reason.contains("myapp"),
6294            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
6295        );
6296    }
6297
6298    #[test]
6299    fn validate_nome_rejects_underscore() {
6300        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
6301        // `_`; the apiserver rejects on admission across every derived
6302        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
6303        // and `:children :caixa` (31bfa43).
6304        let c = caixa_with_nome("my_app");
6305        let err = c.validate_nome().unwrap_err();
6306        assert!(
6307            matches!(
6308                err,
6309                ManifestError::NomeInvalid { ref nome, ref reason }
6310                    if nome == "my_app" && reason.contains('_')
6311            ),
6312            "got {err:?}"
6313        );
6314    }
6315
6316    #[test]
6317    fn validate_nome_rejects_dot() {
6318        // A `:nome` is a single DNS-1123 label, not a subdomain. The
6319        // "I want to namespace with `.`" footgun the gate redirects to
6320        // `-` via the shared predicate's reason wording.
6321        let c = caixa_with_nome("team.app");
6322        let err = c.validate_nome().unwrap_err();
6323        assert!(
6324            matches!(
6325                err,
6326                ManifestError::NomeInvalid { ref nome, ref reason }
6327                    if nome == "team.app" && reason.contains('.')
6328            ),
6329            "got {err:?}"
6330        );
6331    }
6332
6333    #[test]
6334    fn validate_nome_rejects_leading_hyphen() {
6335        // DNS-1123 boundary rule: the label must start with an ASCII
6336        // alphanumeric. Pin the leading-`-` arm explicitly.
6337        let c = caixa_with_nome("-app");
6338        let err = c.validate_nome().unwrap_err();
6339        assert!(
6340            matches!(
6341                err,
6342                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
6343            ),
6344            "got {err:?}"
6345        );
6346    }
6347
6348    #[test]
6349    fn validate_nome_rejects_trailing_hyphen() {
6350        // Symmetric arm of the boundary rule, pinned separately so a
6351        // future relaxation that only checks the leading position
6352        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
6353        // and `_with_trailing_hyphen` on the supervisor / aplicacao
6354        // axes.
6355        let c = caixa_with_nome("app-");
6356        let err = c.validate_nome().unwrap_err();
6357        assert!(
6358            matches!(
6359                err,
6360                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
6361            ),
6362            "got {err:?}"
6363        );
6364    }
6365
6366    #[test]
6367    fn validate_nome_rejects_unicode() {
6368        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
6369        // bytes are rejected by the K8s apiserver on every name axis.
6370        let c = caixa_with_nome("café");
6371        let err = c.validate_nome().unwrap_err();
6372        assert!(
6373            matches!(
6374                err,
6375                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
6376            ),
6377            "got {err:?}"
6378        );
6379    }
6380
6381    #[test]
6382    fn validate_nome_rejects_whitespace() {
6383        // The paste-from-sketch / paste-from-spec footgun. Internal
6384        // whitespace is rejected by every K8s name axis.
6385        let c = caixa_with_nome("my app");
6386        let err = c.validate_nome().unwrap_err();
6387        assert!(
6388            matches!(
6389                err,
6390                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
6391            ),
6392            "got {err:?}"
6393        );
6394    }
6395
6396    #[test]
6397    fn validate_nome_rejects_too_long() {
6398        // 64-byte boundary pin: the K8s apiserver rejects any
6399        // `metadata.name` over 63 bytes at admission; the diagnostic
6400        // names both the 63-byte cap and the actual length so the
6401        // author can shorten in one edit. Mirrors `_too_long` on the
6402        // peer member-/cluster-/child-name axes.
6403        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
6404        let c = caixa_with_nome(&over);
6405        let err = c.validate_nome().unwrap_err();
6406        let ManifestError::NomeInvalid { nome, reason } = err else {
6407            panic!("expected NomeInvalid for over-cap :nome");
6408        };
6409        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
6410        assert!(
6411            reason.contains("63") && reason.contains("64"),
6412            "diagnostic must name the cap + actual length, got {reason:?}"
6413        );
6414    }
6415
6416    #[test]
6417    fn nome_max_length_validates() {
6418        // The 63-byte cap exactly — the boundary-accepting case pinned
6419        // alongside `validate_nome_rejects_too_long` so a future cap
6420        // shift surfaces both arms simultaneously. Mirrors
6421        // `membro_caixa_max_length_validates`,
6422        // `placement_cluster_max_length_validates`,
6423        // `child_caixa_max_length_validates`.
6424        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
6425        caixa_with_nome(&at_cap).validate_nome().unwrap();
6426    }
6427
6428    #[test]
6429    fn nome_empty_takes_precedence_over_invalid() {
6430        // Order pin: the empty arm fires before the predicate is
6431        // consulted. Empty < invalid in self-locating-ness — the
6432        // narrower `NomeEmpty` diagnostic doesn't carry a useless
6433        // `nome: ""` reference into the parser-shaped reason. Mirrors
6434        // `membro_caixa_empty_takes_precedence_over_invalid` on the
6435        // peer axis (3f9d7a0).
6436        let c = caixa_with_nome("");
6437        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
6438    }
6439
6440    #[test]
6441    fn nome_invalid_diagnostic_carries_offending_nome() {
6442        // Diagnostic-shape pin: the error names the offending `:nome`
6443        // verbatim with a non-empty parser-shaped reason, so a `feira
6444        // lint` run can render the diagnostic without re-parsing.
6445        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
6446        let c = caixa_with_nome("MyApp");
6447        let err = c.validate_nome().unwrap_err();
6448        let ManifestError::NomeInvalid { nome, reason } = err else {
6449            panic!("expected NomeInvalid variant");
6450        };
6451        assert_eq!(nome, "MyApp");
6452        assert!(
6453            !reason.is_empty(),
6454            "NomeInvalid `reason` must carry the predicate's wording verbatim"
6455        );
6456    }
6457
6458    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
6459    //
6460    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
6461    // via DNS-1123; this second-axis gate caps the joint
6462    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
6463    // canonical [`crate::lareira_chart_name`] helper's doc comment
6464    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
6465    // "the M4 admission webhook will pin the joint-length invariant
6466    // when it lands". These tests pin it at the manifest-validate
6467    // layer instead, fail-before-pass-after on the 56-byte boundary.
6468
6469    #[test]
6470    fn validate_nome_chart_name_budget_accepts_canonical_template() {
6471        // Positive control: the bare `feira init`-style template's
6472        // `:nome` ("demo") sits far below the cap; the gate must not
6473        // regress this baseline. Same shape every peer
6474        // value-shape-gate baseline pin uses.
6475        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6476        c.validate_nome_chart_name_budget().unwrap();
6477    }
6478
6479    #[test]
6480    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
6481        // Positive-set sweep across the canonical author surface every
6482        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
6483        // `worker`, the `checkout-aplicacao` example members, the
6484        // `akeyless-attest` caixa-tatara fixture). Every value sits
6485        // far below the 55-byte per-`:nome` budget. Same shape every
6486        // peer per-axis baseline pin uses.
6487        for nome in [
6488            "hello-rio",
6489            "cart",
6490            "checkout",
6491            "worker",
6492            "akeyless-attest",
6493            "demo",
6494            "a",
6495        ] {
6496            caixa_with_nome(nome)
6497                .validate_nome_chart_name_budget()
6498                .unwrap_or_else(|e| {
6499                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
6500                });
6501        }
6502    }
6503
6504    #[test]
6505    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
6506        // Boundary-accepting case at the 55-byte per-`:nome` budget —
6507        // the joint chart name is exactly 63 bytes, the DNS-1123 label
6508        // cap. Pinned alongside the rejecting-arm test so a future cap
6509        // shift surfaces both arms simultaneously. Mirrors
6510        // `nome_max_length_validates` on the peer bare-`:nome` axis.
6511        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
6512        caixa_with_nome(&at_cap)
6513            .validate_nome_chart_name_budget()
6514            .unwrap();
6515    }
6516
6517    #[test]
6518    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
6519        // Fail-before-pass-after pin on the 56-byte boundary: the
6520        // smallest `:nome` length that overflows the joint chart-name
6521        // cap. The inner [`is_dns_1123_label`] gate
6522        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
6523        // this gate it silently passed the manifest-validate cascade
6524        // and surfaced as a `helm lint` / apiserver rejection on the
6525        // rendered chart name far from the source `caixa.lisp`, with
6526        // no field naming the overflow. With this gate the diagnostic
6527        // names the offending `:nome` verbatim alongside the rendered
6528        // chart name and the budget, so the author can shorten in one
6529        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
6530        // bare-`:nome` axis.
6531        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
6532        let c = caixa_with_nome(&over);
6533        let err = c.validate_nome_chart_name_budget().unwrap_err();
6534        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
6535            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
6536        };
6537        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
6538        assert_eq!(nome, over);
6539        assert!(
6540            reason.contains("63") && reason.contains("64") && reason.contains("55"),
6541            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
6542             and the per-`:nome` budget (55), got {reason:?}"
6543        );
6544    }
6545
6546    #[test]
6547    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
6548        // The 63-byte `:nome` boundary — passes the bare-`:nome`
6549        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
6550        // joint chart name that overflows the DNS-1123 label cap
6551        // structurally. The most stringent fail-before-pass-after
6552        // surface: every `:nome` in the 56..=63-byte range passed the
6553        // prior cascade and broke at admission.
6554        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
6555        let c = caixa_with_nome(&bare_max);
6556        // The bare-`:nome` gate accepts the 63-byte length.
6557        c.validate_nome().unwrap();
6558        // The new joint-length gate rejects it.
6559        let err = c.validate_nome_chart_name_budget().unwrap_err();
6560        assert!(
6561            matches!(
6562                err,
6563                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
6564                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
6565            ),
6566            "got {err:?}"
6567        );
6568    }
6569
6570    #[test]
6571    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
6572        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
6573        // name appears verbatim in the diagnostic so the author sees
6574        // exactly the string the apiserver / `helm lint` would have
6575        // rejected — no re-derivation required to grep the source.
6576        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
6577        // on the bare-`:nome` axis.
6578        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
6579        let c = caixa_with_nome(&over);
6580        let err = c.validate_nome_chart_name_budget().unwrap_err();
6581        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
6582            panic!("expected NomeChartNameBudgetExceeded variant");
6583        };
6584        assert_eq!(nome, over);
6585        let expected_chart = crate::lareira_chart_name(&over);
6586        assert!(
6587            reason.contains(&expected_chart),
6588            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
6589             got {reason:?}"
6590        );
6591        assert!(
6592            reason.contains("lareira-"),
6593            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
6594        );
6595    }
6596
6597    #[test]
6598    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
6599        // Order pin on the layout cascade: the narrower
6600        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
6601        // joint-length budget. A structurally-malformed `:nome` (here:
6602        // uppercase) surfaces its specific shape error rather than
6603        // the chart-name-budget error, even when the joint length
6604        // would also overflow — the narrower diagnostic is more
6605        // self-locating. Mirrors the cascade-precedence pins peer
6606        // gates already use (e.g. `EntradaParaEmpty` before
6607        // `EntradaParaInvalid`).
6608        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
6609        let c = caixa_with_nome(&over);
6610        // The bare-shape gate fires first.
6611        let err = c.validate_nome().unwrap_err();
6612        assert!(
6613            matches!(err, ManifestError::NomeInvalid { .. }),
6614            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
6615        );
6616        // And the layout verify cascade surfaces that diagnostic, not
6617        // the budget arm. Inject a path-exists oracle so the cascade
6618        // gets past the manifest-presence check and into the
6619        // value-shape gates.
6620        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
6621        let err = crate::LayoutInvariants::verify(
6622            &layout,
6623            &c,
6624            std::path::Path::new("/tmp/caixa-test-fake-root"),
6625        )
6626        .unwrap_err();
6627        let issue = err.to_string();
6628        assert!(
6629            issue.contains("DNS-1123") || issue.contains("uppercase"),
6630            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
6631             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
6632        );
6633    }
6634
6635    #[test]
6636    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
6637        // Cross-axis envelope pin: the layout cascade wraps both
6638        // bare-`:nome` and joint-length-`:nome` failures through the
6639        // same [`LayoutError::NomeViolation`] envelope, since both
6640        // arms are on the `:nome` axis. The user's diagnostic stays
6641        // self-locating ("which axis"), and a future consumer that
6642        // dispatches on the layout-error variant (e.g. a `feira lint`
6643        // exit-code mapping) sees a single per-axis envelope. The
6644        // wrapped `issue:` carries the full inner diagnostic.
6645        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
6646        let c = caixa_with_nome(&over);
6647        // The bare-shape gate accepts.
6648        c.validate_nome().unwrap();
6649        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
6650        let err = crate::LayoutInvariants::verify(
6651            &layout,
6652            &c,
6653            std::path::Path::new("/tmp/caixa-test-fake-root"),
6654        )
6655        .unwrap_err();
6656        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
6657            panic!("expected LayoutError::NomeViolation, got {err:?}");
6658        };
6659        assert_eq!(caixa, over);
6660        assert!(
6661            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
6662            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
6663        );
6664    }
6665
6666    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
6667
6668    fn caixa_with_versao(versao: &str) -> Caixa {
6669        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6670        c.versao = versao.to_string();
6671        c
6672    }
6673
6674    #[test]
6675    fn validate_versao_accepts_canonical_template() {
6676        // Positive control: the bare `feira init`-style template's
6677        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
6678        // must not regress this baseline shape. A future tightening of
6679        // the accepted set surfaces here as a test failure first.
6680        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6681        c.validate_versao().unwrap();
6682    }
6683
6684    #[test]
6685    fn validate_versao_accepts_canonical_forms() {
6686        // Positive-set sweep: each realistic SemVer-2 shape the
6687        // substrate's downstream consumers accept must pass — bare
6688        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
6689        // build metadata (`+build.42`), the combined form, and the
6690        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
6691        // the peer `:nome` axis (6c992f8).
6692        for versao in [
6693            "0.1.0",
6694            "0.0.0",
6695            "1.0.0",
6696            "0.2.0-rc.1",
6697            "1.0.0-alpha.0",
6698            "1.0.0+build.42",
6699            "1.0.0-rc.1+build.42",
6700            "10.20.30",
6701        ] {
6702            caixa_with_versao(versao)
6703                .validate_versao()
6704                .unwrap_or_else(|e| {
6705                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
6706                });
6707        }
6708    }
6709
6710    #[test]
6711    fn validate_versao_rejects_empty() {
6712        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
6713        // an empty `:versao` (the derive macro stores the raw String);
6714        // the gate's empty arm names the offending axis with a narrower
6715        // diagnostic than the `VersaoInvalid` parse arm would emit.
6716        // Mirrors `validate_nome_rejects_empty` (6c992f8).
6717        let c = caixa_with_versao("");
6718        let err = c.validate_versao().unwrap_err();
6719        assert_eq!(err, ManifestError::VersaoEmpty);
6720    }
6721
6722    #[test]
6723    fn validate_versao_rejects_git_tag_shape() {
6724        // The canonical "I copied the git tag verbatim" footgun —
6725        // `feira publish` *emits* `v<versao>` git tags, so a leaked
6726        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
6727        // shift every downstream consumer's version axis. `semver`
6728        // rejects the leading `v` at parse time; the gate moves the
6729        // diagnostic to the source `caixa.lisp`.
6730        let c = caixa_with_versao("v0.1.0");
6731        let err = c.validate_versao().unwrap_err();
6732        let ManifestError::VersaoInvalid { versao, reason } = err else {
6733            panic!("expected VersaoInvalid for git-tag-shape :versao");
6734        };
6735        assert_eq!(versao, "v0.1.0");
6736        assert!(
6737            !reason.is_empty(),
6738            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
6739        );
6740    }
6741
6742    #[test]
6743    fn validate_versao_rejects_missing_patch() {
6744        // The canonical "I shortened it" footgun — SemVer-2 requires
6745        // three parts. Cargo's `version =` field accepts the shortened
6746        // form as a requirement, conflating the two leaks across the
6747        // typed `:deps :versao` vs top-level `:versao` axes; the gate
6748        // pins the top-level axis to the strict three-part shape.
6749        let c = caixa_with_versao("0.1");
6750        let err = c.validate_versao().unwrap_err();
6751        assert!(
6752            matches!(
6753                err,
6754                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
6755            ),
6756            "got {err:?}"
6757        );
6758    }
6759
6760    #[test]
6761    fn validate_versao_rejects_requirement_shape() {
6762        // The canonical "I leaked a requirement into a version" footgun —
6763        // the typed `:deps :versao` / `:membros :versao` axes accept
6764        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
6765        // concrete `Version`. Without this gate the two typed surfaces
6766        // would silently overlap, and a top-level `^0.1` would surface
6767        // at `helm install` time as a Chart.yaml version rejection far
6768        // from the source `caixa.lisp`.
6769        let c = caixa_with_versao("^0.1");
6770        let err = c.validate_versao().unwrap_err();
6771        assert!(
6772            matches!(
6773                err,
6774                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
6775            ),
6776            "got {err:?}"
6777        );
6778    }
6779
6780    #[test]
6781    fn validate_versao_rejects_docker_tag_shape() {
6782        // The "I confused it with a docker tag" footgun — `latest`,
6783        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
6784        // SemVer rejects at parse time; the gate moves the diagnostic
6785        // to the source `caixa.lisp`.
6786        for bad in ["latest", "main", "stable"] {
6787            let c = caixa_with_versao(bad);
6788            let err = c.validate_versao().unwrap_err();
6789            assert!(
6790                matches!(
6791                    err,
6792                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
6793                ),
6794                "got {err:?} for {bad:?}"
6795            );
6796        }
6797    }
6798
6799    #[test]
6800    fn validate_versao_rejects_four_part_form() {
6801        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
6802        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
6803        // semver crate rejects the extra `.0` at parse time.
6804        let c = caixa_with_versao("0.1.0.0");
6805        let err = c.validate_versao().unwrap_err();
6806        assert!(
6807            matches!(
6808                err,
6809                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
6810            ),
6811            "got {err:?}"
6812        );
6813    }
6814
6815    #[test]
6816    fn versao_empty_takes_precedence_over_invalid() {
6817        // Order pin: the empty arm fires before the parser is consulted.
6818        // Empty < invalid in self-locating-ness — the narrower
6819        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
6820        // reference into the parser-shaped reason. Mirrors
6821        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
6822        // peer axis.
6823        let c = caixa_with_versao("");
6824        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
6825    }
6826
6827    #[test]
6828    fn versao_invalid_diagnostic_carries_offending_versao() {
6829        // Diagnostic-shape pin: the error names the offending `:versao`
6830        // verbatim with a non-empty parser-shaped reason, so a `feira
6831        // lint` run can render the diagnostic without re-parsing.
6832        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
6833        let c = caixa_with_versao("v0.1.0");
6834        let err = c.validate_versao().unwrap_err();
6835        let ManifestError::VersaoInvalid { versao, reason } = err else {
6836            panic!("expected VersaoInvalid variant");
6837        };
6838        assert_eq!(versao, "v0.1.0");
6839        assert!(
6840            !reason.is_empty(),
6841            "VersaoInvalid `reason` must carry the parser's wording verbatim"
6842        );
6843    }
6844
6845    #[test]
6846    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
6847        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
6848        // for `:upgrade-from :from` must also pass `validate_versao` —
6849        // the two `:versao`-typed surfaces (top-level `:versao`,
6850        // `:upgrade-from :from`) consume the *same* `semver::Version`
6851        // parser, so they must agree on the accepted set. Without this
6852        // pin, a future tightening of one axis could silently diverge
6853        // from the other. Mirrors the `:versao` requirement-axis
6854        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
6855        // commits established.
6856        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
6857            // From the canonical UpgradeFromEntry round-trip fixture
6858            // (`upgrade::tests::round_trip_load_module` peers).
6859            let entry = crate::UpgradeFromEntry {
6860                from: versao.to_string(),
6861                instructions: Vec::new(),
6862            };
6863            entry
6864                .validate()
6865                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
6866            caixa_with_versao(versao)
6867                .validate_versao()
6868                .unwrap_or_else(|e| {
6869                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
6870                });
6871        }
6872    }
6873
6874    // ── Caixa::validate_restart_window — supervisor restart-window
6875    //    folds through the shared `supervisor::duration_codec` ────────
6876
6877    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
6878        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
6879        c.kind = CaixaKind::Supervisor;
6880        c.restart_window = window.map(str::to_string);
6881        c
6882    }
6883
6884    #[test]
6885    fn validate_restart_window_accepts_none() {
6886        // The canonical "omit the slot to express no reset" shape — a
6887        // `None` raw string is the absence of the typed
6888        // `:restart-window` slot, which is exactly the SupervisorSpec
6889        // "never reset" semantics. The gate must be a no-op here; a
6890        // future tightening that rejected `None` would force every
6891        // supervisor caixa to authoring-time pin a window even when
6892        // the OTP semantics call for none.
6893        caixa_with_restart_window(None)
6894            .validate_restart_window()
6895            .unwrap();
6896    }
6897
6898    #[test]
6899    fn validate_restart_window_accepts_canonical_forms() {
6900        // Positive-set sweep across the canonical authoring units the
6901        // shared `supervisor::duration_codec::parse` accepts —
6902        // matches the codec-side `parse_accepts_integer_canonical_units`
6903        // pin in supervisor::tests so a future codec-side tightening
6904        // surfaces simultaneously on both axes.
6905        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
6906            caixa_with_restart_window(Some(window))
6907                .validate_restart_window()
6908                .unwrap_or_else(|e| {
6909                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
6910                });
6911        }
6912    }
6913
6914    #[test]
6915    fn validate_restart_window_rejects_fractional_seconds() {
6916        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
6917        // as f64 to 1.5 → renders back as `"1500ms"` on first
6918        // serialize). Prior to the fold + this gate, the inline
6919        // `parse_window_inline` accepted f64 magnitudes and silently
6920        // produced a `Duration::from_secs_f64(1.5)`, divergent from
6921        // the shared codec's integer-magnitude discipline on the
6922        // serde-routed siblings. The gate now surfaces a self-locating
6923        // diagnostic at the manifest layer.
6924        let err = caixa_with_restart_window(Some("1.5s"))
6925            .validate_restart_window()
6926            .unwrap_err();
6927        let ManifestError::RestartWindowMalformed {
6928            restart_window,
6929            reason,
6930        } = err
6931        else {
6932            panic!("expected RestartWindowMalformed for fractional seconds");
6933        };
6934        assert_eq!(restart_window, "1.5s");
6935        assert!(
6936            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
6937            "diagnostic must carry shared-codec wording, got {reason:?}"
6938        );
6939    }
6940
6941    #[test]
6942    fn validate_restart_window_rejects_decimal_shaped_integer() {
6943        // The `"1.0s"` class — numerically `1s` exactly, but the
6944        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
6945        // gets the same canonical-form diagnostic.
6946        let err = caixa_with_restart_window(Some("1.0s"))
6947            .validate_restart_window()
6948            .unwrap_err();
6949        assert!(
6950            matches!(
6951                err,
6952                ManifestError::RestartWindowMalformed { ref restart_window, .. }
6953                    if restart_window == "1.0s"
6954            ),
6955            "got {err:?}"
6956        );
6957    }
6958
6959    #[test]
6960    fn validate_restart_window_rejects_half_unit_minute() {
6961        // `"0.5m"` is the unit-fraction footgun — author writes a
6962        // human-readable half-minute, the prior inline parser silently
6963        // produced `Duration::from_secs_f64(30.0)` and serde
6964        // re-emitted as `"30s"`, rewriting author intent. The gate
6965        // closes the loop at the manifest layer.
6966        let err = caixa_with_restart_window(Some("0.5m"))
6967            .validate_restart_window()
6968            .unwrap_err();
6969        let ManifestError::RestartWindowMalformed {
6970            restart_window,
6971            reason,
6972        } = err
6973        else {
6974            panic!("expected RestartWindowMalformed");
6975        };
6976        assert_eq!(restart_window, "0.5m");
6977        assert!(
6978            reason.contains("\"30s\""),
6979            "diagnostic must point at the canonical-form remediation, got {reason:?}"
6980        );
6981    }
6982
6983    #[test]
6984    fn validate_restart_window_rejects_leading_sign() {
6985        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
6986        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
6987        // and was caught by the `num < 0.0` arm which silently
6988        // returned `None`, dropping the author-supplied window). The
6989        // shared codec's digit-only gate rejects both with a unified
6990        // canonical-form diagnostic; the manifest-layer wrapper names
6991        // the offending value.
6992        for bad in ["+30s", "-30s"] {
6993            let err = caixa_with_restart_window(Some(bad))
6994                .validate_restart_window()
6995                .unwrap_err();
6996            assert!(
6997                matches!(
6998                    err,
6999                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
7000                        if restart_window == bad
7001                ),
7002                "got {err:?} for {bad:?}"
7003            );
7004        }
7005    }
7006
7007    #[test]
7008    fn validate_restart_window_rejects_unknown_unit() {
7009        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
7010        // unit dispatch surfaces an `unknown duration unit` reason;
7011        // the manifest-layer wrapper names the offending value.
7012        let err = caixa_with_restart_window(Some("30x"))
7013            .validate_restart_window()
7014            .unwrap_err();
7015        let ManifestError::RestartWindowMalformed {
7016            restart_window,
7017            reason,
7018        } = err
7019        else {
7020            panic!("expected RestartWindowMalformed for unknown unit");
7021        };
7022        assert_eq!(restart_window, "30x");
7023        assert!(
7024            reason.contains("unknown duration unit"),
7025            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
7026        );
7027    }
7028
7029    #[test]
7030    fn validate_restart_window_rejects_garbage() {
7031        // Pure non-numeric magnitude (`"abc"`) falls through to the
7032        // shared codec's narrower `"bad duration magnitude"` arm. Same
7033        // diagnostic shape as the codec-side
7034        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
7035        let err = caixa_with_restart_window(Some("abc"))
7036            .validate_restart_window()
7037            .unwrap_err();
7038        let ManifestError::RestartWindowMalformed {
7039            restart_window,
7040            reason,
7041        } = err
7042        else {
7043            panic!("expected RestartWindowMalformed for garbage");
7044        };
7045        assert_eq!(restart_window, "abc");
7046        assert!(
7047            reason.contains("bad duration magnitude"),
7048            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
7049        );
7050    }
7051
7052    #[test]
7053    fn validate_restart_window_rejects_empty_string() {
7054        // The empty-after-trim edge case — distinct from the `None`
7055        // canonical "omit the slot" shape. The shared codec's
7056        // digit-only gate refuses an empty magnitude; the manifest
7057        // layer names the offending `""` so the author can grep for
7058        // the literal empty value in their `caixa.lisp` and either
7059        // remove the slot (the canonical "no reset" shape) or pin a
7060        // positive duration.
7061        let err = caixa_with_restart_window(Some(""))
7062            .validate_restart_window()
7063            .unwrap_err();
7064        assert!(
7065            matches!(
7066                err,
7067                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7068                    if restart_window.is_empty()
7069            ),
7070            "got {err:?}"
7071        );
7072    }
7073
7074    #[test]
7075    fn validate_restart_window_diagnostic_carries_offending_value() {
7076        // Diagnostic-shape pin (peer with
7077        // `nome_invalid_diagnostic_carries_offending_nome` /
7078        // `versao_invalid_diagnostic_carries_offending_versao`): the
7079        // error names the offending raw `:restart-window` verbatim
7080        // with a non-empty shared-codec-shaped reason, so a `feira
7081        // lint` run can render the diagnostic without re-parsing.
7082        let err = caixa_with_restart_window(Some("1.5s"))
7083            .validate_restart_window()
7084            .unwrap_err();
7085        let ManifestError::RestartWindowMalformed {
7086            restart_window,
7087            reason,
7088        } = err
7089        else {
7090            panic!("expected RestartWindowMalformed variant");
7091        };
7092        assert_eq!(restart_window, "1.5s");
7093        assert!(
7094            !reason.is_empty(),
7095            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
7096        );
7097    }
7098
7099    #[test]
7100    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
7101        // Behavioral parity pin after the fold (`parse_window_inline`
7102        // deletion): the canonical `"60s"` still produces
7103        // `Duration::from_secs(60)` on the typed view — the fold is
7104        // semantically equivalent to the prior inline parser on the
7105        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
7106        // pin, narrowed to the parser-side contract.
7107        let c = caixa_with_restart_window(Some("60s"));
7108        let view = c.supervisor_view().expect("Supervisor kind has a view");
7109        assert_eq!(
7110            view.restart_window,
7111            Some(std::time::Duration::from_secs(60))
7112        );
7113    }
7114
7115    #[test]
7116    fn supervisor_view_soft_swallows_what_validate_rejects() {
7117        // Parity pin between the view-construction path and the
7118        // manifest-level validator: the same `"1.5s"` that surfaces
7119        // `RestartWindowMalformed` at `validate_restart_window` time
7120        // becomes `restart_window: None` on the typed view (the fold
7121        // preserves the existing best-effort shape of `supervisor_view`).
7122        // The contract is: a layout-verifier / `feira lint` flow that
7123        // cares about the malformed-window axis MUST consult
7124        // `validate_restart_window` — relying solely on the view's
7125        // `None` swallows the diagnostic silently. This pin makes the
7126        // expectation a typed invariant.
7127        let c = caixa_with_restart_window(Some("1.5s"));
7128        let view = c.supervisor_view().expect("Supervisor kind has a view");
7129        assert_eq!(
7130            view.restart_window, None,
7131            "view-construction path soft-swallows the parse error to None"
7132        );
7133        // And the manifest-level validator does NOT soft-swallow:
7134        assert!(
7135            matches!(
7136                c.validate_restart_window().unwrap_err(),
7137                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7138                    if restart_window == "1.5s"
7139            ),
7140            "validator must surface the offending value",
7141        );
7142    }
7143
7144    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
7145
7146    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
7147        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7148        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
7149        c.exe = exe.into_iter().map(String::from).collect();
7150        c.servicos = servicos.into_iter().map(String::from).collect();
7151        c
7152    }
7153
7154    #[test]
7155    fn validate_code_paths_accepts_canonical_template() {
7156        // The bare `Caixa::template` shape is the gate's identity element
7157        // on the canonical authoring shape — `:bibliotecas
7158        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
7159        // that the gate is non-disruptive against every existing caixa.
7160        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7161        c.validate_code_paths().unwrap();
7162    }
7163
7164    #[test]
7165    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
7166        // Positive control sweep: a canonical-shaped path on every slot
7167        // passes. Mirrors the peer
7168        // `behavior::validate_every_slot_relative_is_ok` pin.
7169        let c = caixa_with_code_paths(
7170            vec!["lib/demo.lisp", "lib/helpers.lisp"],
7171            vec!["exe/demo", "exe/tool"],
7172            vec!["servicos/demo.computeunit.yaml"],
7173        );
7174        c.validate_code_paths().unwrap();
7175    }
7176
7177    #[test]
7178    fn validate_code_paths_accepts_all_empty_lists() {
7179        // The empty-list identity element: every Caixa with no declared
7180        // code paths trivially passes (Supervisor / Aplicacao kinds rely
7181        // on this — the OwnCode gate already rejected them before the
7182        // path-shape gate runs in the layout, but the validator itself
7183        // must accept the empty shape).
7184        let c = caixa_with_code_paths(vec![], vec![], vec![]);
7185        c.validate_code_paths().unwrap();
7186    }
7187
7188    #[test]
7189    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
7190        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
7191        let err = c.validate_code_paths().unwrap_err();
7192        assert!(
7193            matches!(
7194                err,
7195                ManifestError::CodePathEmpty {
7196                    slot: ":bibliotecas"
7197                }
7198            ),
7199            "got {err:?}",
7200        );
7201    }
7202
7203    #[test]
7204    fn validate_code_paths_rejects_empty_exe_entry() {
7205        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
7206        let err = c.validate_code_paths().unwrap_err();
7207        assert!(
7208            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
7209            "got {err:?}",
7210        );
7211    }
7212
7213    #[test]
7214    fn validate_code_paths_rejects_empty_servicos_entry() {
7215        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
7216        let err = c.validate_code_paths().unwrap_err();
7217        assert!(
7218            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
7219            "got {err:?}",
7220        );
7221    }
7222
7223    #[test]
7224    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
7225        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
7226        // so an absolute path that resolves on disk silently passes the
7227        // layout's existence check — the canonical sandbox-escape on
7228        // the biblioteca axis.
7229        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7230        let err = c.validate_code_paths().unwrap_err();
7231        let ManifestError::CodePathAbsolute { slot, path } = err else {
7232            panic!("expected CodePathAbsolute, got {err:?}");
7233        };
7234        assert_eq!(slot, ":bibliotecas");
7235        assert_eq!(path, PathBuf::from("/etc/passwd"));
7236    }
7237
7238    #[test]
7239    fn validate_code_paths_rejects_absolute_exe_entry() {
7240        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
7241        let err = c.validate_code_paths().unwrap_err();
7242        let ManifestError::CodePathAbsolute { slot, path } = err else {
7243            panic!("expected CodePathAbsolute, got {err:?}");
7244        };
7245        assert_eq!(slot, ":exe");
7246        assert_eq!(path, PathBuf::from("/usr/bin/env"));
7247    }
7248
7249    #[test]
7250    fn validate_code_paths_rejects_absolute_servicos_entry() {
7251        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
7252        let err = c.validate_code_paths().unwrap_err();
7253        let ManifestError::CodePathAbsolute { slot, path } = err else {
7254            panic!("expected CodePathAbsolute, got {err:?}");
7255        };
7256        assert_eq!(slot, ":servicos");
7257        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
7258    }
7259
7260    #[test]
7261    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
7262        // Canonical "I want a lib from a sibling caixa" footgun on the
7263        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
7264        // downstream, so a leading `..` traverses to the parent of the
7265        // caixa root with no diagnostic at layout time if the resolved
7266        // target exists.
7267        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
7268        let err = c.validate_code_paths().unwrap_err();
7269        let ManifestError::CodePathParentEscape { slot, path } = err else {
7270            panic!("expected CodePathParentEscape, got {err:?}");
7271        };
7272        assert_eq!(slot, ":bibliotecas");
7273        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
7274    }
7275
7276    #[test]
7277    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
7278        // Mid-path `..` defeats the layout's component-aware
7279        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
7280        // `starts_with(<root>/exe)` is true, but the canonical resolution
7281        // lives outside the caixa root. Caught regardless of where the
7282        // `..` sits — mirrors the peer
7283        // `behavior::validate_rejects_parent_escape_mid_path` pin.
7284        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
7285        let err = c.validate_code_paths().unwrap_err();
7286        let ManifestError::CodePathParentEscape { slot, path } = err else {
7287            panic!("expected CodePathParentEscape, got {err:?}");
7288        };
7289        assert_eq!(slot, ":exe");
7290        assert_eq!(path, PathBuf::from("exe/../../escape"));
7291    }
7292
7293    #[test]
7294    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
7295        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
7296        let err = c.validate_code_paths().unwrap_err();
7297        let ManifestError::CodePathParentEscape { slot, path } = err else {
7298            panic!("expected CodePathParentEscape, got {err:?}");
7299        };
7300        assert_eq!(slot, ":servicos");
7301        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
7302    }
7303
7304    #[test]
7305    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
7306        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
7307        // `:servicos`. A manifest with malformed entries on all three
7308        // surfaces surfaces the `:bibliotecas` defect first, mirroring
7309        // the canonical declaration order
7310        // `Caixa::declared_foreign_code_slots` already establishes for
7311        // the foreign-code-slot diagnostic.
7312        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
7313        let err = c.validate_code_paths().unwrap_err();
7314        assert!(
7315            matches!(
7316                err,
7317                ManifestError::CodePathEmpty {
7318                    slot: ":bibliotecas"
7319                }
7320            ),
7321            "got {err:?}",
7322        );
7323    }
7324
7325    #[test]
7326    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
7327        // Within-slot precedence pin: empty → absolute → parent-escape,
7328        // matching the [`PathShapeViolation`] arm-ordering every peer
7329        // `is_sandboxed_relative_path` caller follows (b0c8389
7330        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
7331        // `:bibliotecas` list whose first entry is empty *and* whose
7332        // later entries are absolute/parent-escape surfaces the empty
7333        // arm first, on the lexicographically-earliest offending entry.
7334        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
7335        let err = c.validate_code_paths().unwrap_err();
7336        assert!(
7337            matches!(
7338                err,
7339                ManifestError::CodePathEmpty {
7340                    slot: ":bibliotecas"
7341                }
7342            ),
7343            "got {err:?}",
7344        );
7345    }
7346
7347    #[test]
7348    fn validate_code_paths_first_offender_per_slot_wins() {
7349        // Within a single slot, the first declaration-order offender
7350        // surfaces — pins that the gate is left-to-right deterministic
7351        // (peer of every `*_first_collision_*` pin on duplicate gates).
7352        let c = caixa_with_code_paths(
7353            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
7354            vec![],
7355            vec![],
7356        );
7357        let err = c.validate_code_paths().unwrap_err();
7358        let ManifestError::CodePathAbsolute { slot, path } = err else {
7359            panic!("expected CodePathAbsolute, got {err:?}");
7360        };
7361        assert_eq!(slot, ":bibliotecas");
7362        assert_eq!(path, PathBuf::from("/etc/escape"));
7363    }
7364
7365    #[test]
7366    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
7367        // Diagnostic-shape pin (peer with
7368        // `nome_invalid_diagnostic_carries_offending_nome` /
7369        // `versao_invalid_diagnostic_carries_offending_versao`): the
7370        // error's Display surfaces both the offending `:slot` tag and
7371        // the offending path verbatim, so a `feira lint` run can render
7372        // the diagnostic without re-parsing.
7373        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7374        let rendered = c.validate_code_paths().unwrap_err().to_string();
7375        assert!(
7376            rendered.contains(":bibliotecas"),
7377            "diagnostic must name the offending slot: {rendered}",
7378        );
7379        assert!(
7380            rendered.contains("/etc/passwd"),
7381            "diagnostic must quote the offending path: {rendered}",
7382        );
7383    }
7384
7385    #[test]
7386    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
7387        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
7388        // axis. Without the gate `feira build` re-parses the same lib
7389        // twice, wasting work and silently masking the author's intent
7390        // to declare a *second* biblioteca.
7391        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
7392        let err = c.validate_code_paths().unwrap_err();
7393        let ManifestError::CodePathDuplicate { slot, path } = err else {
7394            panic!("expected CodePathDuplicate, got {err:?}");
7395        };
7396        assert_eq!(slot, ":bibliotecas");
7397        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
7398    }
7399
7400    #[test]
7401    fn validate_code_paths_rejects_duplicate_exe_entry() {
7402        // Same footgun on the Binario surface. The future `caixa-flake`
7403        // emitter that materializes each `:exe` entry as a flake
7404        // `packages.<name>` derivation would collide on the duplicate
7405        // package key — surfaced here at the typed-validate layer with a
7406        // self-locating diagnostic instead.
7407        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
7408        let err = c.validate_code_paths().unwrap_err();
7409        let ManifestError::CodePathDuplicate { slot, path } = err else {
7410            panic!("expected CodePathDuplicate, got {err:?}");
7411        };
7412        assert_eq!(slot, ":exe");
7413        assert_eq!(path, PathBuf::from("exe/cli"));
7414    }
7415
7416    #[test]
7417    fn validate_code_paths_rejects_duplicate_servicos_entry() {
7418        // Same footgun on the Servico surface. The peer caixa-helm /
7419        // caixa-flux renderers refuse `:servicos.len() != 1` with the
7420        // narrower `UnsupportedServicoCount` diagnostic, but that
7421        // diagnostic surfaces "too many servicos" without naming
7422        // "duplicate entry" — the typed self-locating framing only lands
7423        // at this gate.
7424        let c = caixa_with_code_paths(
7425            vec![],
7426            vec![],
7427            vec![
7428                "servicos/demo.computeunit.yaml",
7429                "servicos/demo.computeunit.yaml",
7430            ],
7431        );
7432        let err = c.validate_code_paths().unwrap_err();
7433        let ManifestError::CodePathDuplicate { slot, path } = err else {
7434            panic!("expected CodePathDuplicate, got {err:?}");
7435        };
7436        assert_eq!(slot, ":servicos");
7437        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
7438    }
7439
7440    #[test]
7441    fn validate_code_paths_accepts_same_path_across_slots() {
7442        // Per-list scope pin: a `:bibliotecas` entry that happens to
7443        // collide with an `:exe` or `:servicos` entry as a *string* is
7444        // not a duplicate by this gate (each list gets its own HashSet),
7445        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
7446        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
7447        // shape on the dep axis). The structural `starts_with(<exe |
7448        // servicos>_dir)` fence at layout time prevents the realistic
7449        // cross-slot collision case from existing on disk, but the gate's
7450        // per-list scope is correct independent of that downstream fence.
7451        let c = caixa_with_code_paths(
7452            vec!["lib/x.lisp"],
7453            vec!["exe/x"],
7454            vec!["servicos/x.computeunit.yaml"],
7455        );
7456        c.validate_code_paths().unwrap();
7457    }
7458
7459    #[test]
7460    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
7461        // Within-slot ordering pin: structural defects (empty / absolute
7462        // / parent-escape) fire before the duplicate gate on the same
7463        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
7464        // surfaces the narrower `CodePathEmpty` for the empty entry
7465        // first, not the duplicate on the later pair — same arm-ordering
7466        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
7467        // `:autores` 86c769b, `:deps` 359fba5).
7468        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
7469        let err = c.validate_code_paths().unwrap_err();
7470        assert!(
7471            matches!(
7472                err,
7473                ManifestError::CodePathEmpty {
7474                    slot: ":bibliotecas"
7475                }
7476            ),
7477            "got {err:?}",
7478        );
7479    }
7480
7481    #[test]
7482    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
7483        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
7484        // duplicates surface before `:exe` duplicates, matching the
7485        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
7486        // order every peer per-slot diagnostic on this surface follows.
7487        let c = caixa_with_code_paths(
7488            vec!["lib/x.lisp", "lib/x.lisp"],
7489            vec!["exe/y", "exe/y"],
7490            vec![],
7491        );
7492        let err = c.validate_code_paths().unwrap_err();
7493        let ManifestError::CodePathDuplicate { slot, path } = err else {
7494            panic!("expected CodePathDuplicate, got {err:?}");
7495        };
7496        assert_eq!(slot, ":bibliotecas");
7497        assert_eq!(path, PathBuf::from("lib/x.lisp"));
7498    }
7499
7500    #[test]
7501    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
7502        // Diagnostic-shape pin (peer with
7503        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
7504        // on the structural arm): the duplicate-arm Display surfaces both
7505        // the offending `:slot` tag and the offending path verbatim, so a
7506        // `feira lint` run can render the diagnostic without re-parsing.
7507        let c = caixa_with_code_paths(
7508            vec![],
7509            vec![],
7510            vec![
7511                "servicos/demo.computeunit.yaml",
7512                "servicos/demo.computeunit.yaml",
7513            ],
7514        );
7515        let rendered = c.validate_code_paths().unwrap_err().to_string();
7516        assert!(
7517            rendered.contains(":servicos"),
7518            "diagnostic must name the offending slot: {rendered}",
7519        );
7520        assert!(
7521            rendered.contains("servicos/demo.computeunit.yaml"),
7522            "diagnostic must quote the offending path: {rendered}",
7523        );
7524    }
7525
7526    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
7527    //
7528    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
7529    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
7530    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
7531    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
7532    // at parse time — the same downstream consumer the peer `:behavior
7533    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
7534    // `:upgrade-from :state-change :script` (33cc830,
7535    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
7536    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
7537    // nix-built executable surface (`"exe/<name>"` shape per the canonical
7538    // [`crate::LayoutError::ExeOutsideDir`] error message and every
7539    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
7540    // is the `.computeunit.yaml` ComputeUnit-CR axis.
7541
7542    #[test]
7543    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
7544        // Canonical "I dragged the wrong file from the workspace tree"
7545        // footgun on the biblioteca axis. Without the gate `feira build`
7546        // hands the extensionless path to `tatara_lisp::read` and fails
7547        // with a parser-shaped diagnostic far from the source caixa.lisp,
7548        // with no field naming the offending `:bibliotecas` entry.
7549        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
7550            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
7551            let err = c.validate_code_paths().unwrap_err();
7552            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
7553                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
7554            };
7555            assert_eq!(slot, ":bibliotecas");
7556            assert_eq!(path, PathBuf::from(relpath));
7557        }
7558    }
7559
7560    #[test]
7561    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
7562        // Wrong-extension sweep across common authoring footguns. Same
7563        // sweep posture as the peer
7564        // `behavior::validate_rejects_wrong_extension` (c97815a) and
7565        // `upgrade::tests::state_change_rejects_wrong_extension_script`
7566        // (33cc830) cases.
7567        for relpath in [
7568            "lib/demo.rs",
7569            "lib/demo.txt",
7570            "lib/demo.md",
7571            "lib/demo.json",
7572            "lib/demo.yaml",
7573            "lib/demo.toml",
7574            "lib/demo.lisp.bak",
7575            "lib/demo.lispx",
7576            "lib/demo.lis",
7577        ] {
7578            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
7579            let err = c.validate_code_paths().unwrap_err();
7580            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
7581                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
7582            };
7583            assert_eq!(slot, ":bibliotecas");
7584            assert_eq!(path, PathBuf::from(relpath));
7585        }
7586    }
7587
7588    #[test]
7589    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
7590        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
7591        // contract. An uppercase `.LISP` shape that the layout's existence
7592        // check would (case-insensitively, on case-insensitive volumes)
7593        // match the on-disk file still mismatches the canonical form the
7594        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
7595        // contract. Mirrors the peer
7596        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
7597        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
7598        // (33cc830) sweeps.
7599        for relpath in [
7600            "lib/demo.LISP",
7601            "lib/demo.Lisp",
7602            "lib/demo.LiSp",
7603            "lib/demo.lISP",
7604        ] {
7605            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
7606            let err = c.validate_code_paths().unwrap_err();
7607            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
7608                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
7609            };
7610            assert_eq!(slot, ":bibliotecas");
7611            assert_eq!(path, PathBuf::from(relpath));
7612        }
7613    }
7614
7615    #[test]
7616    fn validate_code_paths_accepts_canonical_lisp_shapes() {
7617        // Positive-control sweep through every canonical authoring shape
7618        // every in-tree fixture and the `Caixa::template` scaffold use.
7619        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
7620        // (c97815a) and the lifted predicate's own
7621        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
7622        // (33cc830).
7623        for relpath in [
7624            "lib/demo.lisp",
7625            "lib/handlers.lisp",
7626            "lib/migrations/v01-to-v02.lisp",
7627            "demo.lisp",
7628            "a.lisp",
7629            "./lib/demo.lisp",
7630            "lib/./handlers.lisp",
7631            "lib/migrations/v.0.1.lisp",
7632        ] {
7633            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
7634            c.validate_code_paths()
7635                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
7636        }
7637    }
7638
7639    #[test]
7640    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
7641        // The file-type gate is per-slot — only `:bibliotecas` carries the
7642        // tatara-lisp-source contract. An extensionless `:exe` entry
7643        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
7644        // canonical shapes every in-tree fixture uses, and must continue
7645        // to pass validate. Pins that a future tightening that broadens
7646        // the `.lisp` gate to either axis surfaces as a test failure
7647        // rather than as a silent breaking change to existing valid
7648        // manifests.
7649        let c = caixa_with_code_paths(
7650            vec![],
7651            vec!["exe/demo", "exe/tool"],
7652            vec!["servicos/demo.computeunit.yaml"],
7653        );
7654        c.validate_code_paths().unwrap();
7655    }
7656
7657    #[test]
7658    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
7659        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
7660        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
7661        // sandbox-shape diagnostic first (the `.lisp` remediation would
7662        // be misleading when the offending path can never resolve under
7663        // the caixa root anyway). Mirrors the peer
7664        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
7665        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
7666        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
7667        // on `:upgrade-from :state-change :script` (33cc830).
7668        //
7669        // Empty wins (the strictly-smaller-scope structural arm).
7670        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
7671        assert!(
7672            matches!(
7673                c.validate_code_paths().unwrap_err(),
7674                ManifestError::CodePathEmpty {
7675                    slot: ":bibliotecas"
7676                }
7677            ),
7678            "empty must win over non-lisp-extension",
7679        );
7680        // Absolute wins (the path can't resolve under the caixa root).
7681        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7682        let err = c.validate_code_paths().unwrap_err();
7683        let ManifestError::CodePathAbsolute { slot, .. } = err else {
7684            panic!("absolute must win over non-lisp-extension, got {err:?}");
7685        };
7686        assert_eq!(slot, ":bibliotecas");
7687        // ParentEscape wins (the path escapes the caixa root).
7688        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
7689        let err = c.validate_code_paths().unwrap_err();
7690        let ManifestError::CodePathParentEscape { slot, .. } = err else {
7691            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
7692        };
7693        assert_eq!(slot, ":bibliotecas");
7694    }
7695
7696    #[test]
7697    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
7698        // Within-slot precedence pin: the per-entry file-type shape gate
7699        // fires before the cross-entry duplicate gate, so the narrower
7700        // structural defect dominates the uniqueness diagnostic. A
7701        // `("lib/x.txt" "lib/x.txt")` shape surfaces
7702        // `CodePathNonLispExtension` on the first entry rather than
7703        // `CodePathDuplicate` on the pair — same posture every per-entry
7704        // shape-gate-precedes-duplicate cascade follows on this surface
7705        // (the empty / absolute / parent-escape arms already precede the
7706        // duplicate arm; the lifted file-type arm joins that set).
7707        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
7708        let err = c.validate_code_paths().unwrap_err();
7709        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
7710            panic!("expected CodePathNonLispExtension, got {err:?}");
7711        };
7712        assert_eq!(slot, ":bibliotecas");
7713        assert_eq!(path, PathBuf::from("lib/x.txt"));
7714    }
7715
7716    #[test]
7717    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
7718        // Diagnostic-shape pin (peer with
7719        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
7720        // on the sandbox-shape arms and
7721        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
7722        // on the duplicate arm): the file-type-arm Display surfaces both
7723        // the offending `:slot` tag, the offending path verbatim, and the
7724        // expected `.lisp` extension named in the remediation text, so a
7725        // `feira lint` run can render the diagnostic without re-parsing.
7726        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
7727        let rendered = c.validate_code_paths().unwrap_err().to_string();
7728        assert!(
7729            rendered.contains(":bibliotecas"),
7730            "diagnostic must name the offending slot: {rendered}",
7731        );
7732        assert!(
7733            rendered.contains("lib/demo.rs"),
7734            "diagnostic must quote the offending path: {rendered}",
7735        );
7736        assert!(
7737            rendered.contains(".lisp"),
7738            "diagnostic must name the expected extension: {rendered}",
7739        );
7740    }
7741
7742    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
7743    //
7744    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
7745    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
7746    // contract. The peer caixa-helm / caixa-flux renderers consume each
7747    // `:servicos` entry through `serde_yaml::from_str` as a typed
7748    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
7749    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
7750    // axis `Path::extension` can't express on its own.
7751
7752    #[test]
7753    fn validate_code_paths_rejects_no_extension_servicos_entry() {
7754        // Canonical "I dragged the wrong file from the workspace tree"
7755        // footgun on the Servico axis. Without the gate the peer
7756        // caixa-helm / caixa-flux renderers hand the extensionless path
7757        // to `serde_yaml::from_str` and fail with a parser-shaped
7758        // diagnostic far from the source caixa.lisp, with no field
7759        // naming the offending `:servicos` entry.
7760        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
7761            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
7762            let err = c.validate_code_paths().unwrap_err();
7763            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
7764                panic!(
7765                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
7766                     got {err:?}"
7767                );
7768            };
7769            assert_eq!(slot, ":servicos");
7770            assert_eq!(path, PathBuf::from(relpath));
7771        }
7772    }
7773
7774    #[test]
7775    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
7776        // Wrong-extension sweep across common authoring footguns on the
7777        // Servico axis. Bare `.yaml` is the canonical "I forgot the
7778        // `.computeunit` segment" typo; the off-by-one-segment shapes
7779        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
7780        // bare `Path::extension` view but mismatch the typed compound
7781        // suffix the renderers' `serde_yaml::from_str` consumer demands.
7782        // Same sweep-posture as the peer
7783        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
7784        // (64772a9) on the sibling tatara-lisp-source axis.
7785        for relpath in [
7786            "servicos/demo.yaml",
7787            "servicos/demo.yml",
7788            "servicos/demo.json",
7789            "servicos/demo.toml",
7790            "servicos/demo.txt",
7791            "servicos/demo.computeunit.yaml.bak",
7792            "servicos/demo.computeunit.yam",
7793            "servicos/demo.computeunit",
7794            "servicos/demo-computeunit.yaml",
7795            "servicos/demo_computeunit.yaml",
7796        ] {
7797            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
7798            let err = c.validate_code_paths().unwrap_err();
7799            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
7800                panic!(
7801                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
7802                     got {err:?}"
7803                );
7804            };
7805            assert_eq!(slot, ":servicos");
7806            assert_eq!(path, PathBuf::from(relpath));
7807        }
7808    }
7809
7810    #[test]
7811    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
7812        // Case-sensitivity sweep — pins the strict lowercase
7813        // `.computeunit.yaml` contract. A case-folded shape that the
7814        // layout's existence check would (case-insensitively, on
7815        // case-insensitive volumes) match the on-disk file still
7816        // mismatches the canonical form the codec emits, breaking the
7817        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
7818        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
7819        // (64772a9) sweep on the sibling tatara-lisp-source axis.
7820        for relpath in [
7821            "servicos/demo.ComputeUnit.yaml",
7822            "servicos/demo.COMPUTEUNIT.yaml",
7823            "servicos/demo.computeunit.YAML",
7824            "servicos/demo.computeunit.Yaml",
7825            "servicos/demo.COMPUTEUNIT.YAML",
7826        ] {
7827            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
7828            let err = c.validate_code_paths().unwrap_err();
7829            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
7830                panic!(
7831                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
7832                     got {err:?}"
7833                );
7834            };
7835            assert_eq!(slot, ":servicos");
7836            assert_eq!(path, PathBuf::from(relpath));
7837        }
7838    }
7839
7840    #[test]
7841    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
7842        // Degenerate hidden-file shape: a file name exactly equal to the
7843        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
7844        // the structural "Servico declared with no identity" footgun.
7845        // The substrate identifies each ComputeUnit by the file-stem
7846        // segment that precedes `.computeunit.yaml` (the rendered
7847        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
7848        // the M3 `:contratos` membership lookup), so an empty stem
7849        // leaves the Servico unidentifiable. Pinned at the typed-axis
7850        // level so a future regression that drops the `name.len() >
7851        // SUFFIX.len()` bound at the predicate surfaces here, not
7852        // piecemeal as a `lareira-` chart-name collision at render time.
7853        for relpath in ["servicos/.computeunit.yaml"] {
7854            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
7855            let err = c.validate_code_paths().unwrap_err();
7856            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
7857                panic!(
7858                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
7859                     got {err:?}"
7860                );
7861            };
7862            assert_eq!(slot, ":servicos");
7863            assert_eq!(path, PathBuf::from(relpath));
7864        }
7865    }
7866
7867    #[test]
7868    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
7869        // Positive-control sweep through every canonical authoring shape
7870        // every in-tree fixture and the `Caixa::template` scaffold use.
7871        // Mirrors the peer
7872        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
7873        // and the lifted predicate's own
7874        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
7875        // render.rs.
7876        for relpath in [
7877            "servicos/demo.computeunit.yaml",
7878            "servicos/hello-rio.computeunit.yaml",
7879            "servicos/my-service.computeunit.yaml",
7880            "servicos/a.computeunit.yaml",
7881            "./servicos/demo.computeunit.yaml",
7882            "servicos/./demo.computeunit.yaml",
7883            "servicos/sub/nested.computeunit.yaml",
7884            "servicos/v0.1.computeunit.yaml",
7885        ] {
7886            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
7887            c.validate_code_paths()
7888                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
7889        }
7890    }
7891
7892    #[test]
7893    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
7894        // The file-type gate is per-slot — only `:servicos` carries the
7895        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
7896        // entry and an extensionless `:exe` entry are the canonical
7897        // shapes every in-tree fixture uses, and must continue to pass
7898        // validate. Peer of
7899        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
7900        // (64772a9) — together pin that the typed
7901        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
7902        // cross-axis leakage in either direction.
7903        let c = caixa_with_code_paths(
7904            vec!["lib/demo.lisp"],
7905            vec!["exe/demo", "exe/tool"],
7906            vec!["servicos/demo.computeunit.yaml"],
7907        );
7908        c.validate_code_paths().unwrap();
7909    }
7910
7911    #[test]
7912    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
7913        // Cross-arm precedence pin: a `:servicos` entry that is *both*
7914        // sandbox-escaping and wrong-extension surfaces the more
7915        // fundamental sandbox-shape diagnostic first (the
7916        // `.computeunit.yaml` remediation would be misleading when the
7917        // offending path can never resolve under the caixa root
7918        // anyway). Mirrors the peer
7919        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
7920        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
7921        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
7922        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
7923        // table establishes.
7924        //
7925        // Empty wins (the strictly-smaller-scope structural arm).
7926        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
7927        assert!(
7928            matches!(
7929                c.validate_code_paths().unwrap_err(),
7930                ManifestError::CodePathEmpty { slot: ":servicos" }
7931            ),
7932            "empty must win over non-computeunit-yaml-extension",
7933        );
7934        // Absolute wins (the path can't resolve under the caixa root).
7935        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
7936        let err = c.validate_code_paths().unwrap_err();
7937        let ManifestError::CodePathAbsolute { slot, .. } = err else {
7938            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
7939        };
7940        assert_eq!(slot, ":servicos");
7941        // ParentEscape wins (the path escapes the caixa root).
7942        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
7943        let err = c.validate_code_paths().unwrap_err();
7944        let ManifestError::CodePathParentEscape { slot, .. } = err else {
7945            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
7946        };
7947        assert_eq!(slot, ":servicos");
7948    }
7949
7950    #[test]
7951    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
7952        // Within-slot precedence pin: the per-entry file-type shape gate
7953        // fires before the cross-entry duplicate gate, so the narrower
7954        // structural defect dominates the uniqueness diagnostic. A
7955        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
7956        // `CodePathNonComputeUnitYamlExtension` on the first entry
7957        // rather than `CodePathDuplicate` on the pair — same posture
7958        // every per-entry shape-gate-precedes-duplicate cascade follows
7959        // on this surface, peer of the 64772a9 `:bibliotecas`
7960        // `("lib/x.txt" "lib/x.txt")` ordering.
7961        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
7962        let err = c.validate_code_paths().unwrap_err();
7963        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
7964            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
7965        };
7966        assert_eq!(slot, ":servicos");
7967        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
7968    }
7969
7970    #[test]
7971    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
7972     {
7973        // Diagnostic-shape pin (peer with
7974        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
7975        // on the sibling tatara-lisp-source axis): the file-type-arm
7976        // Display surfaces both the offending `:slot` tag, the
7977        // offending path verbatim, and the expected
7978        // `.computeunit.yaml` compound suffix named in the remediation
7979        // text, so a `feira lint` run can render the diagnostic without
7980        // re-parsing.
7981        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
7982        let rendered = c.validate_code_paths().unwrap_err().to_string();
7983        assert!(
7984            rendered.contains(":servicos"),
7985            "diagnostic must name the offending slot: {rendered}",
7986        );
7987        assert!(
7988            rendered.contains("servicos/demo.yaml"),
7989            "diagnostic must quote the offending path: {rendered}",
7990        );
7991        assert!(
7992            rendered.contains(".computeunit.yaml"),
7993            "diagnostic must name the expected compound suffix: {rendered}",
7994        );
7995    }
7996
7997    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
7998
7999    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
8000        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8001        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
8002        c
8003    }
8004
8005    #[test]
8006    fn validate_etiquetas_accepts_empty_list() {
8007        // The empty-list identity: every caixa with no declared tags
8008        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
8009        // so the gate is non-disruptive against every existing manifest.
8010        let c = caixa_with_etiquetas(vec![]);
8011        c.validate_etiquetas().unwrap();
8012    }
8013
8014    #[test]
8015    fn validate_etiquetas_accepts_canonical_forms() {
8016        // Positive control sweep: a canonical-shaped non-empty distinct
8017        // tag list passes, mirroring the example checkout-aplicacao
8018        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
8019        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
8020        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
8021        c.validate_etiquetas().unwrap();
8022    }
8023
8024    #[test]
8025    fn validate_etiquetas_rejects_empty_entry() {
8026        // Canonical paste-from-blank-doc footgun. Without the gate the
8027        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
8028        // no-op tag indexing nothing in the future caixa-registry.
8029        let c = caixa_with_etiquetas(vec![""]);
8030        let err = c.validate_etiquetas().unwrap_err();
8031        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8032    }
8033
8034    #[test]
8035    fn validate_etiquetas_rejects_duplicate_entry() {
8036        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
8037        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
8038        // collect at chart render — a "second wins / one silently
8039        // disappears" shape divergent from every peer typed-graph set
8040        // gate. The duplicate-arm names the offending tag verbatim.
8041        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8042        let err = c.validate_etiquetas().unwrap_err();
8043        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8044            panic!("expected EtiquetaDuplicate, got {err:?}");
8045        };
8046        assert_eq!(etiqueta, "demo");
8047    }
8048
8049    #[test]
8050    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
8051        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
8052        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
8053        // structural "this entry has no value" defect dominates the
8054        // cross-entry uniqueness diagnostic. Mirrors the peer
8055        // empty-before-duplicate cascades on `:caracteristicas`
8056        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8057        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
8058        // `MembroDuplicate`).
8059        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
8060        let err = c.validate_etiquetas().unwrap_err();
8061        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8062    }
8063
8064    #[test]
8065    fn validate_etiquetas_duplicate_reports_first_collision() {
8066        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8067        // duplicate (the lexicographically-earliest offending position
8068        // — the second `"a"` at index 2 collides with the first `"a"`
8069        // at index 0), not the later `"b"` collision at index 3,
8070        // peer with every other first-collision diagnostic posture on
8071        // this surface (`validate_load_singularity_reports_first_collision`,
8072        // `validate_cleanup_singularity_reports_first_collision`).
8073        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
8074        let err = c.validate_etiquetas().unwrap_err();
8075        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8076            panic!("expected EtiquetaDuplicate, got {err:?}");
8077        };
8078        assert_eq!(etiqueta, "a");
8079    }
8080
8081    #[test]
8082    fn validate_etiquetas_case_sensitive() {
8083        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
8084        // mirroring the peer `:membros :caixa` / `:children :caixa`
8085        // exact-string-match discipline. The shape gate this routine
8086        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
8087        // grammar) accepts mixed case — crates.io's keyword rule is
8088        // "case-insensitive" at the index layer but admits mixed case
8089        // at the entry layer (the canonical Helm chart `keywords:`
8090        // shape is lowercase by convention, but the grammar admits
8091        // uppercase). Case-sensitivity at the duplicate-set layer
8092        // remains structural — two distinct strings are two distinct
8093        // entries.
8094        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
8095        c.validate_etiquetas().unwrap();
8096    }
8097
8098    #[test]
8099    fn validate_etiquetas_diagnostic_carries_offending_tag() {
8100        // Diagnostic-shape pin (peer with
8101        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
8102        // the error's Display surfaces the offending tag verbatim, so a
8103        // `feira lint` run can render the diagnostic without re-parsing
8104        // and the author can grep their caixa.lisp for the offending
8105        // value.
8106        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8107        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8108        assert!(
8109            rendered.contains(":etiquetas"),
8110            "diagnostic must name the offending slot: {rendered}",
8111        );
8112        assert!(
8113            rendered.contains("demo"),
8114            "diagnostic must quote the offending tag: {rendered}",
8115        );
8116    }
8117
8118    #[test]
8119    fn validate_etiquetas_rejects_leading_whitespace_entry() {
8120        // Canonical paste-from-aligned-doc footgun. Without the shape
8121        // gate `" mesh"` silently passed validate and landed as a
8122        // YAML plain-style scalar with leading whitespace in the
8123        // rendered Chart.yaml `keywords:` array — every YAML 1.2
8124        // dumper trims leading whitespace from plain-style scalars,
8125        // so the authored space round-tripped inconsistently back
8126        // through `caixa.lisp`. Mirrors the peer
8127        // `validate_autores_rejects_leading_whitespace_entry`.
8128        let c = caixa_with_etiquetas(vec![" mesh"]);
8129        let err = c.validate_etiquetas().unwrap_err();
8130        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8131            panic!("expected EtiquetaInvalid, got {err:?}");
8132        };
8133        assert_eq!(etiqueta, " mesh");
8134        assert!(reason.contains("whitespace"), "got: {reason}");
8135    }
8136
8137    #[test]
8138    fn validate_etiquetas_rejects_embedded_newline_entry() {
8139        // Canonical paste-from-multiline-doc footgun — the author
8140        // pasted a multi-tag block into one `:etiquetas` entry
8141        // instead of splitting into one entry per tag. Without the
8142        // shape gate `"mesh\nhttp"` silently passed validate and
8143        // landed as a YAML-illegal multi-line scalar in the rendered
8144        // Chart.yaml `keywords:` array.
8145        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8146        let err = c.validate_etiquetas().unwrap_err();
8147        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8148            panic!("expected EtiquetaInvalid, got {err:?}");
8149        };
8150        assert_eq!(etiqueta, "mesh\nhttp");
8151        assert!(reason.contains("newline"), "got: {reason}");
8152    }
8153
8154    #[test]
8155    fn validate_etiquetas_rejects_embedded_comma_entry() {
8156        // Canonical CSV-list-separator-confusion footgun: the author
8157        // confused the CSV-style separator convention with the
8158        // `:etiquetas` list grammar. Without the shape gate
8159        // `"mesh,http,grpc"` silently passed validate and landed as a
8160        // single malformed search tag in the rendered Chart.yaml
8161        // `keywords:` array — Artifact Hub's keyword index would
8162        // either silently drop the tag or index it as
8163        // `mesh,http,grpc` instead of three separate tags.
8164        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
8165        let err = c.validate_etiquetas().unwrap_err();
8166        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8167            panic!("expected EtiquetaInvalid, got {err:?}");
8168        };
8169        assert_eq!(etiqueta, "mesh,http,grpc");
8170        assert!(reason.contains('`'), "got: {reason}");
8171        assert!(reason.contains(','), "got: {reason}");
8172    }
8173
8174    #[test]
8175    fn validate_etiquetas_rejects_embedded_slash_entry() {
8176        // Canonical path-separator-confusion footgun: the author
8177        // confused namespace-path notation with the keyword grammar.
8178        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
8179        let err = c.validate_etiquetas().unwrap_err();
8180        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8181            panic!("expected EtiquetaInvalid, got {err:?}");
8182        };
8183        assert_eq!(etiqueta, "caixa/servico");
8184        assert!(reason.contains('/'), "got: {reason}");
8185    }
8186
8187    #[test]
8188    fn validate_etiquetas_rejects_leading_digit_entry() {
8189        // Canonical paste-from-numbered-list footgun: the author
8190        // copied `1. mesh` from a numbered doc and the `1` leaked
8191        // into the tag.
8192        let c = caixa_with_etiquetas(vec!["1mesh"]);
8193        let err = c.validate_etiquetas().unwrap_err();
8194        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8195            panic!("expected EtiquetaInvalid, got {err:?}");
8196        };
8197        assert_eq!(etiqueta, "1mesh");
8198        assert!(reason.contains("digit"), "got: {reason}");
8199    }
8200
8201    #[test]
8202    fn validate_etiquetas_rejects_leading_hyphen_entry() {
8203        // Canonical kebab-leak footgun.
8204        let c = caixa_with_etiquetas(vec!["-foo"]);
8205        let err = c.validate_etiquetas().unwrap_err();
8206        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8207            panic!("expected EtiquetaInvalid, got {err:?}");
8208        };
8209        assert_eq!(etiqueta, "-foo");
8210        assert!(reason.contains('-'), "got: {reason}");
8211    }
8212
8213    #[test]
8214    fn validate_etiquetas_rejects_non_ascii_entry() {
8215        // Canonical paste-from-Unicode-doc footgun. Every legitimate
8216        // search tag is strict ASCII; raw non-ASCII silently
8217        // round-trips inconsistently across NFC/NFD normalization on
8218        // APFS / case-folding filesystems and breaks the Artifact Hub
8219        // keyword search index lookup.
8220        let c = caixa_with_etiquetas(vec!["café"]);
8221        let err = c.validate_etiquetas().unwrap_err();
8222        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8223            panic!("expected EtiquetaInvalid, got {err:?}");
8224        };
8225        assert_eq!(etiqueta, "café");
8226        assert!(reason.contains("non-ASCII"), "got: {reason}");
8227    }
8228
8229    #[test]
8230    fn validate_etiquetas_rejects_period_entry() {
8231        // Canonical namespace-confusion / version-suffix footgun
8232        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
8233        // excludes `.` from the continuation set even though the
8234        // sibling `:caracteristicas` axis (Cargo's feature-name
8235        // grammar) admits it. Tighter than the sibling axis, peer
8236        // with Cargo's own crates.io keyword shape.
8237        let c = caixa_with_etiquetas(vec!["http.1"]);
8238        let err = c.validate_etiquetas().unwrap_err();
8239        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8240            panic!("expected EtiquetaInvalid, got {err:?}");
8241        };
8242        assert_eq!(etiqueta, "http.1");
8243        assert!(reason.contains('.'), "got: {reason}");
8244    }
8245
8246    #[test]
8247    fn validate_etiquetas_empty_takes_precedence_over_shape() {
8248        // Per-entry empty-first cascade pin: an entry that is both
8249        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
8250        // narrower "this entry has no value" structural defect
8251        // dominates the broader shape-predicate diagnostic). The
8252        // empty arm fires before the shape predicate is consulted,
8253        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
8254        // cascade established on the sibling universal-axis Vec<String>
8255        // surface.
8256        let c = caixa_with_etiquetas(vec![""]);
8257        let err = c.validate_etiquetas().unwrap_err();
8258        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8259    }
8260
8261    #[test]
8262    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
8263        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
8264        // entry that is malformed surfaces `EtiquetaInvalid` even when
8265        // a later entry would have collided on duplicate. The
8266        // per-entry shape arm fires inside the same loop iteration as
8267        // the empty arm, before the seen-set insert at end-of-iteration
8268        // — structural per-entry defects dominate the cross-entry
8269        // uniqueness diagnostic. Mirrors the peer
8270        // `validate_autores_shape_takes_precedence_over_duplicate`.
8271        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
8272        let err = c.validate_etiquetas().unwrap_err();
8273        assert!(
8274            matches!(err, ManifestError::EtiquetaInvalid { .. }),
8275            "got {err:?}",
8276        );
8277    }
8278
8279    #[test]
8280    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
8281        // Diagnostic-shape pin on the new shape arm (peer with
8282        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
8283        // the rendered Display surfaces both the offending slot name
8284        // and the offending value verbatim, so a `feira lint` run
8285        // points the author at the exact `:etiquetas` entry to fix.
8286        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8287        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8288        assert!(
8289            rendered.contains(":etiquetas"),
8290            "diagnostic must name the offending slot: {rendered}",
8291        );
8292        assert!(
8293            rendered.contains("mesh\\nhttp"),
8294            "diagnostic must quote the offending value (debug-escaped): {rendered}",
8295        );
8296    }
8297
8298    #[test]
8299    fn validate_etiquetas_rejects_at_21_byte_boundary() {
8300        // The 20-byte cap pin — boundary-exceeding case rejected,
8301        // boundary-accepting case passes. Mirrors the peer
8302        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
8303        // side pin, surfaced at the per-axis caller so the cap
8304        // propagates through validate end-to-end. Constructed as a
8305        // single all-`a` token so only the cap arm fires.
8306        let max_ok = "a".repeat(20);
8307        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
8308        c.validate_etiquetas().unwrap();
8309        let too_long = "a".repeat(21);
8310        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
8311        let err = c.validate_etiquetas().unwrap_err();
8312        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
8313            panic!("expected EtiquetaInvalid, got {err:?}");
8314        };
8315        assert!(reason.contains("20"), "got: {reason}");
8316        assert!(reason.contains("21"), "got: {reason}");
8317    }
8318
8319    #[test]
8320    fn validate_etiquetas_accepts_canonical_shaped_forms() {
8321        // Positive control sweep: every canonical-shaped tag from the
8322        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
8323        // example fixtures plus the substrate-fixed tags caixa-helm
8324        // unions in at chart render. Drift between this list and the
8325        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
8326        // sweep surfaces here — one source of truth for the rule.
8327        let c = caixa_with_etiquetas(vec![
8328            "example",
8329            "aplicacao",
8330            "mesh",
8331            "ecommerce",
8332            "demo",
8333            "infrastructure",
8334            "aws",
8335            "akeyless",
8336            "pangea-native",
8337            "hello-world",
8338            "wasm",
8339            "rust",
8340            "tatara-lisp",
8341            "caixa-servico",
8342            "lareira",
8343        ]);
8344        c.validate_etiquetas().unwrap();
8345    }
8346
8347    // ── validate_autores — universal-axis maintainer shape ────────────
8348
8349    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
8350        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8351        c.autores = autores.into_iter().map(String::from).collect();
8352        c
8353    }
8354
8355    #[test]
8356    fn validate_autores_accepts_empty_list() {
8357        // The empty-list identity: `Caixa::template` emits `:autores ()`,
8358        // so the gate is non-disruptive against every existing manifest.
8359        let c = caixa_with_autores(vec![]);
8360        c.validate_autores().unwrap();
8361    }
8362
8363    #[test]
8364    fn validate_autores_accepts_canonical_forms() {
8365        // Positive control sweep: every canonical-shaped non-empty
8366        // distinct maintainer list passes — the hello-rio / checkout-
8367        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
8368        // multi-author shape downstream packaging surfaces emit.
8369        let c = caixa_with_autores(vec!["pleme-io"]);
8370        c.validate_autores().unwrap();
8371        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
8372        c.validate_autores().unwrap();
8373    }
8374
8375    #[test]
8376    fn validate_autores_rejects_empty_entry() {
8377        // Canonical paste-from-blank-doc footgun. Without the gate the
8378        // empty entry rendered as `maintainers: [{name: "", email: null}]`
8379        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
8380        // to.
8381        let c = caixa_with_autores(vec![""]);
8382        let err = c.validate_autores().unwrap_err();
8383        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8384    }
8385
8386    #[test]
8387    fn validate_autores_rejects_duplicate_entry() {
8388        // Canonical copy-paste-the-wrong-author footgun. Unlike the
8389        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
8390        // dedups the rendered `keywords:` array), the `maintainers:`
8391        // rendering has *no* dedup — duplicates stack verbatim. The
8392        // duplicate-arm names the offending author verbatim.
8393        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
8394        let err = c.validate_autores().unwrap_err();
8395        let ManifestError::AutorDuplicate { autor } = err else {
8396            panic!("expected AutorDuplicate, got {err:?}");
8397        };
8398        assert_eq!(autor, "pleme-io");
8399    }
8400
8401    #[test]
8402    fn validate_autores_empty_takes_precedence_over_duplicate() {
8403        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
8404        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
8405        // "this entry has no value" defect dominates the cross-entry
8406        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
8407        // cascades on `:etiquetas` (`EtiquetaEmpty` before
8408        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
8409        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8410        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
8411        // `MembroDuplicate`).
8412        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
8413        let err = c.validate_autores().unwrap_err();
8414        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8415    }
8416
8417    #[test]
8418    fn validate_autores_duplicate_reports_first_collision() {
8419        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8420        // duplicate (the lexicographically-earliest offending position
8421        // — the second `"a"` at index 2 collides with the first `"a"`
8422        // at index 0), not the later `"b"` collision at index 3,
8423        // peer with every other first-collision diagnostic posture on
8424        // this surface.
8425        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
8426        let err = c.validate_autores().unwrap_err();
8427        let ManifestError::AutorDuplicate { autor } = err else {
8428            panic!("expected AutorDuplicate, got {err:?}");
8429        };
8430        assert_eq!(autor, "a");
8431    }
8432
8433    #[test]
8434    fn validate_autores_case_sensitive() {
8435        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
8436        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
8437        // / `:children :caixa` exact-string-match discipline.
8438        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
8439        c.validate_autores().unwrap();
8440    }
8441
8442    #[test]
8443    fn validate_autores_diagnostic_carries_offending_author() {
8444        // Diagnostic-shape pin (peer with
8445        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
8446        // error's Display surfaces the offending author verbatim, so a
8447        // `feira lint` run can render the diagnostic without re-parsing
8448        // and the author can grep their caixa.lisp for the offending
8449        // value.
8450        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
8451        let rendered = c.validate_autores().unwrap_err().to_string();
8452        assert!(
8453            rendered.contains(":autores"),
8454            "diagnostic must name the offending slot: {rendered}",
8455        );
8456        assert!(
8457            rendered.contains("pleme-io"),
8458            "diagnostic must quote the offending author: {rendered}",
8459        );
8460    }
8461
8462    #[test]
8463    fn validate_autores_rejects_leading_whitespace_entry() {
8464        // Canonical paste-from-aligned-doc footgun. Without the shape
8465        // gate `" pleme-io"` silently passed validate and landed as a
8466        // YAML plain-style scalar with leading whitespace in the
8467        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
8468        // dumper trims leading whitespace from plain-style scalars, so
8469        // the authored space round-tripped inconsistently back through
8470        // `caixa.lisp`. Mirrors the peer
8471        // `validate_descricao_rejects_leading_whitespace`.
8472        let c = caixa_with_autores(vec![" pleme-io"]);
8473        let err = c.validate_autores().unwrap_err();
8474        let ManifestError::AutorInvalid { autor, reason } = err else {
8475            panic!("expected AutorInvalid, got {err:?}");
8476        };
8477        assert_eq!(autor, " pleme-io");
8478        assert!(reason.contains("whitespace"), "got: {reason}");
8479    }
8480
8481    #[test]
8482    fn validate_autores_rejects_trailing_whitespace_entry() {
8483        // Canonical paste-from-doc footgun.
8484        let c = caixa_with_autores(vec!["pleme-io "]);
8485        let err = c.validate_autores().unwrap_err();
8486        let ManifestError::AutorInvalid { autor, reason } = err else {
8487            panic!("expected AutorInvalid, got {err:?}");
8488        };
8489        assert_eq!(autor, "pleme-io ");
8490        assert!(reason.contains("whitespace"), "got: {reason}");
8491    }
8492
8493    #[test]
8494    fn validate_autores_rejects_embedded_newline_entry() {
8495        // Canonical paste-from-multiline-doc footgun — the author
8496        // pasted a multi-line block of author records into one
8497        // `:autores` entry instead of splitting into one entry per
8498        // author. Without the shape gate `"alice\nbob"` silently
8499        // passed validate and landed as a YAML-illegal multi-line
8500        // scalar in the rendered Chart.yaml `maintainers:` array.
8501        let c = caixa_with_autores(vec!["alice\nbob"]);
8502        let err = c.validate_autores().unwrap_err();
8503        let ManifestError::AutorInvalid { autor, reason } = err else {
8504            panic!("expected AutorInvalid, got {err:?}");
8505        };
8506        assert_eq!(autor, "alice\nbob");
8507        assert!(reason.contains("newline"), "got: {reason}");
8508    }
8509
8510    #[test]
8511    fn validate_autores_rejects_embedded_carriage_return_entry() {
8512        // Canonical paste-from-Windows-CRLF-doc footgun.
8513        let c = caixa_with_autores(vec!["alice\rbob"]);
8514        let err = c.validate_autores().unwrap_err();
8515        let ManifestError::AutorInvalid { autor, reason } = err else {
8516            panic!("expected AutorInvalid, got {err:?}");
8517        };
8518        assert_eq!(autor, "alice\rbob");
8519        assert!(reason.contains("carriage return"), "got: {reason}");
8520    }
8521
8522    #[test]
8523    fn validate_autores_rejects_embedded_tab_entry() {
8524        // Canonical tab-from-aligned-doc footgun.
8525        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
8526        let err = c.validate_autores().unwrap_err();
8527        let ManifestError::AutorInvalid { autor, reason } = err else {
8528            panic!("expected AutorInvalid, got {err:?}");
8529        };
8530        assert_eq!(autor, "Pleme\tContributors");
8531        assert!(reason.contains("tab"), "got: {reason}");
8532    }
8533
8534    #[test]
8535    fn validate_autores_rejects_embedded_control_bytes_entry() {
8536        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
8537        // surface the same control-byte arm.
8538        for entry in [
8539            "alice\x00bob",
8540            "alice\x07bob",
8541            "alice\x1bbob",
8542            "alice\x7fbob",
8543        ] {
8544            let c = caixa_with_autores(vec![entry]);
8545            let err = c.validate_autores().unwrap_err();
8546            let ManifestError::AutorInvalid { autor, reason } = err else {
8547                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
8548            };
8549            assert_eq!(autor, entry);
8550            assert!(
8551                reason.contains("control character"),
8552                "{entry:?} reason: {reason}",
8553            );
8554        }
8555    }
8556
8557    #[test]
8558    fn validate_autores_accepts_unicode_entry() {
8559        // Unicode positive control: realistic maintainer names carry
8560        // Unicode (`François`, `日本語`, `naïve`). The predicate must
8561        // round-trip Unicode losslessly, peer with the
8562        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
8563        // sweep.
8564        let c = caixa_with_autores(vec![
8565            "François Dupont",
8566            "日本語の名前",
8567            "naïve <naive@example.com>",
8568        ]);
8569        c.validate_autores().unwrap();
8570    }
8571
8572    #[test]
8573    fn validate_autores_empty_takes_precedence_over_shape() {
8574        // Per-entry empty-first cascade pin: an entry that is both
8575        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
8576        // "this entry has no value" structural defect dominates the
8577        // broader shape-predicate diagnostic). The empty arm fires
8578        // before the shape predicate is consulted, mirroring the peer
8579        // `validate_repositorio_empty_takes_precedence_over_shape`
8580        // cascade on the universal `Option<String>` siblings — and now
8581        // established on the Vec<String> per-entry surface.
8582        let c = caixa_with_autores(vec![""]);
8583        let err = c.validate_autores().unwrap_err();
8584        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8585    }
8586
8587    #[test]
8588    fn validate_autores_shape_takes_precedence_over_duplicate() {
8589        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
8590        // entry that is malformed surfaces `AutorInvalid` even when a
8591        // later entry would have collided on duplicate. The per-entry
8592        // shape arm fires inside the same loop iteration as the empty
8593        // arm, before the seen-set insert at end-of-iteration —
8594        // structural per-entry defects dominate the cross-entry
8595        // uniqueness diagnostic.
8596        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
8597        let err = c.validate_autores().unwrap_err();
8598        assert!(
8599            matches!(err, ManifestError::AutorInvalid { .. }),
8600            "got {err:?}",
8601        );
8602    }
8603
8604    #[test]
8605    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
8606        // Diagnostic-shape pin on the new shape arm (peer with
8607        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
8608        // the rendered Display surfaces both the offending slot name
8609        // and the offending value verbatim, so a `feira lint` run
8610        // points the author at the exact `:autores` entry to fix.
8611        let c = caixa_with_autores(vec!["alice\nbob"]);
8612        let rendered = c.validate_autores().unwrap_err().to_string();
8613        assert!(
8614            rendered.contains(":autores"),
8615            "diagnostic must name the offending slot: {rendered}",
8616        );
8617        assert!(
8618            rendered.contains("alice\\nbob"),
8619            "diagnostic must quote the offending value (debug-escaped): {rendered}",
8620        );
8621    }
8622
8623    #[test]
8624    fn validate_autores_rejects_at_129_byte_boundary() {
8625        // The 128-byte cap pin — boundary-exceeding case rejected,
8626        // boundary-accepting case passes. Mirrors the peer
8627        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
8628        // substrate-side pin, surfaced at the per-axis caller so the
8629        // cap propagates through validate end-to-end. Constructed as
8630        // a single all-`a` token so only the cap arm fires.
8631        let max_ok = "a".repeat(128);
8632        let c = caixa_with_autores(vec![max_ok.as_str()]);
8633        c.validate_autores().unwrap();
8634        let too_long = "a".repeat(129);
8635        let c = caixa_with_autores(vec![too_long.as_str()]);
8636        let err = c.validate_autores().unwrap_err();
8637        let ManifestError::AutorInvalid { reason, .. } = err else {
8638            panic!("expected AutorInvalid, got {err:?}");
8639        };
8640        assert!(reason.contains("128"), "got: {reason}");
8641        assert!(reason.contains("129"), "got: {reason}");
8642    }
8643
8644    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
8645
8646    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
8647        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8648        c.repositorio = repositorio.map(String::from);
8649        c
8650    }
8651
8652    #[test]
8653    fn validate_repositorio_accepts_none() {
8654        // The omit-the-slot identity: `:repositorio` is optional. The
8655        // gate is a no-op when the author didn't declare a value —
8656        // every caixa without a `:repositorio` line trivially passes,
8657        // and the substrate-side renderers fall back to their
8658        // documented placeholder (`caixa-helm`'s `home: None`,
8659        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
8660        // URL). Mirrors the peer `validate_restart_window_accepts_none`
8661        // posture on the other `Option<String>` Caixa slot.
8662        let c = caixa_with_repositorio(None);
8663        c.validate_repositorio().unwrap();
8664    }
8665
8666    #[test]
8667    fn validate_repositorio_accepts_canonical_forms() {
8668        // Positive control sweep across every documented `:repositorio`
8669        // authoring shape — the same union the shared
8670        // `crate::render::is_git_repo_url` predicate accepts and the
8671        // peer `:deps :fonte :repo` axis already routes through.
8672        // Covers the `github:` shorthand (the canonical pleme-io
8673        // convention used in the `:repositorio` field of every
8674        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
8675        // `examples/`), the `https://…` URL the README quickstart uses,
8676        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
8677        // `file://` URL schemes the shared predicate documents.
8678        for repo in [
8679            "github:pleme-io/hello-rio",
8680            "github:pleme-io/checkout",
8681            "https://github.com/pleme-io/hello-rio",
8682            "ssh://git@github.com/pleme-io/hello-rio.git",
8683            "git://github.com/pleme-io/hello-rio.git",
8684            "git@github.com:pleme-io/hello-rio.git",
8685            "file:///srv/pleme/hello-rio",
8686        ] {
8687            let c = caixa_with_repositorio(Some(repo));
8688            c.validate_repositorio()
8689                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
8690        }
8691    }
8692
8693    #[test]
8694    fn validate_repositorio_rejects_empty_some() {
8695        // Canonical paste-from-blank-doc footgun. The narrower
8696        // [`ManifestError::RepositorioEmpty`] arm fires before the
8697        // shape predicate is consulted, mirroring the empty-first
8698        // cascade every peer per-axis identity gate uses
8699        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
8700        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
8701        // the empty `Some("")` silently passed the renderer's
8702        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
8703        // on `None`) and landed as `home: ""` in `Chart.yaml` /
8704        // `url: ""` in the FluxCD `GitRepository`.
8705        let c = caixa_with_repositorio(Some(""));
8706        let err = c.validate_repositorio().unwrap_err();
8707        assert!(
8708            matches!(err, ManifestError::RepositorioEmpty),
8709            "got {err:?}",
8710        );
8711    }
8712
8713    #[test]
8714    fn validate_repositorio_rejects_whitespace() {
8715        // Paste-from-doc whitespace footgun. The shared
8716        // `is_git_repo_url` predicate refuses any whitespace byte; a
8717        // trailing space in a `:repositorio` value silently broke
8718        // `git clone '<value> '` at clone time. The diagnostic names
8719        // the offending value verbatim.
8720        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
8721        let err = c.validate_repositorio().unwrap_err();
8722        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
8723            panic!("expected RepositorioInvalid, got {err:?}");
8724        };
8725        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
8726    }
8727
8728    #[test]
8729    fn validate_repositorio_rejects_control_char() {
8730        // Paste-from-multiline-doc CRLF footgun — control characters
8731        // at the URL boundary are a class of subprocess-arg injection
8732        // and break git's URL parser at every porcelain entry point.
8733        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
8734        let err = c.validate_repositorio().unwrap_err();
8735        assert!(
8736            matches!(err, ManifestError::RepositorioInvalid { .. }),
8737            "got {err:?}",
8738        );
8739    }
8740
8741    #[test]
8742    fn validate_repositorio_rejects_leading_dash() {
8743        // Canonical CLI-argument-injection footgun: `git clone <repo>`
8744        // interprets a leading `-` as a CLI flag, so a
8745        // `-upload-pack=…` value escapes the subprocess argument
8746        // boundary. The shared predicate refuses every leading-`-`
8747        // shape at validate time.
8748        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
8749        let err = c.validate_repositorio().unwrap_err();
8750        assert!(
8751            matches!(err, ManifestError::RepositorioInvalid { .. }),
8752            "got {err:?}",
8753        );
8754    }
8755
8756    #[test]
8757    fn validate_repositorio_rejects_missing_colon_separator() {
8758        // The bare `org/repo` ambiguity footgun — `git clone` reads
8759        // a no-`:` form as a relative filesystem path rather than the
8760        // GitHub-shorthand expansion the author probably intended.
8761        // The shared predicate refuses every shape without a `:`
8762        // separator.
8763        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
8764        let err = c.validate_repositorio().unwrap_err();
8765        assert!(
8766            matches!(err, ManifestError::RepositorioInvalid { .. }),
8767            "got {err:?}",
8768        );
8769    }
8770
8771    #[test]
8772    fn validate_repositorio_rejects_fragment_anchor() {
8773        // Paste-from-browser-address-bar footgun on the
8774        // `:repositorio` axis — an author copies a GitHub permalink
8775        // to a README section / line-permalink and forgets to trim
8776        // the `#fragment` tail. The shared `is_git_repo_url`
8777        // predicate refuses the byte at the URL-grammar layer
8778        // (libcurl strips the fragment before opening the
8779        // transport, so the byte rides verbatim into the rendered
8780        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
8781        // fields but is silently dropped on the wire — two
8782        // manifest variants whose values differ only in their
8783        // fragment anchor lock to two distinct rendered artifacts
8784        // for the byte-identical clone, defeating the THEORY.md
8785        // §V.2 render-determinism contract on the `:repositorio`
8786        // axis the peer `:fonte :repo` axis already closes).
8787        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
8788        let err = c.validate_repositorio().unwrap_err();
8789        let ManifestError::RepositorioInvalid {
8790            repositorio,
8791            reason,
8792        } = err
8793        else {
8794            panic!("expected RepositorioInvalid, got {err:?}");
8795        };
8796        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
8797        assert!(
8798            reason.contains("must not contain `#`"),
8799            "reason must surface the fragment-`#` arm, got {reason:?}"
8800        );
8801    }
8802
8803    #[test]
8804    fn validate_repositorio_rejects_query_string() {
8805        // Paste-from-browser-address-bar footgun on the
8806        // `:repositorio` axis (peer with the a68f818 fragment-`#`
8807        // arm on the same axis). An author copies a GitHub tab
8808        // deep-link out of the address bar and forgets to trim
8809        // the `?tab=…` query tail. The shared `is_git_repo_url`
8810        // predicate refuses the byte at the URL-grammar layer
8811        // (GitHub / GitLab / Bitbucket silently ignore the
8812        // `?query` tail and serve the same repo regardless, so
8813        // the byte rides verbatim into the rendered `Chart.yaml`
8814        // `home:` and FluxCD `GitRepository` `url:` fields but
8815        // is silently masked at the wire — two manifest variants
8816        // whose values differ only in their query tail lock to
8817        // two distinct rendered artifacts for the byte-identical
8818        // clone, defeating the THEORY.md §V.2 render-determinism
8819        // contract on the `:repositorio` axis the peer `:fonte
8820        // :repo` axis already closes).
8821        let c = caixa_with_repositorio(Some(
8822            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
8823        ));
8824        let err = c.validate_repositorio().unwrap_err();
8825        let ManifestError::RepositorioInvalid {
8826            repositorio,
8827            reason,
8828        } = err
8829        else {
8830            panic!("expected RepositorioInvalid, got {err:?}");
8831        };
8832        assert_eq!(
8833            repositorio,
8834            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
8835        );
8836        assert!(
8837            reason.contains("must not contain `?`"),
8838            "reason must surface the query-`?` arm, got {reason:?}"
8839        );
8840    }
8841
8842    #[test]
8843    fn validate_repositorio_rejects_embedded_backslash() {
8844        // Windows-file-path-confusion footgun on the `:repositorio`
8845        // axis (peer with the prior fragment-`#` / query-`?` arms on
8846        // the same axis, and peer with the new dep-level `:fonte :repo`
8847        // backslash arm on the URL-grammar trajectory). An author
8848        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
8849        // hello-rio` into the `:repositorio` slot, expecting the
8850        // `lareira-<nome>` chart's `home:` field and the FluxCD
8851        // `GitRepository` `url:` field to render the canonical local
8852        // file-URI. The shared `is_git_repo_url` predicate refuses
8853        // the byte at the URL-grammar layer (libcurl silently
8854        // translates `\` → `/` on some platforms and refuses it on
8855        // others, so the byte rides verbatim into the rendered
8856        // artifacts but is silently rewritten or rejected at the wire
8857        // — two manifest variants whose values differ only in
8858        // backslash-vs-forward-slash lock to two distinct rendered
8859        // artifacts for the byte-identical clone, defeating the
8860        // THEORY.md §V.2 render-determinism contract on the
8861        // `:repositorio` axis the peer `:fonte :repo` axis already
8862        // closes).
8863        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
8864        let err = c.validate_repositorio().unwrap_err();
8865        let ManifestError::RepositorioInvalid {
8866            repositorio,
8867            reason,
8868        } = err
8869        else {
8870            panic!("expected RepositorioInvalid, got {err:?}");
8871        };
8872        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
8873        assert!(
8874            reason.contains("must not contain `\\`"),
8875            "reason must surface the backslash-`\\` arm, got {reason:?}"
8876        );
8877    }
8878
8879    #[test]
8880    fn validate_repositorio_rejects_uri_template_placeholder() {
8881        // URI Template (RFC 6570) placeholder footgun on the
8882        // `:repositorio` axis (peer with the prior fragment-`#` /
8883        // query-`?` / backslash-`\` arms on the same axis, and peer
8884        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
8885        // URL-grammar trajectory). An author pastes a quick-start
8886        // README snippet / OpenAPI `servers:` URL / Helm chart
8887        // `home:` template carrying unresolved `{org}` / `{repo}`
8888        // placeholders into the `:repositorio` slot, expecting the
8889        // substrate to resolve the placeholder downstream. The
8890        // shared `is_git_repo_url` predicate refuses the byte at the
8891        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
8892        // `%7B` / `%7D` on the wire, so the byte round-trips
8893        // inconsistently between the rendered `Chart.yaml home:` /
8894        // FluxCD `GitRepository url:` and the resolver's `git clone`
8895        // invocation, defeating the THEORY.md §V.2 render-
8896        // determinism contract on the `:repositorio` axis the peer
8897        // `:fonte :repo` axis already closes; every git porcelain
8898        // entry-point additionally fetches a nonexistent literal-
8899        // `{placeholder}`-named path far from the source caixa.lisp).
8900        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
8901        let err = c.validate_repositorio().unwrap_err();
8902        let ManifestError::RepositorioInvalid {
8903            repositorio,
8904            reason,
8905        } = err
8906        else {
8907            panic!("expected RepositorioInvalid, got {err:?}");
8908        };
8909        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
8910        assert!(
8911            reason.contains("must not contain `{`"),
8912            "reason must surface the open-brace `{{` arm, got {reason:?}"
8913        );
8914        assert!(
8915            reason.contains("URI Template") || reason.contains("RFC 6570"),
8916            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
8917        );
8918    }
8919
8920    #[test]
8921    fn validate_repositorio_empty_takes_precedence_over_shape() {
8922        // Empty-first cascade pin: the empty `Some("")` surfaces the
8923        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
8924        // `RepositorioInvalid`, mirroring the peer
8925        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
8926        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
8927        // `is_git_repo_url` predicate also rejects the empty input
8928        // (defensively, with its own `"must not be empty"` reason),
8929        // but the manifest-layer empty arm runs first to surface the
8930        // narrower diagnostic verbatim.
8931        let c = caixa_with_repositorio(Some(""));
8932        let err = c.validate_repositorio().unwrap_err();
8933        assert!(
8934            matches!(err, ManifestError::RepositorioEmpty),
8935            "got {err:?}",
8936        );
8937    }
8938
8939    #[test]
8940    fn validate_repositorio_diagnostic_carries_offending_value() {
8941        // Diagnostic-shape pin (peer with
8942        // `validate_autores_diagnostic_carries_offending_author`): the
8943        // error's Display surfaces the offending value + slot name
8944        // verbatim, so a `feira lint` run can render the diagnostic
8945        // without re-parsing and the author can grep their caixa.lisp
8946        // for the offending `:repositorio` value.
8947        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
8948        let rendered = c.validate_repositorio().unwrap_err().to_string();
8949        assert!(
8950            rendered.contains(":repositorio"),
8951            "diagnostic must name the offending slot: {rendered}",
8952        );
8953        assert!(
8954            rendered.contains("pleme-io/hello-rio"),
8955            "diagnostic must quote the offending value: {rendered}",
8956        );
8957    }
8958
8959    // ── validate_descricao — universal-axis Chart.yaml description shape ──
8960
8961    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
8962        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8963        c.descricao = descricao.map(String::from);
8964        c
8965    }
8966
8967    #[test]
8968    fn validate_descricao_accepts_none() {
8969        // The omit-the-slot identity: `:descricao` is optional. The
8970        // gate is a no-op when the author didn't declare a value —
8971        // every caixa without a `:descricao` line trivially passes,
8972        // and the substrate-side renderers fall back to their
8973        // documented `caixa.nome`-derived placeholder. Mirrors the
8974        // peer `validate_repositorio_accepts_none` posture on the
8975        // sibling `Option<String>` Caixa slot.
8976        let c = caixa_with_descricao(None);
8977        c.validate_descricao().unwrap();
8978    }
8979
8980    #[test]
8981    fn validate_descricao_accepts_canonical_summary() {
8982        // Positive control: the canonical pleme-io descricao shape —
8983        // a short free-form prose summary — passes the gate. Covers
8984        // the fixture shapes the `caixa-helm` / `caixa-flux` /
8985        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
8986        // wasip2 caixa Servico."`, `"Checkout flow."`).
8987        for desc in [
8988            "Canonical Rust→wasm32-wasip2 caixa Servico.",
8989            "Checkout flow.",
8990            "AWS provider caixa for tatara-lisp",
8991            "FIXME — describe this caixa",
8992            "x",
8993        ] {
8994            let c = caixa_with_descricao(Some(desc));
8995            c.validate_descricao()
8996                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
8997        }
8998    }
8999
9000    #[test]
9001    fn validate_descricao_rejects_empty_some() {
9002        // Canonical paste-from-blank-doc footgun. Without this gate
9003        // the empty `Some("")` silently passed the renderer's
9004        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9005        // on `None`) and landed as `description: ""` in `Chart.yaml`
9006        // and a blank `README.md` header. Mirrors the peer
9007        // [`ManifestError::RepositorioEmpty`] empty-arm on the
9008        // sibling `Option<String>` Caixa slot.
9009        let c = caixa_with_descricao(Some(""));
9010        let err = c.validate_descricao().unwrap_err();
9011        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9012    }
9013
9014    #[test]
9015    fn validate_descricao_rejects_leading_whitespace() {
9016        // Paste-from-aligned-doc footgun: a leading ASCII space the
9017        // bare empty-arm gate accepted, the shape predicate now
9018        // refuses. The diagnostic carries the offending value
9019        // verbatim (with the leading space preserved) so the author
9020        // can grep their caixa.lisp for the exact `:descricao` line
9021        // and fix the round-trip-inconsistent leading whitespace.
9022        // Mirrors the peer
9023        // `validate_licenca_rejects_leading_whitespace` arm on the
9024        // sibling `:licenca` axis.
9025        let c = caixa_with_descricao(Some(" Checkout flow."));
9026        let err = c.validate_descricao().unwrap_err();
9027        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9028            panic!("expected DescricaoInvalid, got {err:?}");
9029        };
9030        assert_eq!(descricao, " Checkout flow.");
9031        assert!(reason.contains("whitespace"), "got: {reason:?}");
9032    }
9033
9034    #[test]
9035    fn validate_descricao_rejects_trailing_whitespace() {
9036        // Paste-from-doc footgun: a trailing ASCII space the bare
9037        // empty-arm gate accepted, the shape predicate now refuses.
9038        let c = caixa_with_descricao(Some("Checkout flow. "));
9039        let err = c.validate_descricao().unwrap_err();
9040        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9041            panic!("expected DescricaoInvalid, got {err:?}");
9042        };
9043        assert_eq!(descricao, "Checkout flow. ");
9044        assert!(reason.contains("whitespace"), "got: {reason:?}");
9045    }
9046
9047    #[test]
9048    fn validate_descricao_rejects_embedded_newline() {
9049        // Paste-from-multiline-doc footgun: an embedded LF the bare
9050        // empty-arm gate accepted, the shape predicate now refuses.
9051        // Without this gate the embedded newline silently landed in
9052        // the rendered Chart.yaml as a multi-line YAML block scalar,
9053        // and every chart-aware UI (`helm list`, `helm search`,
9054        // Artifact Hub) renders the description in a single-line
9055        // column so the embedded newline is silently dropped at
9056        // every downstream consumer.
9057        let c = caixa_with_descricao(Some("Checkout\nflow."));
9058        let err = c.validate_descricao().unwrap_err();
9059        assert!(
9060            matches!(err, ManifestError::DescricaoInvalid { .. }),
9061            "got {err:?}",
9062        );
9063        assert!(err.to_string().contains("newline"), "got {err}");
9064    }
9065
9066    #[test]
9067    fn validate_descricao_rejects_embedded_carriage_return() {
9068        // Paste-from-Windows-CRLF-doc footgun.
9069        let c = caixa_with_descricao(Some("Checkout\rflow."));
9070        let err = c.validate_descricao().unwrap_err();
9071        assert!(
9072            matches!(err, ManifestError::DescricaoInvalid { .. }),
9073            "got {err:?}",
9074        );
9075        assert!(err.to_string().contains("carriage return"), "got {err}");
9076    }
9077
9078    #[test]
9079    fn validate_descricao_rejects_embedded_tab() {
9080        // Tab-from-aligned-doc footgun.
9081        let c = caixa_with_descricao(Some("Checkout\tflow."));
9082        let err = c.validate_descricao().unwrap_err();
9083        assert!(
9084            matches!(err, ManifestError::DescricaoInvalid { .. }),
9085            "got {err:?}",
9086        );
9087        assert!(err.to_string().contains("tab"), "got {err}");
9088    }
9089
9090    #[test]
9091    fn validate_descricao_rejects_embedded_control_bytes() {
9092        // Paste-from-binary-blob footgun: every other control byte
9093        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
9094        // the peer SPDX-expression control-byte arm.
9095        for s in [
9096            "Checkout\x00flow.",
9097            "Checkout\x07flow.",
9098            "Checkout\x1bflow.",
9099            "Checkout\x7fflow.",
9100        ] {
9101            let c = caixa_with_descricao(Some(s));
9102            let err = c.validate_descricao().unwrap_err();
9103            assert!(
9104                matches!(err, ManifestError::DescricaoInvalid { .. }),
9105                "{s:?} got {err:?}",
9106            );
9107            assert!(
9108                err.to_string().contains("control character"),
9109                "{s:?} got {err}",
9110            );
9111        }
9112    }
9113
9114    #[test]
9115    fn validate_descricao_accepts_unicode_prose() {
9116        // Positive control: Unicode prose is accepted — the
9117        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
9118        // and `Caixa::template`'s `"FIXME — describe this caixa"`
9119        // scaffold every `feira init` emits must continue to pass.
9120        for s in [
9121            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9122            "FIXME — describe this caixa",
9123            "Caixa pour le projet tâche",
9124            "日本語の説明",
9125        ] {
9126            let c = caixa_with_descricao(Some(s));
9127            c.validate_descricao()
9128                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
9129        }
9130    }
9131
9132    #[test]
9133    fn validate_descricao_empty_takes_precedence_over_shape() {
9134        // Cascade pin: a `Some("")` surfaces the narrower
9135        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
9136        // shape-predicate arm. Mirrors the peer
9137        // `validate_licenca_empty_takes_precedence_over_shape` pin
9138        // on the sibling `:licenca` axis.
9139        let c = caixa_with_descricao(Some(""));
9140        let err = c.validate_descricao().unwrap_err();
9141        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9142    }
9143
9144    #[test]
9145    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
9146        // Diagnostic-shape pin: the error's Display surfaces both
9147        // the `:descricao` slot name and the offending value
9148        // verbatim, so a `feira lint` run can render the diagnostic
9149        // without re-parsing and the author can grep their caixa.lisp
9150        // for the offending `:descricao` line. Mirrors the peer
9151        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
9152        // pin (ee2e888) on the sibling `:licenca` axis.
9153        // The `{descricao:?}` Debug format escapes embedded control
9154        // bytes; the quoted offending value surfaces as
9155        // `"Checkout\nflow."` (literal backslash-n) in the rendered
9156        // diagnostic. The author can grep their caixa.lisp for the
9157        // literal `Checkout` summary prefix.
9158        let c = caixa_with_descricao(Some("Checkout\nflow."));
9159        let rendered = c.validate_descricao().unwrap_err().to_string();
9160        assert!(
9161            rendered.contains(":descricao"),
9162            "diagnostic must name the offending slot: {rendered}",
9163        );
9164        assert!(
9165            rendered.contains("Checkout\\nflow."),
9166            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9167        );
9168    }
9169
9170    #[test]
9171    fn validate_descricao_template_passes() {
9172        // Round-trip pin: the bare `Caixa::template` shape carries
9173        // `:descricao "FIXME — describe this caixa"` (a non-empty
9174        // sentinel), so the template-derived Caixa passes the gate by
9175        // construction. A future template-shape change that omits or
9176        // empties `:descricao` would surface here as a regression.
9177        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9178        c.validate_descricao().unwrap();
9179    }
9180
9181    #[test]
9182    fn validate_descricao_diagnostic_names_offending_slot() {
9183        // Diagnostic-shape pin (peer with
9184        // `validate_repositorio_diagnostic_carries_offending_value`):
9185        // the error's Display surfaces the `:descricao` slot name
9186        // verbatim, so a `feira lint` run can render the diagnostic
9187        // without re-parsing and the author can grep their caixa.lisp
9188        // for the offending `:descricao` line.
9189        let c = caixa_with_descricao(Some(""));
9190        let rendered = c.validate_descricao().unwrap_err().to_string();
9191        assert!(
9192            rendered.contains(":descricao"),
9193            "diagnostic must name the offending slot: {rendered}",
9194        );
9195    }
9196
9197    // ── validate_licenca — universal-axis chart README license shape ──
9198
9199    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
9200        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9201        c.licenca = licenca.map(String::from);
9202        c
9203    }
9204
9205    #[test]
9206    fn validate_licenca_accepts_none() {
9207        // The omit-the-slot identity: `:licenca` is optional. The
9208        // gate is a no-op when the author didn't declare a value —
9209        // every caixa without a `:licenca` line trivially passes,
9210        // and the substrate-side `caixa-helm` renderer falls back to
9211        // the documented `"MIT"` placeholder. Mirrors the peer
9212        // `validate_descricao_accepts_none` posture on the sibling
9213        // `Option<String>` Caixa slot.
9214        let c = caixa_with_licenca(None);
9215        c.validate_licenca().unwrap();
9216    }
9217
9218    #[test]
9219    fn validate_licenca_accepts_canonical_expressions() {
9220        // Positive control: every canonical SPDX expression shape
9221        // pleme-io carries in its existing fixtures + the canonical
9222        // SPDX dual-license / with-exception / `+`-suffix / grouped /
9223        // user-defined-reference shapes all pass the gate. Covers
9224        // the single-license, `OR`-compound, `AND`-compound,
9225        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
9226        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
9227        // production the SPDX 2.1 expression grammar admits that
9228        // sits within the alphabet floor the
9229        // `is_spdx_expression_shape` predicate enforces.
9230        for lic in [
9231            "MIT",
9232            "Apache-2.0",
9233            "Apache-2.0 OR MIT",
9234            "Apache-2.0 AND MIT",
9235            "BSD-3-Clause",
9236            "MPL-2.0",
9237            "GPL-3.0-or-later",
9238            "GPL-2.0+",
9239            "Apache-2.0 WITH LLVM-exception",
9240            "(MIT OR Apache-2.0) AND BSD-3-Clause",
9241            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
9242            "LicenseRef-MyLicense",
9243            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
9244            "x",
9245        ] {
9246            let c = caixa_with_licenca(Some(lic));
9247            c.validate_licenca()
9248                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
9249        }
9250    }
9251
9252    #[test]
9253    fn validate_licenca_rejects_trailing_whitespace() {
9254        // Paste-from-doc whitespace footgun. A trailing space in the
9255        // `:licenca` value would silently break a downstream SPDX
9256        // parser that splits on exact `AND` / `OR` / `WITH` keyword
9257        // boundaries. The shape predicate refuses every trailing
9258        // whitespace byte by construction. Peer with
9259        // `validate_repositorio_rejects_whitespace` and
9260        // `validate_edicao_rejects_trailing_whitespace`.
9261        let c = caixa_with_licenca(Some("MIT "));
9262        let err = c.validate_licenca().unwrap_err();
9263        let ManifestError::LicencaInvalid { licenca, .. } = err else {
9264            panic!("expected LicencaInvalid, got {err:?}");
9265        };
9266        assert_eq!(licenca, "MIT ");
9267    }
9268
9269    #[test]
9270    fn validate_licenca_rejects_leading_whitespace() {
9271        // Symmetric paste-from-doc whitespace footgun on the leading
9272        // boundary — the gate refuses every shape that starts with a
9273        // space byte by construction. Peer with
9274        // `validate_edicao_rejects_leading_whitespace`.
9275        let c = caixa_with_licenca(Some(" MIT"));
9276        let err = c.validate_licenca().unwrap_err();
9277        assert!(
9278            matches!(err, ManifestError::LicencaInvalid { .. }),
9279            "got {err:?}",
9280        );
9281    }
9282
9283    #[test]
9284    fn validate_licenca_rejects_control_char() {
9285        // Paste-from-multiline-doc CRLF footgun — control characters
9286        // at the value boundary land as a malformed line in the
9287        // rendered chart `README.md` `## License` section. Peer with
9288        // `validate_repositorio_rejects_control_char` and
9289        // `validate_edicao_rejects_control_char`.
9290        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
9291            let c = caixa_with_licenca(Some(lic));
9292            let err = c.validate_licenca().unwrap_err();
9293            assert!(
9294                matches!(err, ManifestError::LicencaInvalid { .. }),
9295                "expected LicencaInvalid on {lic:?}, got {err:?}",
9296            );
9297        }
9298    }
9299
9300    #[test]
9301    fn validate_licenca_rejects_tab() {
9302        // Tab-from-aligned-doc footgun — SPDX expressions use a
9303        // single ASCII space between tokens; a tab breaks every
9304        // downstream SPDX parser that splits on exact `" "`
9305        // boundaries.
9306        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
9307        let err = c.validate_licenca().unwrap_err();
9308        assert!(
9309            matches!(err, ManifestError::LicencaInvalid { .. }),
9310            "got {err:?}",
9311        );
9312    }
9313
9314    #[test]
9315    fn validate_licenca_rejects_non_ascii() {
9316        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
9317        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
9318        // ".")` production. The shape predicate refuses every
9319        // non-ASCII byte by construction; peer with
9320        // `validate_edicao_rejects_non_ascii_lookalike`.
9321        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
9322            let c = caixa_with_licenca(Some(lic));
9323            let err = c.validate_licenca().unwrap_err();
9324            assert!(
9325                matches!(err, ManifestError::LicencaInvalid { .. }),
9326                "expected LicencaInvalid on {lic:?}, got {err:?}",
9327            );
9328        }
9329    }
9330
9331    #[test]
9332    fn validate_licenca_rejects_underscore() {
9333        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
9334        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
9335        // snake-case identifier conventions that don't apply to the
9336        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
9337        // "-" / "."`). The shape predicate refuses every underscore
9338        // byte by construction.
9339        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
9340            let c = caixa_with_licenca(Some(lic));
9341            let err = c.validate_licenca().unwrap_err();
9342            assert!(
9343                matches!(err, ManifestError::LicencaInvalid { .. }),
9344                "expected LicencaInvalid on {lic:?}, got {err:?}",
9345            );
9346        }
9347    }
9348
9349    #[test]
9350    fn validate_licenca_rejects_comma_separator() {
9351        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
9352        // SPDX expressions compose multiple licenses via `AND` / `OR`
9353        // keywords, not the comma separator. The shape predicate
9354        // refuses every comma byte by construction.
9355        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
9356            let c = caixa_with_licenca(Some(lic));
9357            let err = c.validate_licenca().unwrap_err();
9358            assert!(
9359                matches!(err, ManifestError::LicencaInvalid { .. }),
9360                "expected LicencaInvalid on {lic:?}, got {err:?}",
9361            );
9362        }
9363    }
9364
9365    #[test]
9366    fn validate_licenca_rejects_slash_dual_license() {
9367        // Slash-dual-license colloquial idiom footgun — the
9368        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
9369        // `package.license` field but non-SPDX; the SPDX equivalent
9370        // is `MIT OR Apache-2.0`. The shape predicate refuses every
9371        // forward-slash byte by construction.
9372        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
9373            let c = caixa_with_licenca(Some(lic));
9374            let err = c.validate_licenca().unwrap_err();
9375            assert!(
9376                matches!(err, ManifestError::LicencaInvalid { .. }),
9377                "expected LicencaInvalid on {lic:?}, got {err:?}",
9378            );
9379        }
9380    }
9381
9382    #[test]
9383    fn validate_licenca_rejects_semicolon_separator() {
9384        // Semicolon-list-separator confusion footgun — adjacent to
9385        // the comma-separator idiom, every list-separator-belongs-
9386        // to-list-grammar confusion lands here.
9387        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
9388        let err = c.validate_licenca().unwrap_err();
9389        assert!(
9390            matches!(err, ManifestError::LicencaInvalid { .. }),
9391            "got {err:?}",
9392        );
9393    }
9394
9395    #[test]
9396    fn validate_licenca_empty_takes_precedence_over_shape() {
9397        // Empty-first cascade pin: the empty `Some("")` surfaces the
9398        // narrower `LicencaEmpty` not the shape-predicate-wrapped
9399        // `LicencaInvalid`, mirroring the peer
9400        // `validate_edicao_empty_takes_precedence_over_shape` and
9401        // `validate_repositorio_empty_takes_precedence_over_shape`
9402        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
9403        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
9404        // The shape predicate also refuses the empty input
9405        // (defensively — `"must not be empty"`), but the manifest-
9406        // layer empty arm runs first to surface the narrower
9407        // diagnostic verbatim.
9408        let c = caixa_with_licenca(Some(""));
9409        let err = c.validate_licenca().unwrap_err();
9410        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
9411    }
9412
9413    #[test]
9414    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
9415        // Diagnostic-shape pin on the shape-predicate arm (peer with
9416        // `validate_edicao_invalid_diagnostic_carries_offending_value`
9417        // and `validate_repositorio_diagnostic_carries_offending_value`):
9418        // the error's Display surfaces the offending value + slot
9419        // name verbatim, so a `feira lint` run can render the
9420        // diagnostic without re-parsing and the author can grep
9421        // their caixa.lisp for the offending `:licenca` value.
9422        let c = caixa_with_licenca(Some("Apache_2.0"));
9423        let rendered = c.validate_licenca().unwrap_err().to_string();
9424        assert!(
9425            rendered.contains(":licenca"),
9426            "diagnostic must name the offending slot: {rendered}",
9427        );
9428        assert!(
9429            rendered.contains("Apache_2.0"),
9430            "diagnostic must quote the offending value: {rendered}",
9431        );
9432    }
9433
9434    #[test]
9435    fn validate_licenca_rejects_empty_some() {
9436        // Canonical paste-from-blank-doc footgun. Without this gate
9437        // the empty `Some("")` silently passed the renderer's
9438        // `Option::unwrap_or_else(|| "MIT".into())` (which only
9439        // fires on `None`) and landed as a bare trailing period in
9440        // the rendered chart `README.md` `## License` section.
9441        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
9442        // arm on the sibling `Option<String>` Caixa slot.
9443        let c = caixa_with_licenca(Some(""));
9444        let err = c.validate_licenca().unwrap_err();
9445        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
9446    }
9447
9448    #[test]
9449    fn validate_licenca_template_passes() {
9450        // Round-trip pin: the bare `Caixa::template` shape (whether
9451        // it carries `:licenca` or omits it) passes the gate by
9452        // construction. A future template-shape change that
9453        // introduced `(:licenca "")` would surface here as a
9454        // regression. Mirrors the peer
9455        // `validate_descricao_template_passes` pin.
9456        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9457        c.validate_licenca().unwrap();
9458    }
9459
9460    #[test]
9461    fn validate_licenca_diagnostic_names_offending_slot() {
9462        // Diagnostic-shape pin (peer with
9463        // `validate_descricao_diagnostic_names_offending_slot`):
9464        // the error's Display surfaces the `:licenca` slot name
9465        // verbatim, so a `feira lint` run can render the diagnostic
9466        // without re-parsing and the author can grep their caixa.lisp
9467        // for the offending `:licenca` line.
9468        let c = caixa_with_licenca(Some(""));
9469        let rendered = c.validate_licenca().unwrap_err().to_string();
9470        assert!(
9471            rendered.contains(":licenca"),
9472            "diagnostic must name the offending slot: {rendered}",
9473        );
9474    }
9475
9476    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
9477
9478    #[test]
9479    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
9480        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
9481        // pin: [`Caixa::licenca`] must return the `:licenca` typed
9482        // byte-string verbatim as an `Option<&str>`, byte-equal to the
9483        // raw `self.licenca.as_deref()` access across every
9484        // representative value in the accept-set — `None` (the "omit
9485        // the slot to defer to the caixa-helm renderer's `MIT`
9486        // fallback" arm every existing fixture without a `:licenca`
9487        // line carries), `Some("")` (a past-the-guard sentinel that
9488        // pins the accessor doesn't perform a silent
9489        // `Some("") → None` collapse on the empty arm — validate
9490        // rejects `Some("")` through `LicencaEmpty` but the accessor
9491        // must ship the raw slot verbatim so a validate-time gate
9492        // regression surfaces at the caixa-helm emit boundary rather
9493        // than being silently absorbed into the fallback), `Some("MIT")`
9494        // (the canonical single-license shape every `feira init`
9495        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
9496        // canonical `OR`-compound shape the peer
9497        // `validate_licenca_accepts_canonical_expressions` positive
9498        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
9499        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
9500        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
9501        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
9502        // guard sentinels — validate rejects each through
9503        // `LicencaInvalid` but the accessor must ship the raw slot
9504        // verbatim).
9505        //
9506        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
9507        // accessor pin on the substrate primitive — opens the "outer
9508        // [`Caixa`] `Option<&str>` scalar" projection pattern the
9509        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
9510        // future lifts fold on. Sibling in shape to the peer per-`:placement`
9511        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
9512        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
9513        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
9514        // axes, extended onto the outer top-level [`Caixa`] universal-
9515        // axis surface. Pins against a future silent detour that
9516        // returned an owned `Option<String>` (which would type-check
9517        // but silently allocate on every accessor call, breaking the
9518        // zero-cost projection every peer sibling accessor carries), a
9519        // `Some("") → None` collapse (which would silently absorb the
9520        // `LicencaEmpty` refusal case at the accessor boundary and the
9521        // caixa-helm emit path would silently fall back to `"MIT"` on
9522        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
9523        // `None → Some("MIT")` collapse (which would silently reify
9524        // the caixa-helm renderer's `"MIT"` fallback at the accessor
9525        // boundary and every downstream consumer keying off the
9526        // `Option::is_none()` discriminator would lose the "author
9527        // omitted the slot" signal).
9528        for licenca in [
9529            None,
9530            Some(""),
9531            Some("MIT"),
9532            Some("Apache-2.0 OR MIT"),
9533            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
9534            Some("MIT "),
9535            Some(" MIT"),
9536            Some("MIT\n"),
9537            Some("Apache_2.0"),
9538            Some("MIT,Apache-2.0"),
9539        ] {
9540            let c = caixa_with_licenca(licenca);
9541            assert_eq!(
9542                c.licenca(),
9543                licenca,
9544                "Caixa::licenca must return :licenca verbatim (got {:?}, \
9545                 expected {licenca:?})",
9546                c.licenca(),
9547            );
9548            assert_eq!(
9549                c.licenca(),
9550                c.licenca.as_deref(),
9551                "Caixa::licenca must byte-equal the raw \
9552                 `self.licenca.as_deref()` field access across every \
9553                 value in the Option<&str> accept-set",
9554            );
9555        }
9556    }
9557
9558    #[test]
9559    fn validate_licenca_empty_arm_routes_through_accessor() {
9560        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
9561        // must key off [`Caixa::licenca`], not the raw
9562        // `self.licenca.as_deref()` field access. Structurally: a
9563        // `Caixa { licenca: Some(""), .. }` must surface the
9564        // `LicencaEmpty` refusal exactly, and a
9565        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
9566        // single-license form) must pass validate. The pair jointly
9567        // pins the accessor + validate-gate composition: any future
9568        // silent detour that had the accessor return `None` on the
9569        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
9570        // silently absorb the `LicencaEmpty` refusal at the accessor
9571        // boundary and the validate gate would accept a struct-literal
9572        // `Caixa { licenca: Some(""), .. }` — the composition pin
9573        // catches that at caixa-core build time.
9574        //
9575        // Peer of the per-`:politicas :circuit-breaker`
9576        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
9577        // accessor-composition pin
9578        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
9579        // on the sibling per-M3-mesh-slot required-`u32` axis — same
9580        // "the validate / shape-gate predicate must route through the
9581        // substrate-primitive typed dispatch" discipline extended onto
9582        // the outer top-level [`Caixa`] universal-axis
9583        // `Option<&str>`-composition surface.
9584        let c = caixa_with_licenca(Some(""));
9585        assert!(
9586            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
9587            "validate_licenca must reject licenca == Some(\"\") with \
9588             LicencaEmpty — the accessor and the validate gate must \
9589             route through the same substrate-primitive typed dispatch \
9590             on the :licenca empty arm",
9591        );
9592        let c = caixa_with_licenca(Some("MIT"));
9593        assert!(
9594            c.validate_licenca().is_ok(),
9595            "validate_licenca must accept licenca == Some(\"MIT\") \
9596             (the canonical single-license SPDX shape)",
9597        );
9598    }
9599
9600    #[test]
9601    fn licenca_projects_option_str_by_borrow() {
9602        // The by-borrow pin: [`Caixa::licenca`] returns
9603        // `Option<&str>` by borrow — the `&str` borrows the underlying
9604        // `String` storage of the `Option<String>` slot and the
9605        // accessor must not allocate a fresh `String` on every call.
9606        // Peer of the per-`:placement`
9607        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
9608        // borrow pin on the peer per-M3-mesh-slot
9609        // `Option<&str>`-return axis, extended onto the outer top-
9610        // level [`Caixa`] universal-axis `Option<&str>` shape — the
9611        // accessor's returned `&str` must borrow from `&self` (the
9612        // returned reference's lifetime is tied to `&self`), and
9613        // calling the accessor twice on the same [`Caixa`] must yield
9614        // the same `Option<&str>` verbatim (idempotent, no side
9615        // effects on `&self`).
9616        //
9617        // Pins against a future silent detour that returned an owned
9618        // `Option<String>` (which would type-check but silently
9619        // allocate on every call, breaking the zero-cost projection
9620        // every peer sibling accessor carries), or a one-arm-only
9621        // accessor that returned a saturating value on some sentinel
9622        // input (breaking the pass-through invariant the sibling
9623        // required-scalar accessors carry).
9624        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
9625            let c = caixa_with_licenca(licenca);
9626            let first = c.licenca();
9627            let second = c.licenca();
9628            assert_eq!(
9629                first, second,
9630                "Caixa::licenca must be idempotent — two successive \
9631                 calls on the same &self must return the same \
9632                 Option<&str>",
9633            );
9634            assert_eq!(
9635                first, licenca,
9636                "Caixa::licenca must return :licenca verbatim by \
9637                 borrow — got {first:?}, expected {licenca:?}",
9638            );
9639        }
9640    }
9641
9642    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
9643
9644    #[test]
9645    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
9646        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
9647        // pin: [`Caixa::repositorio`] must return the `:repositorio`
9648        // typed byte-string verbatim as an `Option<&str>`, byte-equal
9649        // to the raw `self.repositorio.as_deref()` access across every
9650        // representative value in the accept-set — `None` (the "omit
9651        // the slot to defer to the per-renderer placeholder" arm every
9652        // existing fixture without a `:repositorio` line carries),
9653        // `Some("")` (a past-the-guard sentinel that pins the accessor
9654        // doesn't perform a silent `Some("") → None` collapse on the
9655        // empty arm — validate rejects `Some("")` through
9656        // `RepositorioEmpty` but the accessor must ship the raw slot
9657        // verbatim so a validate-time gate regression surfaces at the
9658        // caixa-helm / caixa-flux emit boundary rather than being
9659        // silently absorbed into the per-renderer fallback),
9660        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
9661        // shorthand every existing manifest fixture across
9662        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
9663        // `Some("https://github.com/pleme-io/checkout")` (the canonical
9664        // `https://` URL the README quickstart uses),
9665        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
9666        // `Some("git://github.com/pleme-io/checkout.git")` /
9667        // `Some("git@github.com:pleme-io/checkout.git")` /
9668        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
9669        // github scheme the shared `is_git_repo_url` predicate
9670        // documents), and five past-the-guard sentinels for the
9671        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
9672        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
9673        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
9674        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
9675        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
9676        // sentinels pin the accessor doesn't silently absorb the
9677        // refusal cases into a fallback).
9678        //
9679        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
9680        // accessor pin on the substrate primitive — sibling of the peer
9681        // [`Caixa::licenca`] (6d5bc28) pin
9682        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
9683        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
9684        // projection pin pattern this pin folds on. Sibling in shape to
9685        // the peer per-`:placement`
9686        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
9687        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
9688        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
9689        // axes, extended onto the outer top-level [`Caixa`] universal-
9690        // axis surface. Pins against a future silent detour that
9691        // returned an owned `Option<String>` (which would type-check
9692        // but silently allocate on every accessor call, breaking the
9693        // zero-cost projection every peer sibling accessor carries), a
9694        // `Some("") → None` collapse (which would silently absorb the
9695        // `RepositorioEmpty` refusal case at the accessor boundary and
9696        // the caixa-helm `Chart.yaml` `home:` fold would silently
9697        // render a `home: null` / omitted field on a struct-literal
9698        // `Caixa { repositorio: Some(""), .. }`), or a
9699        // `None → Some(<default>)` collapse (which would silently reify
9700        // the per-renderer fallback at the accessor boundary and every
9701        // downstream consumer keying off the `Option::is_none()`
9702        // discriminator would lose the "author omitted the slot"
9703        // signal).
9704        for repositorio in [
9705            None,
9706            Some(""),
9707            Some("github:pleme-io/hello-rio"),
9708            Some("https://github.com/pleme-io/checkout"),
9709            Some("ssh://git@github.com/pleme-io/checkout.git"),
9710            Some("git://github.com/pleme-io/checkout.git"),
9711            Some("git@github.com:pleme-io/checkout.git"),
9712            Some("file:///opt/mirrors/pleme-io/checkout"),
9713            Some("pleme-io/checkout"),
9714            Some("-upload-pack=evil"),
9715            Some("github:pleme-io/checkout?ref=main"),
9716            Some("github:pleme-io/checkout#main"),
9717            Some("github:pleme-io/{tpl}"),
9718        ] {
9719            let c = caixa_with_repositorio(repositorio);
9720            assert_eq!(
9721                c.repositorio(),
9722                repositorio,
9723                "Caixa::repositorio must return :repositorio verbatim \
9724                 (got {:?}, expected {repositorio:?})",
9725                c.repositorio(),
9726            );
9727            assert_eq!(
9728                c.repositorio(),
9729                c.repositorio.as_deref(),
9730                "Caixa::repositorio must byte-equal the raw \
9731                 `self.repositorio.as_deref()` field access across every \
9732                 value in the Option<&str> accept-set",
9733            );
9734        }
9735    }
9736
9737    #[test]
9738    fn validate_repositorio_empty_arm_routes_through_accessor() {
9739        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
9740        // gate must key off [`Caixa::repositorio`], not the raw
9741        // `self.repositorio.as_deref()` field access. Structurally: a
9742        // `Caixa { repositorio: Some(""), .. }` must surface the
9743        // `RepositorioEmpty` refusal exactly, and a
9744        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
9745        // (the canonical `github:` shorthand form) must pass validate.
9746        // The pair jointly pins the accessor + validate-gate
9747        // composition: any future silent detour that had the accessor
9748        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
9749        // collapse) would silently absorb the `RepositorioEmpty` refusal
9750        // at the accessor boundary and the validate gate would accept a
9751        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
9752        // composition pin catches that at caixa-core build time.
9753        //
9754        // Peer of the [`Caixa::licenca`] (6d5bc28)
9755        // `validate_licenca_empty_arm_routes_through_accessor`
9756        // composition pin on the sibling outer top-level [`Caixa`]
9757        // `Option<&str>` universal-axis surface — same "the validate /
9758        // shape-gate predicate must route through the substrate-
9759        // primitive typed dispatch" discipline extended onto the second
9760        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
9761        // composition surface.
9762        let c = caixa_with_repositorio(Some(""));
9763        assert!(
9764            matches!(
9765                c.validate_repositorio(),
9766                Err(ManifestError::RepositorioEmpty),
9767            ),
9768            "validate_repositorio must reject repositorio == Some(\"\") \
9769             with RepositorioEmpty — the accessor and the validate gate \
9770             must route through the same substrate-primitive typed \
9771             dispatch on the :repositorio empty arm",
9772        );
9773        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
9774        assert!(
9775            c.validate_repositorio().is_ok(),
9776            "validate_repositorio must accept repositorio == \
9777             Some(\"github:pleme-io/hello-rio\") (the canonical \
9778             `github:` shorthand git-repo-URL shape)",
9779        );
9780    }
9781
9782    #[test]
9783    fn repositorio_projects_option_str_by_borrow() {
9784        // The by-borrow pin: [`Caixa::repositorio`] returns
9785        // `Option<&str>` by borrow — the `&str` borrows the underlying
9786        // `String` storage of the `Option<String>` slot and the
9787        // accessor must not allocate a fresh `String` on every call.
9788        // Peer of the per-`:placement`
9789        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
9790        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
9791        // `Option<&str>`-return axes, extended onto the second outer
9792        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
9793        // the accessor's returned `&str` must borrow from `&self` (the
9794        // returned reference's lifetime is tied to `&self`), and
9795        // calling the accessor twice on the same [`Caixa`] must yield
9796        // the same `Option<&str>` verbatim (idempotent, no side effects
9797        // on `&self`).
9798        //
9799        // Pins against a future silent detour that returned an owned
9800        // `Option<String>` (which would type-check but silently
9801        // allocate on every call, breaking the zero-cost projection
9802        // every peer sibling accessor carries), or a one-arm-only
9803        // accessor that returned a saturating value on some sentinel
9804        // input (breaking the pass-through invariant the sibling
9805        // required-scalar accessors carry).
9806        for repositorio in [
9807            None,
9808            Some(""),
9809            Some("github:pleme-io/hello-rio"),
9810            Some("https://github.com/pleme-io/checkout"),
9811        ] {
9812            let c = caixa_with_repositorio(repositorio);
9813            let first = c.repositorio();
9814            let second = c.repositorio();
9815            assert_eq!(
9816                first, second,
9817                "Caixa::repositorio must be idempotent — two successive \
9818                 calls on the same &self must return the same \
9819                 Option<&str>",
9820            );
9821            assert_eq!(
9822                first, repositorio,
9823                "Caixa::repositorio must return :repositorio verbatim by \
9824                 borrow — got {first:?}, expected {repositorio:?}",
9825            );
9826        }
9827    }
9828
9829    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
9830
9831    #[test]
9832    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
9833        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
9834        // pin: [`Caixa::descricao`] must return the `:descricao` typed
9835        // byte-string verbatim as an `Option<&str>`, byte-equal to the
9836        // raw `self.descricao.as_deref()` access across every
9837        // representative value in the accept-set — `None` (the "omit
9838        // the slot to defer to the per-renderer `caixa.nome`-derived
9839        // fallback" arm every existing fixture without a `:descricao`
9840        // line carries), `Some("")` (a past-the-guard sentinel that
9841        // pins the accessor doesn't perform a silent `Some("") → None`
9842        // collapse on the empty arm — validate rejects `Some("")`
9843        // through `DescricaoEmpty` but the accessor must ship the raw
9844        // slot verbatim so a validate-time gate regression surfaces at
9845        // the caixa-helm / caixa-feira emit boundary rather than being
9846        // silently absorbed into the per-renderer `caixa.nome`-derived
9847        // fallback), `Some("Checkout flow.")` (the canonical one-line
9848        // prose descriptor the peer
9849        // `validate_descricao_accepts_canonical_value` positive sweep
9850        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
9851        // Servico.")` (the multi-byte Unicode continuation-byte shape
9852        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
9853        // multi-glyph Unicode shape the peer
9854        // `is_chart_description_shape` predicate accepts), and five
9855        // past-the-guard sentinels for the `DescricaoInvalid` refusal
9856        // cases (`Some(" Checkout flow.")` leading-whitespace,
9857        // `Some("Checkout flow. ")` trailing-whitespace,
9858        // `Some("Checkout\nflow.")` embedded-LF,
9859        // `Some("Checkout\tflow.")` embedded-TAB, and
9860        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
9861        // the accessor doesn't silently absorb the refusal cases into
9862        // a fallback).
9863        //
9864        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
9865        // accessor pin on the substrate primitive — sibling of the peer
9866        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
9867        // (cc7332d) pins that opened the "outer [`Caixa`]
9868        // `Option<&str>` scalar" projection pin pattern this pin folds
9869        // on. Sibling in shape to the peer per-`:placement`
9870        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
9871        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
9872        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
9873        // axes, extended onto the outer top-level [`Caixa`] universal-
9874        // axis surface. Pins against a future silent detour that
9875        // returned an owned `Option<String>` (which would type-check
9876        // but silently allocate on every accessor call, breaking the
9877        // zero-cost projection every peer sibling accessor carries), a
9878        // `Some("") → None` collapse (which would silently absorb the
9879        // `DescricaoEmpty` refusal case at the accessor boundary and
9880        // the caixa-helm `Chart.yaml` `description:` fold would
9881        // silently render a `caixa.nome`-derived fallback on a
9882        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
9883        // `None → Some(<default>)` collapse (which would silently
9884        // reify the per-renderer `caixa.nome`-derived fallback at the
9885        // accessor boundary and every downstream consumer keying off
9886        // the `Option::is_none()` discriminator would lose the "author
9887        // omitted the slot" signal).
9888        for descricao in [
9889            None,
9890            Some(""),
9891            Some("Checkout flow."),
9892            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
9893            Some("→ — · ✓"),
9894            Some(" Checkout flow."),
9895            Some("Checkout flow. "),
9896            Some("Checkout\nflow."),
9897            Some("Checkout\tflow."),
9898            Some("Checkout\x00flow."),
9899        ] {
9900            let c = caixa_with_descricao(descricao);
9901            assert_eq!(
9902                c.descricao(),
9903                descricao,
9904                "Caixa::descricao must return :descricao verbatim (got \
9905                 {:?}, expected {descricao:?})",
9906                c.descricao(),
9907            );
9908            assert_eq!(
9909                c.descricao(),
9910                c.descricao.as_deref(),
9911                "Caixa::descricao must byte-equal the raw \
9912                 `self.descricao.as_deref()` field access across every \
9913                 value in the Option<&str> accept-set",
9914            );
9915        }
9916    }
9917
9918    #[test]
9919    fn validate_descricao_empty_arm_routes_through_accessor() {
9920        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
9921        // gate must key off [`Caixa::descricao`], not the raw
9922        // `self.descricao.as_deref()` field access. Structurally: a
9923        // `Caixa { descricao: Some(""), .. }` must surface the
9924        // `DescricaoEmpty` refusal exactly, and a
9925        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
9926        // canonical one-line-prose form) must pass validate. The pair
9927        // jointly pins the accessor + validate-gate composition: any
9928        // future silent detour that had the accessor return `None` on
9929        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
9930        // silently absorb the `DescricaoEmpty` refusal at the accessor
9931        // boundary and the validate gate would accept a struct-literal
9932        // `Caixa { descricao: Some(""), .. }` — the composition pin
9933        // catches that at caixa-core build time.
9934        //
9935        // Peer of the [`Caixa::licenca`] (6d5bc28)
9936        // `validate_licenca_empty_arm_routes_through_accessor` and
9937        // [`Caixa::repositorio`] (cc7332d)
9938        // `validate_repositorio_empty_arm_routes_through_accessor`
9939        // composition pins on the sibling outer top-level [`Caixa`]
9940        // `Option<&str>` universal-axis surface — same "the validate /
9941        // shape-gate predicate must route through the substrate-
9942        // primitive typed dispatch" discipline extended onto the third
9943        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
9944        // composition surface.
9945        let c = caixa_with_descricao(Some(""));
9946        assert!(
9947            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
9948            "validate_descricao must reject descricao == Some(\"\") \
9949             with DescricaoEmpty — the accessor and the validate gate \
9950             must route through the same substrate-primitive typed \
9951             dispatch on the :descricao empty arm",
9952        );
9953        let c = caixa_with_descricao(Some("Checkout flow."));
9954        assert!(
9955            c.validate_descricao().is_ok(),
9956            "validate_descricao must accept descricao == \
9957             Some(\"Checkout flow.\") (the canonical one-line-prose \
9958             chart-description shape)",
9959        );
9960    }
9961
9962    #[test]
9963    fn descricao_projects_option_str_by_borrow() {
9964        // The by-borrow pin: [`Caixa::descricao`] returns
9965        // `Option<&str>` by borrow — the `&str` borrows the underlying
9966        // `String` storage of the `Option<String>` slot and the
9967        // accessor must not allocate a fresh `String` on every call.
9968        // Peer of the [`Caixa::licenca`] (6d5bc28) and
9969        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
9970        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
9971        // the per-`:placement`
9972        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
9973        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
9974        // return axis, extended onto the third outer top-level
9975        // [`Caixa`] universal-axis `Option<&str>` shape — the
9976        // accessor's returned `&str` must borrow from `&self` (the
9977        // returned reference's lifetime is tied to `&self`), and
9978        // calling the accessor twice on the same [`Caixa`] must yield
9979        // the same `Option<&str>` verbatim (idempotent, no side
9980        // effects on `&self`).
9981        //
9982        // Pins against a future silent detour that returned an owned
9983        // `Option<String>` (which would type-check but silently
9984        // allocate on every call, breaking the zero-cost projection
9985        // every peer sibling accessor carries), or a one-arm-only
9986        // accessor that returned a saturating value on some sentinel
9987        // input (breaking the pass-through invariant the sibling
9988        // required-scalar accessors carry).
9989        for descricao in [
9990            None,
9991            Some(""),
9992            Some("Checkout flow."),
9993            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
9994        ] {
9995            let c = caixa_with_descricao(descricao);
9996            let first = c.descricao();
9997            let second = c.descricao();
9998            assert_eq!(
9999                first, second,
10000                "Caixa::descricao must be idempotent — two successive \
10001                 calls on the same &self must return the same \
10002                 Option<&str>",
10003            );
10004            assert_eq!(
10005                first, descricao,
10006                "Caixa::descricao must return :descricao verbatim by \
10007                 borrow — got {first:?}, expected {descricao:?}",
10008            );
10009        }
10010    }
10011
10012    // ── validate_edicao — universal-axis language-edition shape ──
10013
10014    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
10015        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10016        c.edicao = edicao.map(String::from);
10017        c
10018    }
10019
10020    #[test]
10021    fn validate_edicao_accepts_none() {
10022        // The omit-the-slot identity: `:edicao` is optional. The
10023        // gate is a no-op when the author didn't declare a value —
10024        // every caixa without an `:edicao` line trivially passes,
10025        // and the substrate-side build pipeline falls back to the
10026        // documented default edition. Mirrors the peer
10027        // `validate_licenca_accepts_none` posture on the sibling
10028        // `Option<String>` Caixa slot.
10029        let c = caixa_with_edicao(None);
10030        c.validate_edicao().unwrap();
10031    }
10032
10033    #[test]
10034    fn validate_edicao_accepts_canonical_value() {
10035        // Positive control: the canonical `"2026"` edition every
10036        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
10037        // `caixa-mesh`) carries by construction passes the gate.
10038        // Future-introduced sibling editions (`"2027"`, `"2030"`,
10039        // `"2049"`) that match the same 4-digit ASCII decimal year
10040        // shape must also trivially pass — the structural shape
10041        // predicate accepts every well-formed year regardless of
10042        // whether the substrate yet understands the specific value
10043        // (a future known-edition allowlist tightens that).
10044        for ed in ["2026", "2027", "2030", "2049"] {
10045            let c = caixa_with_edicao(Some(ed));
10046            c.validate_edicao()
10047                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
10048        }
10049    }
10050
10051    #[test]
10052    fn validate_edicao_rejects_empty_some() {
10053        // Canonical paste-from-blank-doc footgun. Without this gate
10054        // the empty `Some("")` silently lands as `(:edicao "")` in
10055        // the rendered caixa.lisp and a future renderer-side
10056        // consumer's `Option::unwrap_or_else` (which only fires on
10057        // `None`) skips its fallback. Mirrors the peer
10058        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
10059        // `Option<String>` Caixa slot.
10060        let c = caixa_with_edicao(Some(""));
10061        let err = c.validate_edicao().unwrap_err();
10062        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
10063    }
10064
10065    #[test]
10066    fn validate_edicao_rejects_free_form_non_year() {
10067        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
10068        // `"nightly"` shapes carry no operational meaning on the
10069        // substrate's build-time edition selector. Until this gate
10070        // landed the bare empty-arm check let every such value
10071        // through and broke far from the source caixa.lisp. Peer
10072        // with the shape-predicate cascade
10073        // `validate_repositorio_rejects_missing_colon_separator`
10074        // establishes past its own empty arm.
10075        for ed in ["x", "latest", "nightly", "stable"] {
10076            let c = caixa_with_edicao(Some(ed));
10077            let err = c.validate_edicao().unwrap_err();
10078            assert!(
10079                matches!(err, ManifestError::EdicaoInvalid { .. }),
10080                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10081            );
10082        }
10083    }
10084
10085    #[test]
10086    fn validate_edicao_rejects_trailing_whitespace() {
10087        // Paste-from-doc whitespace footgun. A trailing space in
10088        // the `:edicao` value would silently break the substrate's
10089        // build-time edition match-table lookup at the rendered
10090        // artifact's edition-selector consumer. The shape predicate
10091        // refuses every whitespace byte by construction (any byte
10092        // outside `0-9` fails `is_ascii_digit`). Peer with
10093        // `validate_repositorio_rejects_whitespace`.
10094        let c = caixa_with_edicao(Some("2026 "));
10095        let err = c.validate_edicao().unwrap_err();
10096        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
10097            panic!("expected EdicaoInvalid, got {err:?}");
10098        };
10099        assert_eq!(edicao, "2026 ");
10100    }
10101
10102    #[test]
10103    fn validate_edicao_rejects_leading_whitespace() {
10104        // Symmetric paste-from-doc whitespace footgun on the leading
10105        // boundary — the gate refuses every shape with a non-digit
10106        // byte by construction.
10107        let c = caixa_with_edicao(Some(" 2026"));
10108        let err = c.validate_edicao().unwrap_err();
10109        assert!(
10110            matches!(err, ManifestError::EdicaoInvalid { .. }),
10111            "got {err:?}",
10112        );
10113    }
10114
10115    #[test]
10116    fn validate_edicao_rejects_control_char() {
10117        // Paste-from-multiline-doc CRLF footgun — control characters
10118        // at the value boundary break the substrate's build-time
10119        // edition-selector parser. Peer with
10120        // `validate_repositorio_rejects_control_char`.
10121        let c = caixa_with_edicao(Some("2026\n"));
10122        let err = c.validate_edicao().unwrap_err();
10123        assert!(
10124            matches!(err, ManifestError::EdicaoInvalid { .. }),
10125            "got {err:?}",
10126        );
10127    }
10128
10129    #[test]
10130    fn validate_edicao_rejects_non_ascii_lookalike() {
10131        // Fullwidth-keyboard look-alike footgun — `"2026"` is
10132        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
10133        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
10134        // edition selector wants an ASCII year, and the gate
10135        // refuses every non-ASCII shape by construction (length in
10136        // bytes is 12 ≠ 4, *and* every byte falls outside
10137        // `is_ascii_digit`'s `0-9` range).
10138        let c = caixa_with_edicao(Some("2026"));
10139        let err = c.validate_edicao().unwrap_err();
10140        assert!(
10141            matches!(err, ManifestError::EdicaoInvalid { .. }),
10142            "got {err:?}",
10143        );
10144    }
10145
10146    #[test]
10147    fn validate_edicao_rejects_version_tag_prefix() {
10148        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
10149        // / `"r2026"` are familiar shapes from git-tag / Rust
10150        // edition / release-tag conventions that don't apply to
10151        // the year-shaped edition axis. The shape predicate refuses
10152        // every leading non-digit prefix.
10153        for ed in ["v2026", "e2026", "r2026"] {
10154            let c = caixa_with_edicao(Some(ed));
10155            let err = c.validate_edicao().unwrap_err();
10156            assert!(
10157                matches!(err, ManifestError::EdicaoInvalid { .. }),
10158                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10159            );
10160        }
10161    }
10162
10163    #[test]
10164    fn validate_edicao_rejects_decimal_shape() {
10165        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
10166        // `"2026.0"` are familiar shapes from semver / float
10167        // conventions that don't apply to the year-shaped edition
10168        // axis. The shape predicate refuses every non-digit byte
10169        // (`.` falls outside `is_ascii_digit`).
10170        for ed in ["2026.1", "2026.0", "2026.0.1"] {
10171            let c = caixa_with_edicao(Some(ed));
10172            let err = c.validate_edicao().unwrap_err();
10173            assert!(
10174                matches!(err, ManifestError::EdicaoInvalid { .. }),
10175                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10176            );
10177        }
10178    }
10179
10180    #[test]
10181    fn validate_edicao_rejects_wrong_length_numeric() {
10182        // Wrong-length numeric footgun — `"26"` (truncated) /
10183        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
10184        // (zero-padded too wide) all parse as integers but don't
10185        // name a 4-digit year. The shape predicate refuses every
10186        // value whose length isn't exactly 4 bytes.
10187        for ed in ["26", "202", "20260", "00026", "9"] {
10188            let c = caixa_with_edicao(Some(ed));
10189            let err = c.validate_edicao().unwrap_err();
10190            assert!(
10191                matches!(err, ManifestError::EdicaoInvalid { .. }),
10192                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10193            );
10194        }
10195    }
10196
10197    #[test]
10198    fn validate_edicao_empty_takes_precedence_over_shape() {
10199        // Empty-first cascade pin: the empty `Some("")` surfaces
10200        // the narrower `EdicaoEmpty` not the shape-predicate-
10201        // wrapped `EdicaoInvalid`, mirroring the peer
10202        // `validate_repositorio_empty_takes_precedence_over_shape`
10203        // (`RepositorioEmpty` → `RepositorioInvalid`),
10204        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
10205        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
10206        // cascades. The shape predicate also refuses the empty
10207        // input (defensively — `s.len() != 4`), but the
10208        // manifest-layer empty arm runs first to surface the
10209        // narrower diagnostic verbatim.
10210        let c = caixa_with_edicao(Some(""));
10211        let err = c.validate_edicao().unwrap_err();
10212        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
10213    }
10214
10215    #[test]
10216    fn validate_edicao_template_passes() {
10217        // Round-trip pin: the bare `Caixa::template` shape (which
10218        // carries `:edicao "2026"` verbatim) passes the gate by
10219        // construction. A future template-shape change that
10220        // introduced `(:edicao "")` or a non-year value would
10221        // surface here as a regression. Mirrors the peer
10222        // `validate_licenca_template_passes` pin.
10223        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10224        c.validate_edicao().unwrap();
10225    }
10226
10227    #[test]
10228    fn validate_edicao_diagnostic_names_offending_slot() {
10229        // Diagnostic-shape pin (peer with
10230        // `validate_licenca_diagnostic_names_offending_slot`): the
10231        // error's Display surfaces the `:edicao` slot name verbatim,
10232        // so a `feira lint` run can render the diagnostic without
10233        // re-parsing and the author can grep their caixa.lisp for
10234        // the offending `:edicao` line.
10235        let c = caixa_with_edicao(Some(""));
10236        let rendered = c.validate_edicao().unwrap_err().to_string();
10237        assert!(
10238            rendered.contains(":edicao"),
10239            "diagnostic must name the offending slot: {rendered}",
10240        );
10241    }
10242
10243    #[test]
10244    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
10245        // Diagnostic-shape pin on the shape-predicate arm (peer
10246        // with `validate_repositorio_diagnostic_carries_offending_value`):
10247        // the error's Display surfaces the offending value + slot
10248        // name verbatim, so a `feira lint` run can render the
10249        // diagnostic without re-parsing and the author can grep
10250        // their caixa.lisp for the offending `:edicao` value.
10251        let c = caixa_with_edicao(Some("v2026"));
10252        let rendered = c.validate_edicao().unwrap_err().to_string();
10253        assert!(
10254            rendered.contains(":edicao"),
10255            "diagnostic must name the offending slot: {rendered}",
10256        );
10257        assert!(
10258            rendered.contains("v2026"),
10259            "diagnostic must quote the offending value: {rendered}",
10260        );
10261    }
10262
10263    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
10264
10265    #[test]
10266    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
10267        // The canonical per-`Caixa` `:edicao` language-edition scalar
10268        // pin: [`Caixa::edicao`] must return the `:edicao` typed
10269        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10270        // raw `self.edicao.as_deref()` access across every representative
10271        // value in the accept-set — `None` (the "omit the slot to defer
10272        // to the substrate's default edition" arm every existing
10273        // [`caixa-resolver`] fixture without an `:edicao` line carries),
10274        // `Some("")` (a past-the-guard sentinel that pins the accessor
10275        // doesn't perform a silent `Some("") → None` collapse on the
10276        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
10277        // but the accessor must ship the raw slot verbatim so a
10278        // validate-time gate regression surfaces at any future edition-
10279        // aware consumer's boundary rather than being silently absorbed
10280        // into the substrate's default edition), `Some("2026")` (the
10281        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
10282        // template scaffolds via [`Caixa::template`] and every
10283        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
10284        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
10285        // carries by construction), `Some("2018")` / `Some("2021")` /
10286        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
10287        // peer with Cargo's `[package] edition` grammar every future-
10288        // introduced sibling to `"2026"` will follow), and eight
10289        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
10290        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
10291        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
10292        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
10293        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
10294        // length-numeric, `Some("latest")` free-form-non-year — the
10295        // sentinels pin the accessor doesn't silently absorb the
10296        // refusal cases into a substrate-default-edition fallback).
10297        //
10298        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
10299        // return scalar accessor pin on the substrate primitive —
10300        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
10301        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
10302        // (3f16e2f) pins that opened the "outer [`Caixa`]
10303        // `Option<&str>` scalar" projection pin pattern this pin folds
10304        // on. Sibling in shape to the peer per-`:placement`
10305        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10306        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10307        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10308        // axes, extended onto the outer top-level [`Caixa`] universal-
10309        // axis surface's last unlifted `Option<String>` slot. Pins
10310        // against a future silent detour that returned an owned
10311        // `Option<String>` (which would type-check but silently
10312        // allocate on every accessor call, breaking the zero-cost
10313        // projection every peer sibling accessor carries), a
10314        // `Some("") → None` collapse (which would silently absorb the
10315        // `EdicaoEmpty` refusal case at the accessor boundary and any
10316        // future edition-aware consumer would silently fall back to
10317        // the substrate's default edition on a struct-literal
10318        // `Caixa { edicao: Some(""), .. }`), or a
10319        // `None → Some("2026")` collapse (which would silently reify
10320        // the substrate's default edition at the accessor boundary
10321        // and every downstream consumer keying off the
10322        // `Option::is_none()` discriminator would lose the "author
10323        // omitted the slot" signal).
10324        for edicao in [
10325            None,
10326            Some(""),
10327            Some("2026"),
10328            Some("2018"),
10329            Some("2021"),
10330            Some("2024"),
10331            Some("2026 "),
10332            Some(" 2026"),
10333            Some("2026\n"),
10334            Some("2026"),
10335            Some("v2026"),
10336            Some("2026.1"),
10337            Some("26"),
10338            Some("latest"),
10339        ] {
10340            let c = caixa_with_edicao(edicao);
10341            assert_eq!(
10342                c.edicao(),
10343                edicao,
10344                "Caixa::edicao must return :edicao verbatim (got {:?}, \
10345                 expected {edicao:?})",
10346                c.edicao(),
10347            );
10348            assert_eq!(
10349                c.edicao(),
10350                c.edicao.as_deref(),
10351                "Caixa::edicao must byte-equal the raw \
10352                 `self.edicao.as_deref()` field access across every \
10353                 value in the Option<&str> accept-set",
10354            );
10355        }
10356    }
10357
10358    #[test]
10359    fn validate_edicao_empty_arm_routes_through_accessor() {
10360        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
10361        // must key off [`Caixa::edicao`], not the raw
10362        // `self.edicao.as_deref()` field access. Structurally: a
10363        // `Caixa { edicao: Some(""), .. }` must surface the
10364        // `EdicaoEmpty` refusal exactly, and a
10365        // `Caixa { edicao: Some("2026"), .. }` (the canonical
10366        // 4-digit-ASCII-decimal-year form) must pass validate. The
10367        // pair jointly pins the accessor + validate-gate composition:
10368        // any future silent detour that had the accessor return `None`
10369        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
10370        // would silently absorb the `EdicaoEmpty` refusal at the
10371        // accessor boundary and the validate gate would accept a
10372        // struct-literal `Caixa { edicao: Some(""), .. }` — the
10373        // composition pin catches that at caixa-core build time.
10374        //
10375        // Peer of the [`Caixa::licenca`] (6d5bc28)
10376        // `validate_licenca_empty_arm_routes_through_accessor`,
10377        // [`Caixa::repositorio`] (cc7332d)
10378        // `validate_repositorio_empty_arm_routes_through_accessor`,
10379        // and [`Caixa::descricao`] (3f16e2f)
10380        // `validate_descricao_empty_arm_routes_through_accessor`
10381        // composition pins on the sibling outer top-level [`Caixa`]
10382        // `Option<&str>` universal-axis surface — same "the validate /
10383        // shape-gate predicate must route through the substrate-
10384        // primitive typed dispatch" discipline extended onto the
10385        // fourth and final outer top-level [`Caixa`] universal-axis
10386        // `Option<&str>`-composition surface, closing the accessor-
10387        // composition family.
10388        let c = caixa_with_edicao(Some(""));
10389        assert!(
10390            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
10391            "validate_edicao must reject edicao == Some(\"\") with \
10392             EdicaoEmpty — the accessor and the validate gate must \
10393             route through the same substrate-primitive typed dispatch \
10394             on the :edicao empty arm",
10395        );
10396        let c = caixa_with_edicao(Some("2026"));
10397        assert!(
10398            c.validate_edicao().is_ok(),
10399            "validate_edicao must accept edicao == Some(\"2026\") \
10400             (the canonical 4-digit-ASCII-decimal-year shape)",
10401        );
10402    }
10403
10404    #[test]
10405    fn edicao_projects_option_str_by_borrow() {
10406        // The by-borrow pin: [`Caixa::edicao`] returns
10407        // `Option<&str>` by borrow — the `&str` borrows the underlying
10408        // `String` storage of the `Option<String>` slot and the
10409        // accessor must not allocate a fresh `String` on every call.
10410        // Peer of the [`Caixa::licenca`] (6d5bc28),
10411        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
10412        // (3f16e2f) by-borrow pins on the peer outer top-level
10413        // [`Caixa`] `Option<&str>`-return axes, and of the
10414        // per-`:placement`
10415        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10416        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
10417        // return axis, extended onto the fourth and final outer top-
10418        // level [`Caixa`] universal-axis `Option<&str>` shape — the
10419        // accessor's returned `&str` must borrow from `&self` (the
10420        // returned reference's lifetime is tied to `&self`), and
10421        // calling the accessor twice on the same [`Caixa`] must yield
10422        // the same `Option<&str>` verbatim (idempotent, no side
10423        // effects on `&self`).
10424        //
10425        // Pins against a future silent detour that returned an owned
10426        // `Option<String>` (which would type-check but silently
10427        // allocate on every call, breaking the zero-cost projection
10428        // every peer sibling accessor carries), or a one-arm-only
10429        // accessor that returned a saturating value on some sentinel
10430        // input (breaking the pass-through invariant the sibling
10431        // required-scalar accessors carry).
10432        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
10433            let c = caixa_with_edicao(edicao);
10434            let first = c.edicao();
10435            let second = c.edicao();
10436            assert_eq!(
10437                first, second,
10438                "Caixa::edicao must be idempotent — two successive \
10439                 calls on the same &self must return the same \
10440                 Option<&str>",
10441            );
10442            assert_eq!(
10443                first, edicao,
10444                "Caixa::edicao must return :edicao verbatim by \
10445                 borrow — got {first:?}, expected {edicao:?}",
10446            );
10447        }
10448    }
10449
10450    #[test]
10451    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
10452        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
10453        // label caixa-identity scalar pin: [`Caixa::nome`] must return
10454        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
10455        // the raw field access across every representative value in
10456        // the accept-set — the canonical `"demo"` template baseline
10457        // (the same `feira init`-scaffolded default the sibling
10458        // `validate_nome_accepts_canonical_template` positive-control
10459        // gate pins), plus every sibling per-typed-slot atom accessor's
10460        // canonical positive-arm byte-string (`"catalog"` per
10461        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
10462        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
10463        // `caixa-helm`/`caixa-flux` cross-crate integration-test
10464        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
10465        // canonical example), plus every past-the-guard sentinel for
10466        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
10467        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
10468        // the bare DNS-1123 63-byte cap but overflows the joint
10469        // `lareira-<nome>` chart-name budget the sibling
10470        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
10471        //
10472        // The past-the-guard sentinels pin the accessor doesn't
10473        // silently absorb the refusal cases into a template-derived
10474        // fallback (a future `.nome().is_empty().then(|| "demo")`
10475        // collapse would silently absorb the `NomeEmpty` refusal at
10476        // the accessor boundary and the validate gate would accept a
10477        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
10478        // catches that at caixa-core build time).
10479        //
10480        // First outer top-level [`Caixa`] `&str`-return required-
10481        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
10482        // required-scalar" projection pattern the sibling per-`Caixa`
10483        // `:versao` future lift folds on. Sibling in shape to the peer
10484        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
10485        // required-`String`-carry accessor pin on the sibling per-
10486        // sub-struct required-axis, extended onto the outer top-level
10487        // [`Caixa`] universal-axis required-`String`-carry axis.
10488        for nome in [
10489            "demo",
10490            "catalog",
10491            "cart",
10492            "hello-rio",
10493            "checkout",
10494            "",
10495            "Bad_Name",
10496            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
10497        ] {
10498            let c = caixa_with_nome(nome);
10499            assert_eq!(
10500                c.nome(),
10501                nome,
10502                "Caixa::nome must return :nome verbatim (got {}, \
10503                 expected {nome})",
10504                c.nome(),
10505            );
10506            assert_eq!(
10507                c.nome(),
10508                c.nome.as_str(),
10509                "Caixa::nome must byte-equal the raw .nome field \
10510                 access across every value in the String accept-set",
10511            );
10512        }
10513    }
10514
10515    #[test]
10516    fn validate_nome_empty_arm_routes_through_accessor() {
10517        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
10518        // key off [`Caixa::nome`], not the raw `.nome` field access.
10519        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
10520        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
10521        // template baseline (the peer positive-arm the sibling
10522        // `validate_nome_accepts_canonical_template` gate carves out)
10523        // must pass validate. The pair jointly pins the accessor +
10524        // validate-gate composition: any future silent detour that
10525        // had the accessor return a fresh `"demo"` on the empty arm
10526        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
10527        // would silently absorb the `NomeEmpty` refusal at the
10528        // accessor boundary and the validate gate would accept a
10529        // struct-literal `Caixa { nome: "".into(), .. }` — the
10530        // composition pin catches that at caixa-core build time.
10531        //
10532        // Peer of the sibling per-`Caixa`
10533        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
10534        // / `validate_repositorio_empty_arm_routes_through_accessor`
10535        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
10536        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
10537        // (2641cbd) composition pins on the sibling outer top-level
10538        // [`Caixa`] `Option<&str>` axes — same "the validate /
10539        // shape-gate predicate must route through the substrate-
10540        // primitive typed dispatch" discipline extended onto the peer
10541        // outer top-level [`Caixa`] required-`&str` composition axis.
10542        let c = caixa_with_nome("");
10543        assert!(
10544            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
10545            "validate_nome must reject nome == \"\" with NomeEmpty — \
10546             the accessor and the validate gate must route through the \
10547             same substrate-primitive typed dispatch on the :nome \
10548             empty-arm",
10549        );
10550        let c = caixa_with_nome("demo");
10551        assert!(
10552            c.validate_nome().is_ok(),
10553            "validate_nome must accept nome == \"demo\" (the canonical \
10554             DNS-1123-label template baseline)",
10555        );
10556    }
10557
10558    #[test]
10559    fn nome_projects_str_by_borrow() {
10560        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
10561        // — the `&str` borrows the underlying `String` storage of the
10562        // required `nome` slot and the accessor must not allocate a
10563        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
10564        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
10565        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
10566        // by-borrow pins on the peer outer top-level [`Caixa`]
10567        // `Option<&str>`-return axes, extended onto the first outer
10568        // top-level [`Caixa`] required-`&str`-return axis — the
10569        // accessor's returned `&str` must borrow from `&self` (the
10570        // returned reference's lifetime is tied to `&self`), and
10571        // calling the accessor twice on the same [`Caixa`] must yield
10572        // the same `&str` verbatim (idempotent, no side effects on
10573        // `&self`).
10574        //
10575        // Pins against a future silent detour that returned an owned
10576        // `String` (which would type-check but silently allocate on
10577        // every call, breaking the zero-cost projection every peer
10578        // sibling accessor carries), an accidental
10579        // `.nome.to_lowercase()` detour that returned a fresh
10580        // allocation through an already-DNS-1123-lowercase-only
10581        // string (breaking a future `const fn` regression), or a
10582        // one-arm-only accessor that returned a canonicalized value
10583        // on some sentinel input (breaking the pass-through invariant
10584        // the sibling required-scalar accessors carry).
10585        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
10586            let c = caixa_with_nome(nome);
10587            let first = c.nome();
10588            let second = c.nome();
10589            assert_eq!(
10590                first, second,
10591                "Caixa::nome must be idempotent — two successive calls \
10592                 on the same &self must return the same &str",
10593            );
10594            assert_eq!(
10595                first, nome,
10596                "Caixa::nome must return :nome verbatim by borrow — \
10597                 got {first}, expected {nome}",
10598            );
10599        }
10600    }
10601
10602    #[test]
10603    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
10604        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
10605        // pinned-version scalar pin: [`Caixa::versao`] must return the
10606        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
10607        // raw `.versao` field access across every representative value
10608        // in the accept-set — the canonical `"0.1.0"` template baseline
10609        // (the same `feira init`-scaffolded default the sibling
10610        // `validate_versao_accepts_canonical_template` positive-control
10611        // gate pins), plus every canonical SemVer-2 shape the sibling
10612        // `validate_versao_accepts_canonical_forms` positive-arm sweep
10613        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
10614        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
10615        // `"10.20.30"`), plus every past-the-guard sentinel for the
10616        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
10617        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
10618        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
10619        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
10620        // `"latest"` the docker-tag-shape footgun — the sentinels pin
10621        // the accessor doesn't silently absorb the refusal cases into a
10622        // template-derived fallback like `"0.1.0"`).
10623        //
10624        // The past-the-guard sentinels pin the accessor doesn't silently
10625        // absorb the refusal cases into a template-derived fallback (a
10626        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
10627        // silently absorb the `VersaoEmpty` refusal at the accessor
10628        // boundary and the validate gate would accept a struct-literal
10629        // `Caixa { versao: "".into(), .. }` — the pin catches that at
10630        // caixa-core build time).
10631        //
10632        // Second outer top-level [`Caixa`] `&str`-return required-scalar
10633        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
10634        // scalar" projection pattern the sibling per-`Caixa`
10635        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
10636        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
10637        // (4127bb6) / per-`:children`
10638        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
10639        // / per-`:upgrade-from`
10640        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
10641        // struct `:versao`-shaped `&str`-return accessor pins on the
10642        // sibling per-typed-slot version-carrier axes, extended onto the
10643        // second outer top-level [`Caixa`] universal-axis required-
10644        // `String`-carry axis so the two universal-axis identity-
10645        // carrying scalars every `defcaixa` form supplies (`:nome` +
10646        // `:versao`) share the same "one typed dispatch per axis" pin
10647        // discipline.
10648        for versao in [
10649            "0.1.0",
10650            "0.0.0",
10651            "1.0.0",
10652            "0.2.0-rc.1",
10653            "1.0.0-alpha.0",
10654            "1.0.0+build.42",
10655            "1.0.0-rc.1+build.42",
10656            "10.20.30",
10657            "",
10658            "v0.1.0",
10659            "0.1",
10660            "^0.1",
10661            "0.1.0.0",
10662            "latest",
10663        ] {
10664            let c = caixa_with_versao(versao);
10665            assert_eq!(
10666                c.versao(),
10667                versao,
10668                "Caixa::versao must return :versao verbatim (got {}, \
10669                 expected {versao})",
10670                c.versao(),
10671            );
10672            assert_eq!(
10673                c.versao(),
10674                c.versao.as_str(),
10675                "Caixa::versao must byte-equal the raw .versao field \
10676                 access across every value in the String accept-set",
10677            );
10678        }
10679    }
10680
10681    #[test]
10682    fn validate_versao_empty_arm_routes_through_accessor() {
10683        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
10684        // must key off [`Caixa::versao`], not the raw `.versao` field
10685        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
10686        // surface the `VersaoEmpty` refusal exactly, and the canonical
10687        // `"0.1.0"` template baseline (the peer positive-arm the sibling
10688        // `validate_versao_accepts_canonical_template` gate carves out)
10689        // must pass validate. The pair jointly pins the accessor +
10690        // validate-gate composition: any future silent detour that had
10691        // the accessor return a fresh `"0.1.0"` on the empty arm
10692        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
10693        // would silently absorb the `VersaoEmpty` refusal at the
10694        // accessor boundary and the validate gate would accept a
10695        // struct-literal `Caixa { versao: "".into(), .. }` — the
10696        // composition pin catches that at caixa-core build time.
10697        //
10698        // Peer of the sibling per-`Caixa`
10699        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
10700        // composition pin on the sibling outer top-level [`Caixa`]
10701        // required-`&str` universal-axis surface — same "the validate /
10702        // shape-gate predicate must route through the substrate-
10703        // primitive typed dispatch" discipline extended onto the peer
10704        // outer top-level [`Caixa`] required-`&str` universal-axis
10705        // pinned-version composition axis, closing the second
10706        // coordinate of the "one canonical typed dispatch per per-Caixa
10707        // required-`&str` universal-axis" discipline.
10708        let c = caixa_with_versao("");
10709        assert!(
10710            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
10711            "validate_versao must reject versao == \"\" with VersaoEmpty — \
10712             the accessor and the validate gate must route through the \
10713             same substrate-primitive typed dispatch on the :versao \
10714             empty-arm",
10715        );
10716        let c = caixa_with_versao("0.1.0");
10717        assert!(
10718            c.validate_versao().is_ok(),
10719            "validate_versao must accept versao == \"0.1.0\" (the \
10720             canonical SemVer-2 template baseline)",
10721        );
10722    }
10723
10724    #[test]
10725    fn versao_projects_str_by_borrow() {
10726        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
10727        // — the `&str` borrows the underlying `String` storage of the
10728        // required `versao` slot and the accessor must not allocate a
10729        // fresh `String` on every call. Peer of the [`Caixa::nome`]
10730        // (e6b7d97) by-borrow pin on the sibling outer top-level
10731        // [`Caixa`] required-`&str`-return axis, extended onto the
10732        // second outer top-level [`Caixa`] required-`&str`-return
10733        // universal-axis pinned-version surface — the accessor's
10734        // returned `&str` must borrow from `&self` (the returned
10735        // reference's lifetime is tied to `&self`), and calling the
10736        // accessor twice on the same [`Caixa`] must yield the same
10737        // `&str` verbatim (idempotent, no side effects on `&self`).
10738        //
10739        // Pins against a future silent detour that returned an owned
10740        // `String` (which would type-check but silently allocate on
10741        // every call, breaking the zero-cost projection every peer
10742        // sibling accessor carries), an accidental
10743        // `semver::Version::parse(&self.versao).unwrap().to_string()`
10744        // detour that returned a canonicalized fresh allocation through
10745        // an already-canonical byte-string (breaking a future `const fn`
10746        // regression and silently absorbing the `VersaoInvalid` refusal
10747        // at the accessor boundary), or a one-arm-only accessor that
10748        // returned a canonicalized value on some sentinel input
10749        // (breaking the pass-through invariant the sibling required-
10750        // scalar accessors carry).
10751        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
10752            let c = caixa_with_versao(versao);
10753            let first = c.versao();
10754            let second = c.versao();
10755            assert_eq!(
10756                first, second,
10757                "Caixa::versao must be idempotent — two successive \
10758                 calls on the same &self must return the same &str",
10759            );
10760            assert_eq!(
10761                first, versao,
10762                "Caixa::versao must return :versao verbatim by borrow \
10763                 — got {first}, expected {versao}",
10764            );
10765        }
10766    }
10767
10768    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
10769        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10770        c.kind = kind;
10771        c
10772    }
10773
10774    #[test]
10775    fn kind_returns_kind_variant_verbatim_across_permutations() {
10776        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
10777        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
10778        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
10779        // the raw `.kind` field access across every variant in the
10780        // closed accept-set (`Biblioteca` — the library kind that
10781        // exports lisp forms; `Binario` — the nix-built executable kind
10782        // under `exe/`; `Servico` — the wasm-component daemon kind
10783        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
10784        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
10785        // composition kind).
10786        //
10787        // Pins against a future silent detour that re-derived the kind
10788        // from a peer axis (an accidental fallback to
10789        // `if !servicos.is_empty() { Servico } else if
10790        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
10791        // collapse that read the code-surface / mesh-slot columns into
10792        // the kind discriminator), a variant remap the operator
10793        // authors on one consumer without the other, or a stale-derive
10794        // detour that substituted [`CaixaKind::Biblioteca`] as the
10795        // default when the field held any other variant (which would
10796        // silently collapse the distinction between "author explicitly
10797        // declared `:kind Servico`" and "author declared any other
10798        // kind" every downstream renderer-dispatch site depends on).
10799        //
10800        // First outer top-level [`Caixa`] `Copy`-return required-enum-
10801        // discriminant accessor pin — opens the "outer [`Caixa`]
10802        // `Copy`-return required-discriminant" projection pattern.
10803        // Sibling in shape to the peer per-`:supervisor`
10804        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
10805        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
10806        // (921fe1b), and per-`:children`
10807        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
10808        // `Copy`-return closed-set-enum discriminant accessor pins on
10809        // the sibling nested-spec typed-slot discriminator axes,
10810        // extended here to the outer top-level [`Caixa`] universal-
10811        // axis surface.
10812        for kind in [
10813            CaixaKind::Biblioteca,
10814            CaixaKind::Binario,
10815            CaixaKind::Servico,
10816            CaixaKind::Supervisor,
10817            CaixaKind::Aplicacao,
10818        ] {
10819            let c = caixa_with_kind(kind);
10820            assert_eq!(
10821                c.kind(),
10822                kind,
10823                "Caixa::kind must return :kind verbatim (got {:?}, \
10824                 expected {kind:?})",
10825                c.kind(),
10826            );
10827            assert_eq!(
10828                c.kind(),
10829                c.kind,
10830                "Caixa::kind accessor and .kind field access must \
10831                 byte-equal — the accessor is the substrate-primitive \
10832                 typed dispatch every downstream kind-gate consumer \
10833                 must route through",
10834            );
10835        }
10836    }
10837
10838    #[test]
10839    fn require_kind_reads_through_lifted_kind_accessor() {
10840        // Two-consumer coherence pin: the [`crate::render::require_kind`]
10841        // entry-gate predicate (the canonical two-line
10842        // `require_kind(caixa, Servico)?` prelude every per-Servico /
10843        // per-Aplicacao renderer runs at its entry-point) and the
10844        // sibling [`crate::render::KindMismatch`] error carrier's
10845        // `actual:` field (which names the offending caixa's variant
10846        // in the diagnostic) must both key off the lifted accessor, so
10847        // any future rebrand on the typed slot's reader shape lands at
10848        // exactly one place. Pins the two-site coherence by exercising
10849        // every off-diagonal `(actual, expected)` pair across the
10850        // closed accept-set — the `KindMismatch { actual, expected }`
10851        // surfaced on the mismatch arm must byte-equal the pair the
10852        // accessor returns for each side.
10853        //
10854        // Peer of the sibling per-`:placement`
10855        // `validate_placement_reads_through_lifted_estrategia_accessor`
10856        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
10857        // `Copy`-return discriminant axis — same "the entry-gate
10858        // predicate and the error carrier's `actual:` field must route
10859        // through the substrate-primitive typed dispatch" discipline
10860        // extended onto the outer top-level [`Caixa`] universal-axis
10861        // discriminant surface.
10862        for expected in [
10863            CaixaKind::Biblioteca,
10864            CaixaKind::Binario,
10865            CaixaKind::Servico,
10866            CaixaKind::Supervisor,
10867            CaixaKind::Aplicacao,
10868        ] {
10869            for actual in [
10870                CaixaKind::Biblioteca,
10871                CaixaKind::Binario,
10872                CaixaKind::Servico,
10873                CaixaKind::Supervisor,
10874                CaixaKind::Aplicacao,
10875            ] {
10876                let c = caixa_with_kind(actual);
10877                let result = crate::render::require_kind(&c, expected);
10878                if expected == actual {
10879                    assert!(
10880                        result.is_ok(),
10881                        "require_kind must accept when actual == expected \
10882                         (actual={actual:?}, expected={expected:?})",
10883                    );
10884                } else {
10885                    let err = result.expect_err("require_kind must reject when actual != expected");
10886                    assert_eq!(
10887                        err.actual,
10888                        c.kind(),
10889                        "KindMismatch.actual must byte-equal Caixa::kind() \
10890                         — the error carrier's `actual:` field reads \
10891                         through the lifted accessor",
10892                    );
10893                    assert_eq!(
10894                        err.expected, expected,
10895                        "KindMismatch.expected must byte-equal the \
10896                         expected variant passed to require_kind",
10897                    );
10898                }
10899            }
10900        }
10901    }
10902
10903    #[test]
10904    fn aplicacao_view_kind_gate_routes_through_accessor() {
10905        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
10906        // must key off [`Caixa::kind`], not the raw `.kind` field
10907        // access. Structurally: a `Caixa { kind: X, .. }` for any
10908        // non-`Aplicacao` variant must fold to `None` on the
10909        // `aplicacao_view` composer (the "kind mismatch → no typed
10910        // view" contract every downstream Aplicacao consumer keys off
10911        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
10912        // `Some(_)`. The pair jointly pins the accessor + view-gate
10913        // composition: any future silent detour that had the accessor
10914        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
10915        // input would silently absorb the kind-mismatch case at the
10916        // accessor boundary and every per-Aplicacao renderer would
10917        // silently render a non-Aplicacao caixa's mesh slots — the
10918        // composition pin catches that at caixa-core build time.
10919        //
10920        // Peer of the sibling per-`Caixa`
10921        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
10922        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
10923        // composition pins on the sibling outer top-level [`Caixa`]
10924        // required-`&str` universal-axis surfaces — same "the
10925        // composer / validate gate must route through the substrate-
10926        // primitive typed dispatch" discipline extended onto the
10927        // outer top-level [`Caixa`] `Copy`-return required-
10928        // discriminant composition axis.
10929        for kind in [
10930            CaixaKind::Biblioteca,
10931            CaixaKind::Binario,
10932            CaixaKind::Servico,
10933            CaixaKind::Supervisor,
10934        ] {
10935            let c = caixa_with_kind(kind);
10936            assert!(
10937                c.aplicacao_view().is_none(),
10938                "aplicacao_view must return None on non-Aplicacao \
10939                 kind {kind:?} — the composer's kind-gate must route \
10940                 through Caixa::kind()",
10941            );
10942        }
10943        let c = caixa_with_kind(CaixaKind::Aplicacao);
10944        assert!(
10945            c.aplicacao_view().is_some(),
10946            "aplicacao_view must return Some on kind Aplicacao — \
10947             the composer's kind-gate must accept the matching arm \
10948             through Caixa::kind()",
10949        );
10950    }
10951
10952    #[test]
10953    fn supervisor_view_kind_gate_routes_through_accessor() {
10954        // Composition pin (mirror of the sibling
10955        // `aplicacao_view_kind_gate_routes_through_accessor` on the
10956        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
10957        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
10958        // field access. A `Caixa { kind: X, .. }` for any non-
10959        // `Supervisor` variant must fold to `None` on the
10960        // `supervisor_view` composer, and a `Caixa { kind:
10961        // Supervisor, .. }` must fold to `Some(_)`. Same peer
10962        // composition pin discipline on the second `_view` composer
10963        // axis.
10964        for kind in [
10965            CaixaKind::Biblioteca,
10966            CaixaKind::Binario,
10967            CaixaKind::Servico,
10968            CaixaKind::Aplicacao,
10969        ] {
10970            let c = caixa_with_kind(kind);
10971            assert!(
10972                c.supervisor_view().is_none(),
10973                "supervisor_view must return None on non-Supervisor \
10974                 kind {kind:?} — the composer's kind-gate must route \
10975                 through Caixa::kind()",
10976            );
10977        }
10978        let mut c = caixa_with_kind(CaixaKind::Supervisor);
10979        // A Supervisor caixa needs a strategy + at least one child to
10980        // fold to a Some(_) that also validates; the composer itself
10981        // requires only the kind arm, so bare kind flip is enough to
10982        // pin the `Some(_)` return, but we populate the minimum
10983        // supervisor shape so a future strengthening of the composer
10984        // to reject an empty spec doesn't false-positive this pin.
10985        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
10986        c.children = vec![crate::supervisor::ChildSpec {
10987            caixa: "child".into(),
10988            versao: "^0.1".into(),
10989            restart: crate::supervisor::RestartPolicy::Permanent,
10990        }];
10991        assert!(
10992            c.supervisor_view().is_some(),
10993            "supervisor_view must return Some on kind Supervisor — \
10994             the composer's kind-gate must accept the matching arm \
10995             through Caixa::kind()",
10996        );
10997    }
10998
10999    #[test]
11000    fn kind_projects_by_copy() {
11001        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
11002        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
11003        // `&self` (the returned value is owned, `Copy`-projected from
11004        // the underlying [`CaixaKind`] storage; two calls on the same
11005        // [`Caixa`] must yield byte-equal values). Peer of the peer
11006        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
11007        // `SupervisorSpec::estrategia` / per-`:children`
11008        // `ChildSpec::restart` `Copy`-return discriminant accessor
11009        // pins on the sibling nested-spec typed-slot discriminator
11010        // axes, extended onto the first outer top-level [`Caixa`]
11011        // required-`Copy`-return axis — pins against a future silent
11012        // detour that returned `&CaixaKind` (which would type-check
11013        // but silently constrain every consumer's callsite to a
11014        // borrow-shaped dispatch, breaking the zero-cost `Copy`
11015        // projection every peer sibling accessor carries).
11016        for kind in [
11017            CaixaKind::Biblioteca,
11018            CaixaKind::Binario,
11019            CaixaKind::Servico,
11020            CaixaKind::Supervisor,
11021            CaixaKind::Aplicacao,
11022        ] {
11023            let c = caixa_with_kind(kind);
11024            let first: CaixaKind = c.kind();
11025            let second: CaixaKind = c.kind();
11026            assert_eq!(
11027                first, second,
11028                "Caixa::kind must be idempotent — two successive \
11029                 calls on the same &self must return the same \
11030                 CaixaKind variant",
11031            );
11032            assert_eq!(
11033                first, kind,
11034                "Caixa::kind must return :kind verbatim by Copy — \
11035                 got {first:?}, expected {kind:?}",
11036            );
11037        }
11038    }
11039
11040    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
11041
11042    #[test]
11043    fn autores_returns_autores_slice_verbatim_across_permutations() {
11044        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
11045        // name-list slice pin: [`Caixa::autores`] must return the
11046        // `:autores` typed [`Vec<String>`] list verbatim as a
11047        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
11048        // access across every representative value in the accept-set —
11049        // `[]` (the "no maintainers declared" arm every existing
11050        // fixture without an `:autores` line carries), `[""]` (a past-
11051        // the-guard sentinel that pins the accessor doesn't perform a
11052        // silent `[""] → []` collapse on the empty-entry arm — validate
11053        // rejects `[""]` through `AutorEmpty` but the accessor must
11054        // ship the raw slot verbatim so a validate-time gate regression
11055        // surfaces at the caixa-helm emit boundary rather than being
11056        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
11057        // canonical single-maintainer form every `feira init` template
11058        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
11059        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
11060        // (the canonical RFC-5322 `<name> <email>` form the
11061        // `is_chart_maintainer_name_shape` predicate accepts), and
11062        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
11063        // sentinel — validate rejects through `AutorDuplicate` but the
11064        // accessor must ship the raw slot verbatim).
11065        //
11066        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
11067        // pin on the substrate primitive — opens the "outer [`Caixa`]
11068        // `&[T]` slice" projection pattern the sibling per-`Caixa`
11069        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
11070        // / `:servicos` / `:upgrade-from` / `:children` future lifts
11071        // fold on. Sibling in shape to the peer per-`:supervisor`
11072        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
11073        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
11074        // (a6e18d7), per-`:membros`
11075        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
11076        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
11077        // (0dcc926), and per-`:upgrade-from :instructions`
11078        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
11079        // `&[T]`-return slice accessor pins on the sibling per-M2 /
11080        // per-M3 typed-slot list axes, extended onto the outer top-
11081        // level [`Caixa`] universal-axis surface. Pins against a future
11082        // silent detour that returned an owned `Vec<String>` (which
11083        // would type-check but silently clone on every accessor call,
11084        // breaking the zero-cost projection every peer sibling slice
11085        // accessor carries), a `[""] → []` collapse (which would
11086        // silently absorb the `AutorEmpty` refusal case at the accessor
11087        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
11088        // would silently absorb the `AutorDuplicate` refusal case at
11089        // the accessor boundary and the caixa-helm `maintainers:` fold
11090        // would silently render a dedupped list on a struct-literal
11091        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
11092        for autores in [
11093            vec![],
11094            vec![""],
11095            vec!["pleme-io"],
11096            vec!["alice", "bob"],
11097            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
11098            vec!["pleme-io", "pleme-io"],
11099        ] {
11100            let c = caixa_with_autores(autores.clone());
11101            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
11102            assert_eq!(
11103                c.autores(),
11104                expected.as_slice(),
11105                "Caixa::autores must return :autores verbatim (got {:?}, \
11106                 expected {expected:?})",
11107                c.autores(),
11108            );
11109            assert_eq!(
11110                c.autores(),
11111                c.autores.as_slice(),
11112                "Caixa::autores must byte-equal the raw \
11113                 `self.autores.as_slice()` field access across every \
11114                 value in the Vec<String> accept-set",
11115            );
11116        }
11117    }
11118
11119    #[test]
11120    fn validate_autores_empty_entry_arm_routes_through_accessor() {
11121        // Composition pin: [`Caixa::validate_autores`]'s per-entry
11122        // empty-arm gate must key off [`Caixa::autores`], not the raw
11123        // `&self.autores` field-borrow walk. Structurally: a
11124        // `Caixa { autores: vec!["".into()], .. }` must surface the
11125        // `AutorEmpty` refusal exactly, and a
11126        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
11127        // canonical single-maintainer form) must pass validate. The
11128        // pair jointly pins the accessor + validate-gate composition:
11129        // any future silent detour that had the accessor return an
11130        // empty slice on the `[""]` arm (a
11131        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
11132        // would silently absorb the `AutorEmpty` refusal at the
11133        // accessor boundary and the validate gate would accept a
11134        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
11135        // the composition pin catches that at caixa-core build time.
11136        //
11137        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
11138        // accessor-composition pin
11139        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
11140        // sibling `Option<&str>`-composition axis and the
11141        // per-`:politicas :circuit-breaker`
11142        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
11143        // accessor-composition pin
11144        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
11145        // on the sibling required-`u32`-composition axis — same "the
11146        // validate / shape-gate predicate must route through the
11147        // substrate-primitive typed dispatch" discipline extended onto
11148        // the outer top-level [`Caixa`] universal-axis `&[T]`-
11149        // composition surface.
11150        let c = caixa_with_autores(vec![""]);
11151        assert!(
11152            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
11153            "validate_autores must reject autores == vec![\"\"] with \
11154             AutorEmpty — the accessor and the validate gate must \
11155             route through the same substrate-primitive typed dispatch \
11156             on the :autores per-entry empty arm",
11157        );
11158        let c = caixa_with_autores(vec!["pleme-io"]);
11159        assert!(
11160            c.validate_autores().is_ok(),
11161            "validate_autores must accept autores == vec![\"pleme-io\"] \
11162             (the canonical single-maintainer shape every `feira init` \
11163             template scaffolds)",
11164        );
11165    }
11166
11167    #[test]
11168    fn autores_projects_slice_by_borrow() {
11169        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
11170        // borrow — the returned slice borrows the underlying
11171        // `Vec<String>` storage of the `:autores` slot and the
11172        // accessor must not clone the backing `Vec` on every call.
11173        // Peer of the per-`:membros`
11174        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
11175        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
11176        // (0dcc926) / per-`:placement`
11177        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
11178        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
11179        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
11180        // typed-slot `&[T]`-return axes, extended onto the outer top-
11181        // level [`Caixa`] universal-axis `&[String]` shape — the
11182        // accessor's returned slice must borrow from `&self` (the
11183        // returned reference's lifetime is tied to `&self`), and
11184        // calling the accessor twice on the same [`Caixa`] must yield
11185        // slices that are pointer-equal (the underlying byte-buffer is
11186        // the storage `Vec`'s allocation, not a fresh copy) as well as
11187        // value-equal (idempotent, no side effects on `&self`).
11188        //
11189        // Pins against a future silent detour that returned an owned
11190        // `Vec<String>` (which would type-check but silently clone on
11191        // every call, breaking the zero-cost projection every peer
11192        // sibling slice accessor carries), a `&Vec<String>` return
11193        // (which would leak the backing `Vec`'s grow/push/reserve
11194        // surface no downstream consumer reaches for), or a one-arm-
11195        // only accessor that returned a saturating value on some
11196        // sentinel input (breaking the pass-through invariant the
11197        // sibling slice accessors carry).
11198        for autores in [
11199            vec![],
11200            vec!["pleme-io"],
11201            vec!["alice", "bob"],
11202            vec!["pleme-io", "pleme-io"],
11203        ] {
11204            let c = caixa_with_autores(autores.clone());
11205            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
11206            let first = c.autores();
11207            let second = c.autores();
11208            assert_eq!(
11209                first, second,
11210                "Caixa::autores must be idempotent — two successive \
11211                 calls on the same &self must return the same \
11212                 &[String]",
11213            );
11214            assert_eq!(
11215                first.as_ptr(),
11216                second.as_ptr(),
11217                "Caixa::autores must borrow the underlying Vec<String> \
11218                 storage — two successive calls must return slices \
11219                 with the same backing pointer (a fresh Vec<String> \
11220                 clone would change the pointer on every call)",
11221            );
11222            assert_eq!(
11223                first,
11224                expected.as_slice(),
11225                "Caixa::autores must return :autores verbatim by \
11226                 borrow — got {first:?}, expected {expected:?}",
11227            );
11228        }
11229    }
11230
11231    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
11232
11233    #[test]
11234    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
11235        // The canonical per-`Caixa` `:etiquetas` universal-axis
11236        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
11237        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
11238        // as a `&[String]`, byte-equal to the raw
11239        // `self.etiquetas.as_slice()` access across every representative
11240        // value in the accept-set — `[]` (the "no tags declared" arm
11241        // every existing fixture without an `:etiquetas` line carries),
11242        // `[""]` (a past-the-guard sentinel that pins the accessor
11243        // doesn't perform a silent `[""] → []` collapse on the empty-
11244        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
11245        // but the accessor must ship the raw slot verbatim so a
11246        // validate-time gate regression surfaces at the caixa-helm emit
11247        // boundary rather than being silently absorbed into a keyword-
11248        // drop), `["demo"]` (the canonical single-tag form every
11249        // `feira init` template scaffolds), `["example", "aplicacao",
11250        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
11251        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
11252        // (a past-the-guard duplicate sentinel — validate rejects
11253        // through `EtiquetaDuplicate` but the accessor must ship the
11254        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
11255        // at chart-render time isn't silently promoted into the
11256        // accessor boundary and struct-literal
11257        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
11258        // fixtures continue to expose the duplicate at the accessor).
11259        //
11260        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
11261        // pin on the substrate primitive — folds on the "outer
11262        // [`Caixa`] `&[T]` slice" projection pattern
11263        // `autores_returns_autores_slice_verbatim_across_permutations`
11264        // (b5d813f) opened, sibling in shape and idiom. Pins against a
11265        // future silent detour that returned an owned `Vec<String>`
11266        // (which would type-check but silently clone on every accessor
11267        // call, breaking the zero-cost projection every peer sibling
11268        // slice accessor carries), a `[""] → []` collapse (which would
11269        // silently absorb the `EtiquetaEmpty` refusal case at the
11270        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
11271        // (which would silently absorb the `EtiquetaDuplicate` refusal
11272        // case at the accessor boundary — the caixa-helm chart-render
11273        // `BTreeSet::collect` dedup is downstream of the accessor and
11274        // must not be silently promoted into it).
11275        for etiquetas in [
11276            vec![],
11277            vec![""],
11278            vec!["demo"],
11279            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
11280            vec!["demo", "demo"],
11281        ] {
11282            let c = caixa_with_etiquetas(etiquetas.clone());
11283            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
11284            assert_eq!(
11285                c.etiquetas(),
11286                expected.as_slice(),
11287                "Caixa::etiquetas must return :etiquetas verbatim (got \
11288                 {:?}, expected {expected:?})",
11289                c.etiquetas(),
11290            );
11291            assert_eq!(
11292                c.etiquetas(),
11293                c.etiquetas.as_slice(),
11294                "Caixa::etiquetas must byte-equal the raw \
11295                 `self.etiquetas.as_slice()` field access across every \
11296                 value in the Vec<String> accept-set",
11297            );
11298        }
11299    }
11300
11301    #[test]
11302    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
11303        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
11304        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
11305        // `&self.etiquetas` field-borrow walk. Structurally: a
11306        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
11307        // `EtiquetaEmpty` refusal exactly, and a
11308        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
11309        // single-tag form) must pass validate. The pair jointly pins
11310        // the accessor + validate-gate composition: any future silent
11311        // detour that had the accessor return an empty slice on the
11312        // `[""]` arm (a
11313        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
11314        // silently absorb the `EtiquetaEmpty` refusal at the accessor
11315        // boundary and the validate gate would accept a struct-literal
11316        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
11317        // pin catches that at caixa-core build time.
11318        //
11319        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
11320        // through_accessor` (b5d813f) accessor-composition pin on the
11321        // sibling `&[T]`-composition axis — same "the validate / shape-
11322        // gate predicate must route through the substrate-primitive
11323        // typed dispatch" discipline extended onto the sibling outer
11324        // top-level [`Caixa`] `&[T]`-composition surface.
11325        let c = caixa_with_etiquetas(vec![""]);
11326        assert!(
11327            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
11328            "validate_etiquetas must reject etiquetas == vec![\"\"] \
11329             with EtiquetaEmpty — the accessor and the validate gate \
11330             must route through the same substrate-primitive typed \
11331             dispatch on the :etiquetas per-entry empty arm",
11332        );
11333        let c = caixa_with_etiquetas(vec!["demo"]);
11334        assert!(
11335            c.validate_etiquetas().is_ok(),
11336            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
11337             (the canonical single-tag shape every `feira init` \
11338             template scaffolds)",
11339        );
11340    }
11341
11342    #[test]
11343    fn etiquetas_projects_slice_by_borrow() {
11344        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
11345        // by borrow — the returned slice borrows the underlying
11346        // `Vec<String>` storage of the `:etiquetas` slot and the
11347        // accessor must not clone the backing `Vec` on every call.
11348        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
11349        // (b5d813f) by-borrow pin on the sibling outer top-level
11350        // [`Caixa`] `&[String]`-return axis — the accessor's returned
11351        // slice must borrow from `&self` (the returned reference's
11352        // lifetime is tied to `&self`), and calling the accessor twice
11353        // on the same [`Caixa`] must yield slices that are pointer-
11354        // equal (the underlying byte-buffer is the storage `Vec`'s
11355        // allocation, not a fresh copy) as well as value-equal
11356        // (idempotent, no side effects on `&self`).
11357        //
11358        // Pins against a future silent detour that returned an owned
11359        // `Vec<String>` (which would type-check but silently clone on
11360        // every call, breaking the zero-cost projection every peer
11361        // sibling slice accessor carries), a `&Vec<String>` return
11362        // (which would leak the backing `Vec`'s grow/push/reserve
11363        // surface no downstream consumer reaches for), or a one-arm-
11364        // only accessor that returned a saturating value on some
11365        // sentinel input (breaking the pass-through invariant the
11366        // sibling slice accessors carry).
11367        for etiquetas in [
11368            vec![],
11369            vec!["demo"],
11370            vec!["example", "aplicacao", "mesh"],
11371            vec!["demo", "demo"],
11372        ] {
11373            let c = caixa_with_etiquetas(etiquetas.clone());
11374            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
11375            let first = c.etiquetas();
11376            let second = c.etiquetas();
11377            assert_eq!(
11378                first, second,
11379                "Caixa::etiquetas must be idempotent — two successive \
11380                 calls on the same &self must return the same \
11381                 &[String]",
11382            );
11383            assert_eq!(
11384                first.as_ptr(),
11385                second.as_ptr(),
11386                "Caixa::etiquetas must borrow the underlying \
11387                 Vec<String> storage — two successive calls must \
11388                 return slices with the same backing pointer (a fresh \
11389                 Vec<String> clone would change the pointer on every \
11390                 call)",
11391            );
11392            assert_eq!(
11393                first,
11394                expected.as_slice(),
11395                "Caixa::etiquetas must return :etiquetas verbatim by \
11396                 borrow — got {first:?}, expected {expected:?}",
11397            );
11398        }
11399    }
11400
11401    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
11402
11403    #[test]
11404    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
11405        // The canonical per-`Caixa` `:bibliotecas` universal-axis
11406        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
11407        // must return the `:bibliotecas` typed [`Vec<String>`] list
11408        // verbatim as a `&[String]`, byte-equal to the raw
11409        // `self.bibliotecas.as_slice()` access across every
11410        // representative value in the accept-set — `[]` (the "no
11411        // libraries declared" arm every `:kind` other than `Biblioteca`
11412        // + every `Biblioteca` relying on the canonical
11413        // `lib/<nome>.lisp` implicit-default path carries; the
11414        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
11415        // fires exactly on this empty-slot + `Biblioteca`-kind
11416        // combination), `[""]` (a past-the-guard sentinel that pins
11417        // the accessor doesn't perform a silent `[""] → []` collapse
11418        // on the empty-entry arm — validate rejects `[""]` through
11419        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
11420        // must ship the raw slot verbatim so a validate-time gate
11421        // regression surfaces at the `feira build` phase-1 parse
11422        // boundary rather than being silently absorbed into a
11423        // library-drop), `["lib/demo.lisp"]` (the canonical single-
11424        // entry form `Caixa::template` scaffolds and every `feira init`
11425        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
11426        // (the canonical multi-library form the
11427        // `validate_code_paths_accepts_explicit_relative_paths_on_
11428        // every_slot` fixture emits), and `["lib/foo.lisp",
11429        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
11430        // validate rejects through `CodePathDuplicate { slot:
11431        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
11432        // but the accessor must ship the raw slot verbatim so the
11433        // `feira build` `for entry in caixa.bibliotecas()` parse walk
11434        // sees the duplicate at the accessor boundary and struct-
11435        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
11436        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
11437        // the duplicate at the accessor).
11438        //
11439        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
11440        // pin on the substrate primitive — folds on the "outer
11441        // [`Caixa`] `&[T]` slice" projection pattern
11442        // `autores_returns_autores_slice_verbatim_across_permutations`
11443        // (b5d813f) opened and
11444        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
11445        // (78c7d3c) folded on, sibling in shape and idiom. Pins
11446        // against a future silent detour that returned an owned
11447        // `Vec<String>` (which would type-check but silently clone on
11448        // every accessor call, breaking the zero-cost projection
11449        // every peer sibling slice accessor carries), a `[""] → []`
11450        // collapse (which would silently absorb the `CodePathEmpty`
11451        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
11452        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
11453        // would silently absorb the `CodePathDuplicate` refusal case
11454        // at the accessor boundary — the per-slot set-not-multiset
11455        // gate is downstream of the accessor and must not be silently
11456        // promoted into it).
11457        for bibliotecas in [
11458            vec![],
11459            vec![""],
11460            vec!["lib/demo.lisp"],
11461            vec!["lib/demo.lisp", "lib/helpers.lisp"],
11462            vec!["lib/foo.lisp", "lib/foo.lisp"],
11463        ] {
11464            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
11465            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
11466            assert_eq!(
11467                c.bibliotecas(),
11468                expected.as_slice(),
11469                "Caixa::bibliotecas must return :bibliotecas verbatim \
11470                 (got {:?}, expected {expected:?})",
11471                c.bibliotecas(),
11472            );
11473            assert_eq!(
11474                c.bibliotecas(),
11475                c.bibliotecas.as_slice(),
11476                "Caixa::bibliotecas must byte-equal the raw \
11477                 `self.bibliotecas.as_slice()` field access across \
11478                 every value in the Vec<String> accept-set",
11479            );
11480        }
11481    }
11482
11483    #[test]
11484    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
11485        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
11486        // empty-arm gate on the `:bibliotecas` slot must key off
11487        // [`Caixa::bibliotecas`], not a divergent raw
11488        // `&self.bibliotecas` field-borrow walk. Structurally: a
11489        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
11490        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
11491        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
11492        // into()], .. }` (the canonical single-library form
11493        // `Caixa::template` scaffolds) must pass validate. The pair
11494        // jointly pins the accessor + validate-gate composition: any
11495        // future silent detour that had the accessor return an empty
11496        // slice on the `[""]` arm (a `.iter().filter(|s|
11497        // !s.is_empty()).collect()` collapse) would silently absorb
11498        // the `CodePathEmpty` refusal at the accessor boundary and
11499        // the validate gate would accept a struct-literal
11500        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
11501        // composition pin catches that at caixa-core build time.
11502        //
11503        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
11504        // through_accessor` (b5d813f) and
11505        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
11506        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
11507        // composition axes — same "the validate / shape-gate
11508        // predicate must route through the substrate-primitive typed
11509        // dispatch" discipline extended onto the sibling outer top-
11510        // level [`Caixa`] `&[T]`-composition surface. Nominally the
11511        // in-tree `validate_code_paths` production body still keys
11512        // off the internal `[(":bibliotecas", &self.bibliotecas,
11513        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
11514        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
11515        // (the tuple's homogeneous slice-typed shape blocks a per-
11516        // element accessor swap in isolation — a future companion
11517        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
11518        // `&[T]` slice-accessor axis closes that tuple onto the
11519        // triple of typed dispatches as a unit); the composition pin
11520        // catches any future accessor-side silent filter drop against
11521        // that eventual tuple-closure regardless of whether the
11522        // `:bibliotecas` slot is threaded through the accessor or the
11523        // raw field access at the tuple's construction site.
11524        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
11525        assert!(
11526            matches!(
11527                c.validate_code_paths(),
11528                Err(ManifestError::CodePathEmpty {
11529                    slot: ":bibliotecas"
11530                })
11531            ),
11532            "validate_code_paths must reject bibliotecas == vec![\"\"] \
11533             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
11534             accessor and the validate gate must route through the \
11535             same substrate-primitive typed dispatch on the \
11536             :bibliotecas per-entry empty arm",
11537        );
11538        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
11539        assert!(
11540            c.validate_code_paths().is_ok(),
11541            "validate_code_paths must accept bibliotecas == \
11542             vec![\"lib/demo.lisp\"] (the canonical single-library \
11543             shape every `feira init` template scaffolds)",
11544        );
11545    }
11546
11547    #[test]
11548    fn bibliotecas_projects_slice_by_borrow() {
11549        // The by-borrow pin: [`Caixa::bibliotecas`] returns
11550        // `&[String]` by borrow — the returned slice borrows the
11551        // underlying `Vec<String>` storage of the `:bibliotecas` slot
11552        // and the accessor must not clone the backing `Vec` on every
11553        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
11554        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
11555        // by-borrow pins on the sibling outer top-level [`Caixa`]
11556        // `&[String]`-return axes — the accessor's returned slice
11557        // must borrow from `&self` (the returned reference's lifetime
11558        // is tied to `&self`), and calling the accessor twice on the
11559        // same [`Caixa`] must yield slices that are pointer-equal
11560        // (the underlying byte-buffer is the storage `Vec`'s
11561        // allocation, not a fresh copy) as well as value-equal
11562        // (idempotent, no side effects on `&self`).
11563        //
11564        // Pins against a future silent detour that returned an owned
11565        // `Vec<String>` (which would type-check but silently clone on
11566        // every call, breaking the zero-cost projection every peer
11567        // sibling slice accessor carries), a `&Vec<String>` return
11568        // (which would leak the backing `Vec`'s grow/push/reserve
11569        // surface no downstream consumer reaches for), or a one-arm-
11570        // only accessor that returned a saturating value on some
11571        // sentinel input (breaking the pass-through invariant the
11572        // sibling slice accessors carry).
11573        for bibliotecas in [
11574            vec![],
11575            vec!["lib/demo.lisp"],
11576            vec!["lib/demo.lisp", "lib/helpers.lisp"],
11577            vec!["lib/foo.lisp", "lib/foo.lisp"],
11578        ] {
11579            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
11580            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
11581            let first = c.bibliotecas();
11582            let second = c.bibliotecas();
11583            assert_eq!(
11584                first, second,
11585                "Caixa::bibliotecas must be idempotent — two \
11586                 successive calls on the same &self must return the \
11587                 same &[String]",
11588            );
11589            assert_eq!(
11590                first.as_ptr(),
11591                second.as_ptr(),
11592                "Caixa::bibliotecas must borrow the underlying \
11593                 Vec<String> storage — two successive calls must \
11594                 return slices with the same backing pointer (a \
11595                 fresh Vec<String> clone would change the pointer on \
11596                 every call)",
11597            );
11598            assert_eq!(
11599                first,
11600                expected.as_slice(),
11601                "Caixa::bibliotecas must return :bibliotecas verbatim \
11602                 by borrow — got {first:?}, expected {expected:?}",
11603            );
11604        }
11605    }
11606
11607    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
11608
11609    #[test]
11610    fn exe_returns_exe_slice_verbatim_across_permutations() {
11611        // The canonical per-`Caixa` `:exe` universal-axis
11612        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
11613        // must return the `:exe` typed [`Vec<String>`] list verbatim as
11614        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
11615        // access across every representative value in the accept-set —
11616        // `[]` (the "no executable declared" arm every `:kind` other
11617        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
11618        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
11619        // + `Binario`-kind combination), `[""]` (a past-the-guard
11620        // sentinel that pins the accessor doesn't perform a silent
11621        // `[""] → []` collapse on the empty-entry arm — validate rejects
11622        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
11623        // accessor must ship the raw slot verbatim so a validate-time
11624        // gate regression surfaces at the layout / `feira nix` boundary
11625        // rather than being silently absorbed into an executable-drop),
11626        // `["exe/cli"]` (the canonical single-entry Binario form every
11627        // in-tree `caixa_with_code_paths` positive control uses),
11628        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
11629        // form the `validate_code_paths_accepts_explicit_relative_paths_
11630        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
11631        // (a past-the-guard duplicate sentinel — validate rejects
11632        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
11633        // set-not-multiset gate, but the accessor must ship the raw
11634        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
11635        // into(), "exe/cli".into()], .. }` fixtures continue to expose
11636        // the duplicate at the accessor).
11637        //
11638        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
11639        // pin on the substrate primitive — folds on the "outer
11640        // [`Caixa`] `&[T]` slice" projection pattern
11641        // `autores_returns_autores_slice_verbatim_across_permutations`
11642        // (b5d813f) opened,
11643        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
11644        // (78c7d3c) folded on, and
11645        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
11646        // (8a36c23) closed the universal-axis text-tag family of.
11647        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
11648        // the sibling `:servicos` future lift closes onto. Pins against
11649        // a future silent detour that returned an owned `Vec<String>`
11650        // (which would type-check but silently clone on every accessor
11651        // call, breaking the zero-cost projection every peer sibling
11652        // slice accessor carries), a `[""] → []` collapse (which would
11653        // silently absorb the `CodePathEmpty` refusal case at the
11654        // accessor boundary), or an `["exe/cli", "exe/cli"] →
11655        // ["exe/cli"]` dedup collapse (which would silently absorb the
11656        // `CodePathDuplicate` refusal case at the accessor boundary —
11657        // the per-slot set-not-multiset gate is downstream of the
11658        // accessor and must not be silently promoted into it).
11659        for exe in [
11660            vec![],
11661            vec![""],
11662            vec!["exe/cli"],
11663            vec!["exe/cli", "exe/serve"],
11664            vec!["exe/cli", "exe/cli"],
11665        ] {
11666            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
11667            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
11668            assert_eq!(
11669                c.exe(),
11670                expected.as_slice(),
11671                "Caixa::exe must return :exe verbatim (got {:?}, \
11672                 expected {expected:?})",
11673                c.exe(),
11674            );
11675            assert_eq!(
11676                c.exe(),
11677                c.exe.as_slice(),
11678                "Caixa::exe must byte-equal the raw \
11679                 `self.exe.as_slice()` field access across every value \
11680                 in the Vec<String> accept-set",
11681            );
11682        }
11683    }
11684
11685    #[test]
11686    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
11687        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
11688        // empty-arm gate on the `:exe` slot must key off
11689        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
11690        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
11691        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
11692        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
11693        // (the canonical single-executable form every in-tree
11694        // `caixa_with_code_paths` positive control uses) must pass
11695        // validate. The pair jointly pins the accessor + validate-gate
11696        // composition: any future silent detour that had the accessor
11697        // return an empty slice on the `[""]` arm (a
11698        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
11699        // silently absorb the `CodePathEmpty` refusal at the accessor
11700        // boundary and the validate gate would accept a struct-literal
11701        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
11702        // catches that at caixa-core build time.
11703        //
11704        // Peer of the per-`Caixa`
11705        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
11706        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
11707        // (b5d813f), and
11708        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
11709        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
11710        // composition axes — same "the validate / shape-gate predicate
11711        // must route through the substrate-primitive typed dispatch"
11712        // discipline extended onto the sibling outer top-level [`Caixa`]
11713        // `&[T]`-composition surface. Nominally the in-tree
11714        // `validate_code_paths` production body still keys off the
11715        // internal `[(":bibliotecas", &self.bibliotecas,
11716        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
11717        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
11718        // (the tuple's homogeneous slice-typed shape blocks a per-
11719        // element accessor swap in isolation — a future companion lift
11720        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
11721        // accessor axis closes that tuple onto the triple of typed
11722        // dispatches as a unit); the composition pin catches any future
11723        // accessor-side silent filter drop against that eventual tuple-
11724        // closure regardless of whether the `:exe` slot is threaded
11725        // through the accessor or the raw field access at the tuple's
11726        // construction site.
11727        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
11728        assert!(
11729            matches!(
11730                c.validate_code_paths(),
11731                Err(ManifestError::CodePathEmpty { slot: ":exe" })
11732            ),
11733            "validate_code_paths must reject exe == vec![\"\"] \
11734             with CodePathEmpty {{ slot: \":exe\" }} — the \
11735             accessor and the validate gate must route through the \
11736             same substrate-primitive typed dispatch on the \
11737             :exe per-entry empty arm",
11738        );
11739        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
11740        assert!(
11741            c.validate_code_paths().is_ok(),
11742            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
11743             (the canonical single-executable shape every in-tree \
11744             `caixa_with_code_paths` positive control uses)",
11745        );
11746    }
11747
11748    #[test]
11749    fn exe_projects_slice_by_borrow() {
11750        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
11751        // borrow — the returned slice borrows the underlying
11752        // `Vec<String>` storage of the `:exe` slot and the accessor
11753        // must not clone the backing `Vec` on every call. Peer of the
11754        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
11755        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
11756        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
11757        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
11758        // return axes — the accessor's returned slice must borrow from
11759        // `&self` (the returned reference's lifetime is tied to
11760        // `&self`), and calling the accessor twice on the same
11761        // [`Caixa`] must yield slices that are pointer-equal (the
11762        // underlying byte-buffer is the storage `Vec`'s allocation,
11763        // not a fresh copy) as well as value-equal (idempotent, no
11764        // side effects on `&self`).
11765        //
11766        // Pins against a future silent detour that returned an owned
11767        // `Vec<String>` (which would type-check but silently clone on
11768        // every call, breaking the zero-cost projection every peer
11769        // sibling slice accessor carries), a `&Vec<String>` return
11770        // (which would leak the backing `Vec`'s grow/push/reserve
11771        // surface no downstream consumer reaches for), or a one-arm-
11772        // only accessor that returned a saturating value on some
11773        // sentinel input (breaking the pass-through invariant the
11774        // sibling slice accessors carry).
11775        for exe in [
11776            vec![],
11777            vec!["exe/cli"],
11778            vec!["exe/cli", "exe/serve"],
11779            vec!["exe/cli", "exe/cli"],
11780        ] {
11781            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
11782            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
11783            let first = c.exe();
11784            let second = c.exe();
11785            assert_eq!(
11786                first, second,
11787                "Caixa::exe must be idempotent — two successive calls \
11788                 on the same &self must return the same &[String]",
11789            );
11790            assert_eq!(
11791                first.as_ptr(),
11792                second.as_ptr(),
11793                "Caixa::exe must borrow the underlying Vec<String> \
11794                 storage — two successive calls must return slices \
11795                 with the same backing pointer (a fresh Vec<String> \
11796                 clone would change the pointer on every call)",
11797            );
11798            assert_eq!(
11799                first,
11800                expected.as_slice(),
11801                "Caixa::exe must return :exe verbatim by borrow — \
11802                 got {first:?}, expected {expected:?}",
11803            );
11804        }
11805    }
11806
11807    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
11808
11809    #[test]
11810    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
11811        // The canonical per-`Caixa` `:servicos` universal-axis
11812        // ComputeUnit-CR-YAML-entry-path-list slice pin:
11813        // [`Caixa::servicos`] must return the `:servicos` typed
11814        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
11815        // the raw `self.servicos.as_slice()` access across every
11816        // representative value in the accept-set — `[]` (the "no
11817        // ComputeUnit-CR declared" arm every `:kind` other than
11818        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
11819        // `ServicoWithoutServicos` arm-gate fires exactly on this
11820        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
11821        // guard sentinel that pins the accessor doesn't perform a
11822        // silent `[""] → []` collapse on the empty-entry arm — validate
11823        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
11824        // but the accessor must ship the raw slot verbatim so a
11825        // validate-time gate regression surfaces at the layout /
11826        // per-Servico renderer boundary rather than being silently
11827        // absorbed into a component-drop),
11828        // `["servicos/demo.computeunit.yaml"]` (the canonical
11829        // singleton V0-shape every in-tree `caixa_with_code_paths`
11830        // positive control uses; the same shape
11831        // [`crate::require_single_servico`] admits),
11832        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
11833        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
11834        // singularity gate rejects through `ServicoCountMismatch
11835        // { count: 2 }` but the accessor must ship the raw slot
11836        // verbatim so struct-literal `Caixa { servicos: vec![...,
11837        // ...], .. }` fixtures continue to expose the count at the
11838        // accessor), and `["servicos/a.computeunit.yaml",
11839        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
11840        // sentinel — validate rejects through
11841        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
11842        // set-not-multiset gate, but the accessor must ship the raw
11843        // slot verbatim so struct-literal fixtures continue to expose
11844        // the duplicate at the accessor).
11845        //
11846        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
11847        // slice accessor pin on the substrate primitive — folds on the
11848        // "outer [`Caixa`] `&[T]` slice" projection pattern
11849        // `autores_returns_autores_slice_verbatim_across_permutations`
11850        // (b5d813f) opened,
11851        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
11852        // (78c7d3c) folded on,
11853        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
11854        // (8a36c23) closed the universal-axis text-tag family of, and
11855        // `exe_returns_exe_slice_verbatim_across_permutations`
11856        // (65d9527) opened the foreign-code-slot sub-family of. Closes
11857        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
11858        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
11859        // `:servicos`) now each carries a substrate-canonical slice
11860        // accessor. Pins against a future silent detour that returned
11861        // an owned `Vec<String>` (which would type-check but silently
11862        // clone on every accessor call, breaking the zero-cost
11863        // projection every peer sibling slice accessor carries), a
11864        // `[""] → []` collapse (which would silently absorb the
11865        // `CodePathEmpty` refusal case at the accessor boundary), an
11866        // `[a, a] → [a]` dedup collapse (which would silently absorb
11867        // the `CodePathDuplicate` refusal case at the accessor
11868        // boundary — the per-slot set-not-multiset gate is downstream
11869        // of the accessor and must not be silently promoted into it),
11870        // or a `[a, b] → [a]` singleton collapse (which would silently
11871        // absorb the V0 `ServicoCountMismatch` refusal case at the
11872        // accessor boundary — the V0 singularity gate is downstream of
11873        // the accessor and must not be silently promoted into it).
11874        for servicos in [
11875            vec![],
11876            vec![""],
11877            vec!["servicos/demo.computeunit.yaml"],
11878            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
11879            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
11880        ] {
11881            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
11882            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
11883            assert_eq!(
11884                c.servicos(),
11885                expected.as_slice(),
11886                "Caixa::servicos must return :servicos verbatim (got \
11887                 {:?}, expected {expected:?})",
11888                c.servicos(),
11889            );
11890            assert_eq!(
11891                c.servicos(),
11892                c.servicos.as_slice(),
11893                "Caixa::servicos must byte-equal the raw \
11894                 `self.servicos.as_slice()` field access across every \
11895                 value in the Vec<String> accept-set",
11896            );
11897        }
11898    }
11899
11900    #[test]
11901    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
11902        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
11903        // empty-arm gate on the `:servicos` slot must key off
11904        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
11905        // field-borrow walk. Structurally: a `Caixa { servicos:
11906        // vec!["".into()], .. }` must surface the `CodePathEmpty
11907        // { slot: ":servicos" }` refusal exactly, and a `Caixa
11908        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
11909        // .. }` (the canonical singleton V0-shape every in-tree
11910        // `caixa_with_code_paths` positive control uses) must pass
11911        // validate. The pair jointly pins the accessor + validate-gate
11912        // composition: any future silent detour that had the accessor
11913        // return an empty slice on the `[""]` arm (a `.iter().filter
11914        // (|s| !s.is_empty()).collect()` collapse) would silently
11915        // absorb the `CodePathEmpty` refusal at the accessor boundary
11916        // and the validate gate would accept a struct-literal
11917        // `Caixa { servicos: vec!["".into()], .. }` — the composition
11918        // pin catches that at caixa-core build time.
11919        //
11920        // Peer of the per-`Caixa`
11921        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
11922        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
11923        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
11924        // (b5d813f), and
11925        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
11926        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
11927        // composition axes — same "the validate / shape-gate predicate
11928        // must route through the substrate-primitive typed dispatch"
11929        // discipline extended onto the sibling outer top-level
11930        // [`Caixa`] `&[T]`-composition surface, closing the trio of
11931        // code-surface accessor-composition pins on the same axis.
11932        // Nominally the in-tree `validate_code_paths` production body
11933        // still keys off the internal
11934        // `[(":bibliotecas", &self.bibliotecas,
11935        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
11936        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
11937        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
11938        // per-element accessor swap in isolation — a future companion
11939        // lift promotes the tuple's element type to `&[String]` and
11940        // threads the triple of typed dispatches through as a unit);
11941        // the composition pin catches any future accessor-side silent
11942        // filter drop against that eventual tuple-closure regardless
11943        // of whether the `:servicos` slot is threaded through the
11944        // accessor or the raw field access at the tuple's construction
11945        // site.
11946        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
11947        assert!(
11948            matches!(
11949                c.validate_code_paths(),
11950                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
11951            ),
11952            "validate_code_paths must reject servicos == vec![\"\"] \
11953             with CodePathEmpty {{ slot: \":servicos\" }} — the \
11954             accessor and the validate gate must route through the \
11955             same substrate-primitive typed dispatch on the \
11956             :servicos per-entry empty arm",
11957        );
11958        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
11959        assert!(
11960            c.validate_code_paths().is_ok(),
11961            "validate_code_paths must accept servicos == \
11962             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
11963             singleton V0-shape every in-tree `caixa_with_code_paths` \
11964             positive control uses)",
11965        );
11966    }
11967
11968    #[test]
11969    fn servicos_projects_slice_by_borrow() {
11970        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
11971        // borrow — the returned slice borrows the underlying
11972        // `Vec<String>` storage of the `:servicos` slot and the
11973        // accessor must not clone the backing `Vec` on every call.
11974        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
11975        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
11976        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
11977        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
11978        // the sibling outer top-level [`Caixa`] `&[String]`-return
11979        // axes — the accessor's returned slice must borrow from
11980        // `&self` (the returned reference's lifetime is tied to
11981        // `&self`), and calling the accessor twice on the same
11982        // [`Caixa`] must yield slices that are pointer-equal (the
11983        // underlying byte-buffer is the storage `Vec`'s allocation,
11984        // not a fresh copy) as well as value-equal (idempotent, no
11985        // side effects on `&self`).
11986        //
11987        // Pins against a future silent detour that returned an owned
11988        // `Vec<String>` (which would type-check but silently clone on
11989        // every call, breaking the zero-cost projection every peer
11990        // sibling slice accessor carries), a `&Vec<String>` return
11991        // (which would leak the backing `Vec`'s grow/push/reserve
11992        // surface no downstream consumer reaches for), or a one-arm-
11993        // only accessor that returned a saturating value on some
11994        // sentinel input (breaking the pass-through invariant the
11995        // sibling slice accessors carry).
11996        for servicos in [
11997            vec![],
11998            vec!["servicos/demo.computeunit.yaml"],
11999            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
12000            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
12001        ] {
12002            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
12003            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
12004            let first = c.servicos();
12005            let second = c.servicos();
12006            assert_eq!(
12007                first, second,
12008                "Caixa::servicos must be idempotent — two successive \
12009                 calls on the same &self must return the same &[String]",
12010            );
12011            assert_eq!(
12012                first.as_ptr(),
12013                second.as_ptr(),
12014                "Caixa::servicos must borrow the underlying \
12015                 Vec<String> storage — two successive calls must \
12016                 return slices with the same backing pointer (a fresh \
12017                 Vec<String> clone would change the pointer on every \
12018                 call)",
12019            );
12020            assert_eq!(
12021                first,
12022                expected.as_slice(),
12023                "Caixa::servicos must return :servicos verbatim by \
12024                 borrow — got {first:?}, expected {expected:?}",
12025            );
12026        }
12027    }
12028
12029    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
12030
12031    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
12032        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12033        c.deps = deps;
12034        c
12035    }
12036
12037    #[test]
12038    fn deps_returns_deps_slice_verbatim_across_permutations() {
12039        // The canonical per-`Caixa` `:deps` universal-axis runtime-
12040        // dependency-declaration-list slice pin: [`Caixa::deps`] must
12041        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
12042        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
12043        // access across every representative value in the accept-set —
12044        // `[]` (the "no runtime deps declared" arm every existing
12045        // fixture without a `:deps` line carries; the
12046        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
12047        // single-entry list (the shape most consumer caixas carry), a
12048        // canonical two-entry list (the multi-dep runtime closure), and
12049        // two past-the-guard sentinels — a `[""]`-`:nome` entry
12050        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
12051        // `NomeInvalid` but the accessor must ship the raw slot
12052        // verbatim) and a `[a, a]` duplicate (validate rejects through
12053        // `DuplicateNome { list: ":deps" }` but the accessor must ship
12054        // the raw slot verbatim so struct-literal fixtures continue to
12055        // expose the duplicate at the accessor).
12056        //
12057        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
12058        // pin on the substrate primitive — opens the outer-`Caixa`
12059        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
12060        // future lift closes on. Peer of the closed outer-`Caixa`
12061        // foreign-code-slot `&[String]` sub-family
12062        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12063        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
12064        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
12065        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
12066        // (`autores_returns_autores_slice_verbatim_across_permutations`
12067        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12068        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
12069        // projection pattern onto a novel element-type axis (`Dep`
12070        // composite vs the prior sibling family's `String` scalar).
12071        // Pins against a future silent detour that returned an owned
12072        // `Vec<Dep>` (which would type-check but silently clone on every
12073        // accessor call, breaking the zero-cost projection every peer
12074        // sibling slice accessor carries), a `[""] → []` collapse (which
12075        // would silently absorb the `NomeEmpty` refusal case at the
12076        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
12077        // would silently absorb the `DuplicateNome` refusal case at the
12078        // accessor boundary).
12079        for deps in [
12080            vec![],
12081            vec![Dep::simple("", "^0.1")],
12082            vec![Dep::simple("caixa-teia", "^0.1")],
12083            vec![
12084                Dep::simple("caixa-teia", "^0.1"),
12085                Dep::simple("caixa-core", "^0.1"),
12086            ],
12087            vec![
12088                Dep::simple("caixa-teia", "^0.1"),
12089                Dep::simple("caixa-teia", "^0.2"),
12090            ],
12091        ] {
12092            let c = caixa_with_deps(deps.clone());
12093            assert_eq!(
12094                c.deps(),
12095                deps.as_slice(),
12096                "Caixa::deps must return :deps verbatim (got {:?}, \
12097                 expected {deps:?})",
12098                c.deps(),
12099            );
12100            assert_eq!(
12101                c.deps(),
12102                c.deps.as_slice(),
12103                "Caixa::deps must element-equal the raw \
12104                 `self.deps.as_slice()` field access across every \
12105                 value in the Vec<Dep> accept-set",
12106            );
12107        }
12108    }
12109
12110    #[test]
12111    fn validate_deps_duplicate_arm_routes_through_accessor() {
12112        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
12113        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
12114        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
12115        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
12116        // "^0.2")], .. }` must surface the `DuplicateNome { list:
12117        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
12118        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
12119        // form) must pass validate. The pair jointly pins the accessor +
12120        // validate-gate composition: any future silent detour that had
12121        // the accessor return a dedupped slice on the `[a, a]` arm (a
12122        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
12123        // would silently absorb the `DuplicateNome` refusal at the
12124        // accessor boundary and the validate gate would accept a
12125        // struct-literal `Caixa` carrying the drift — the composition
12126        // pin catches that at caixa-core build time.
12127        //
12128        // Peer of the per-`Caixa`
12129        // `validate_autores_empty_entry_arm_routes_through_accessor`
12130        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12131        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12132        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
12133        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
12134        // (611f78b) accessor-composition pins on the sibling `&[T]`-
12135        // composition axes — same "the validate gate must route through
12136        // the substrate-primitive typed dispatch" discipline extended
12137        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
12138        // composition surface, opening the outer-`Caixa` dependency-slot
12139        // arm of the composition-pin family.
12140        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
12141        let err = c.validate_deps().unwrap_err();
12142        assert!(
12143            matches!(
12144                err,
12145                DepError::DuplicateNome { ref nome, list } if nome == "d"
12146                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
12147            ),
12148            "validate_deps must reject deps == \
12149             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
12150             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
12151             accessor and the validate gate must route through the \
12152             same substrate-primitive typed dispatch on the :deps \
12153             within-list duplicate arm (got {err:?})",
12154        );
12155        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
12156        assert!(
12157            c.validate_deps().is_ok(),
12158            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
12159             (the canonical single-entry form)",
12160        );
12161    }
12162
12163    #[test]
12164    fn deps_projects_slice_by_borrow() {
12165        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
12166        // — the returned slice borrows the underlying `Vec<Dep>` storage
12167        // of the `:deps` slot and the accessor must not clone the
12168        // backing `Vec` on every call. Peer of the per-`Caixa`
12169        // `autores_projects_slice_by_borrow` (b5d813f),
12170        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
12171        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
12172        // `exe_projects_slice_by_borrow` (65d9527), and
12173        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
12174        // on the sibling outer top-level [`Caixa`] `&[String]`-return
12175        // axes — the accessor's returned slice must borrow from `&self`
12176        // (the returned reference's lifetime is tied to `&self`), and
12177        // calling the accessor twice on the same [`Caixa`] must yield
12178        // slices that are pointer-equal (the underlying byte-buffer is
12179        // the storage `Vec`'s allocation, not a fresh copy) as well as
12180        // value-equal (idempotent, no side effects on `&self`).
12181        //
12182        // Pins against a future silent detour that returned an owned
12183        // `Vec<Dep>` (which would type-check but silently clone on
12184        // every call), a `&Vec<Dep>` return (which would leak the
12185        // backing `Vec`'s grow/push/reserve surface no downstream
12186        // consumer reaches for), or a one-arm-only accessor that
12187        // returned a saturating value on some sentinel input.
12188        for deps in [
12189            vec![],
12190            vec![Dep::simple("caixa-teia", "^0.1")],
12191            vec![
12192                Dep::simple("caixa-teia", "^0.1"),
12193                Dep::simple("caixa-core", "^0.1"),
12194            ],
12195        ] {
12196            let c = caixa_with_deps(deps.clone());
12197            let first = c.deps();
12198            let second = c.deps();
12199            assert_eq!(
12200                first, second,
12201                "Caixa::deps must be idempotent — two successive calls \
12202                 on the same &self must return the same &[Dep]",
12203            );
12204            assert_eq!(
12205                first.as_ptr(),
12206                second.as_ptr(),
12207                "Caixa::deps must borrow the underlying Vec<Dep> \
12208                 storage — two successive calls must return slices \
12209                 with the same backing pointer (a fresh Vec<Dep> clone \
12210                 would change the pointer on every call)",
12211            );
12212            assert_eq!(
12213                first,
12214                deps.as_slice(),
12215                "Caixa::deps must return :deps verbatim by borrow — \
12216                 got {first:?}, expected {deps:?}",
12217            );
12218        }
12219    }
12220
12221    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
12222
12223    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
12224        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12225        c.deps_dev = deps_dev;
12226        c
12227    }
12228
12229    #[test]
12230    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
12231        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
12232        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
12233        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
12234        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
12235        // access across every representative value in the accept-set —
12236        // `[]` (the "no dev deps declared" arm every existing fixture
12237        // without a `:deps-dev` line carries; the [`Caixa::template`]
12238        // scaffold emits `:deps-dev ()`), a canonical single-entry list
12239        // (the shape most consumer caixas carry — a `tatara-check` dev
12240        // pin), a canonical two-entry list (the multi-dev-dep closure),
12241        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
12242        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
12243        // `NomeInvalid` but the accessor must ship the raw slot
12244        // verbatim) and a `[a, a]` duplicate (validate rejects through
12245        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
12246        // ship the raw slot verbatim so struct-literal fixtures continue
12247        // to expose the duplicate at the accessor).
12248        //
12249        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
12250        // pin on the substrate primitive — closes the outer-`Caixa`
12251        // dependency-slot `&[Dep]` sub-family the sibling
12252        // `deps_returns_deps_slice_verbatim_across_permutations`
12253        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
12254        // slice" projection pattern onto the sibling dev-dep axis —
12255        // pins against a future silent detour that returned an owned
12256        // `Vec<Dep>` (which would type-check but silently clone on every
12257        // accessor call, breaking the zero-cost projection every peer
12258        // sibling slice accessor carries), a `[""] → []` collapse (which
12259        // would silently absorb the `NomeEmpty` refusal case at the
12260        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
12261        // would silently absorb the `DuplicateNome` refusal case at the
12262        // accessor boundary).
12263        for deps_dev in [
12264            vec![],
12265            vec![Dep::simple("", "^0.1")],
12266            vec![Dep::simple("tatara-check", "^0.1")],
12267            vec![
12268                Dep::simple("tatara-check", "^0.1"),
12269                Dep::simple("caixa-lint", "^0.1"),
12270            ],
12271            vec![
12272                Dep::simple("tatara-check", "^0.1"),
12273                Dep::simple("tatara-check", "^0.2"),
12274            ],
12275        ] {
12276            let c = caixa_with_deps_dev(deps_dev.clone());
12277            assert_eq!(
12278                c.deps_dev(),
12279                deps_dev.as_slice(),
12280                "Caixa::deps_dev must return :deps-dev verbatim (got \
12281                 {:?}, expected {deps_dev:?})",
12282                c.deps_dev(),
12283            );
12284            assert_eq!(
12285                c.deps_dev(),
12286                c.deps_dev.as_slice(),
12287                "Caixa::deps_dev must element-equal the raw \
12288                 `self.deps_dev.as_slice()` field access across every \
12289                 value in the Vec<Dep> accept-set",
12290            );
12291        }
12292    }
12293
12294    #[test]
12295    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
12296        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
12297        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
12298        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
12299        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
12300        // Dep::simple("d", "^0.2")], .. }` must surface the
12301        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
12302        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
12303        // canonical single-entry form) must pass validate. The pair
12304        // jointly pins the accessor + validate-gate composition: any
12305        // future silent detour that had the accessor return a dedupped
12306        // slice on the `[a, a]` arm (a
12307        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
12308        // would silently absorb the `DuplicateNome` refusal at the
12309        // accessor boundary and the validate gate would accept a
12310        // struct-literal `Caixa` carrying the drift — the composition
12311        // pin catches that at caixa-core build time.
12312        //
12313        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
12314        // (ad34b4e) on the sibling `:deps` axis — same "the validate
12315        // gate must route through the substrate-primitive typed
12316        // dispatch" discipline folded onto the sibling `:deps-dev`
12317        // axis, closing the two-list dep-graph composition-pin family.
12318        // The `:deps-dev` diagnostic must carry the
12319        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
12320        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
12321        // offending list unambiguously.
12322        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
12323        let err = c.validate_deps().unwrap_err();
12324        assert!(
12325            matches!(
12326                err,
12327                DepError::DuplicateNome { ref nome, list } if nome == "d"
12328                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
12329            ),
12330            "validate_deps must reject deps_dev == \
12331             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
12332             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
12333             accessor and the validate gate must route through the \
12334             same substrate-primitive typed dispatch on the :deps-dev \
12335             within-list duplicate arm (got {err:?})",
12336        );
12337        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
12338        assert!(
12339            c.validate_deps().is_ok(),
12340            "validate_deps must accept deps_dev == \
12341             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
12342        );
12343    }
12344
12345    #[test]
12346    fn deps_dev_projects_slice_by_borrow() {
12347        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
12348        // borrow — the returned slice borrows the underlying `Vec<Dep>`
12349        // storage of the `:deps-dev` slot and the accessor must not
12350        // clone the backing `Vec` on every call. Peer of
12351        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
12352        // `:deps` axis, and of the per-`Caixa`
12353        // `autores_projects_slice_by_borrow` (b5d813f),
12354        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
12355        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
12356        // `exe_projects_slice_by_borrow` (65d9527), and
12357        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
12358        // on the sibling outer top-level [`Caixa`] `&[String]`-return
12359        // axes — the accessor's returned slice must borrow from `&self`
12360        // (the returned reference's lifetime is tied to `&self`), and
12361        // calling the accessor twice on the same [`Caixa`] must yield
12362        // slices that are pointer-equal (the underlying byte-buffer is
12363        // the storage `Vec`'s allocation, not a fresh copy) as well as
12364        // value-equal (idempotent, no side effects on `&self`).
12365        //
12366        // Pins against a future silent detour that returned an owned
12367        // `Vec<Dep>` (which would type-check but silently clone on
12368        // every call), a `&Vec<Dep>` return (which would leak the
12369        // backing `Vec`'s grow/push/reserve surface no downstream
12370        // consumer reaches for), or a one-arm-only accessor that
12371        // returned a saturating value on some sentinel input.
12372        for deps_dev in [
12373            vec![],
12374            vec![Dep::simple("tatara-check", "^0.1")],
12375            vec![
12376                Dep::simple("tatara-check", "^0.1"),
12377                Dep::simple("caixa-lint", "^0.1"),
12378            ],
12379        ] {
12380            let c = caixa_with_deps_dev(deps_dev.clone());
12381            let first = c.deps_dev();
12382            let second = c.deps_dev();
12383            assert_eq!(
12384                first, second,
12385                "Caixa::deps_dev must be idempotent — two successive \
12386                 calls on the same &self must return the same &[Dep]",
12387            );
12388            assert_eq!(
12389                first.as_ptr(),
12390                second.as_ptr(),
12391                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
12392                 storage — two successive calls must return slices \
12393                 with the same backing pointer (a fresh Vec<Dep> clone \
12394                 would change the pointer on every call)",
12395            );
12396            assert_eq!(
12397                first,
12398                deps_dev.as_slice(),
12399                "Caixa::deps_dev must return :deps-dev verbatim by \
12400                 borrow — got {first:?}, expected {deps_dev:?}",
12401            );
12402        }
12403    }
12404
12405    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
12406
12407    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
12408        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12409        c.limits = limits;
12410        c
12411    }
12412
12413    #[test]
12414    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
12415        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
12416        // composite optional-composite-reference-shape pin:
12417        // [`Caixa::limits`] must return the `:limits` typed
12418        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
12419        // reference over the same backing storage the raw
12420        // `self.limits.as_ref()` field access borrows from, byte-equal
12421        // across every representative fixture in the accept-set — the
12422        // author-omitted `None` shape (the "engine-default applies"
12423        // partition every downstream Servico M2 overlay emitter treats
12424        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
12425        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
12426        // per-axis cap is `None`, so the peer M2 overlay emitter's
12427        // `.is_empty()`-gated projection still emits nothing but the
12428        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
12429        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
12430        // fixture (only `:memory` set — the canonical shape most
12431        // memory-heavy Servicos carry), and a fully-populated composite
12432        // (every per-axis cap set — the canonical shape a
12433        // sandboxed-by-default Servico carries).
12434        //
12435        // Pins against a future silent detour that returned a fresh-
12436        // cloned [`LimitsSpec`] copy (which would type-check via the
12437        // `Clone` impl but silently break every downstream caller that
12438        // relied on the reference sharing the composite's backing
12439        // identity), a reference to an operator-resolved overlay (the
12440        // future per-cluster `:limits-overrides` slot — its resolution
12441        // must land at exactly this accessor body, not silently divert
12442        // the raw slot away from a second consumer), a
12443        // `None` → `Some(LimitsSpec::default)` cluster-default
12444        // projection (which would collapse the load-bearing
12445        // "author-omitted `:limits` ⇒ engine-default applies" partition
12446        // the peer [`crate::render::servico_m2_overlay`] emitter and
12447        // the peer [`Caixa::declared_servico_slots`] enumerator both
12448        // read), or an axis-shuffled projection (a future detour that
12449        // swapped `memory` and `fuel` through the accessor would
12450        // silently split the paired [`crate::StandardLayout::verify`]
12451        // per-`:limits` shape gate's traversal input from the peer
12452        // `servico_m2_overlay` emitter's projection input).
12453        //
12454        // First outer top-level [`Caixa`] `Option<&Composite>`-return
12455        // composite-reference accessor pin on the substrate primitive
12456        // — opens the outer-`Caixa` `Option<&Composite>` composite-
12457        // reference projection pattern the sibling `:behavior`
12458        // [`crate::BehaviorSpec`] / `:politicas`
12459        // [`crate::aplicacao::MeshPolicy`] / `:placement`
12460        // [`crate::aplicacao::Placement`] / `:entrada`
12461        // [`crate::aplicacao::Entrada`] future outer-composite lifts
12462        // fold on. Peer of the closed M3 outer-composite family the
12463        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
12464        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
12465        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
12466        // reference accessor pins already carry on the outer
12467        // [`crate::AplicacaoSpec`] altitude — extends the outer-
12468        // accessor byte-equal-projection discipline onto the outer
12469        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
12470        use crate::LimitsSpec;
12471        use std::time::Duration;
12472        let fixtures: Vec<Option<LimitsSpec>> = vec![
12473            None,
12474            Some(LimitsSpec::default()),
12475            Some(LimitsSpec {
12476                memory: Some(64 * 1024 * 1024),
12477                ..Default::default()
12478            }),
12479            Some(LimitsSpec {
12480                memory: Some(64 * 1024 * 1024),
12481                fuel: Some(1_000_000),
12482                wall_clock: Some(Duration::from_secs(30)),
12483                cpu: Some(500),
12484            }),
12485        ];
12486        for limits in fixtures {
12487            let c = caixa_with_limits(limits.clone());
12488            assert_eq!(
12489                c.limits(),
12490                limits.as_ref(),
12491                "Caixa::limits must return :limits verbatim (got {:?}, \
12492                 expected {:?})",
12493                c.limits(),
12494                limits.as_ref(),
12495            );
12496            match (c.limits(), c.limits.as_ref()) {
12497                (Some(a), Some(b)) => assert!(
12498                    std::ptr::eq(a, b),
12499                    "Caixa::limits accessor and self.limits.as_ref() \
12500                     field access must borrow the same backing storage \
12501                     — the accessor is the substrate-primitive typed \
12502                     dispatch every downstream Servico-M2-overlay \
12503                     composite consumer must route through, and a \
12504                     reference-identity split would silently break \
12505                     every consumer that relied on the borrow sharing \
12506                     the composite's storage",
12507                ),
12508                (None, None) => {}
12509                _ => panic!(
12510                    "Caixa::limits presence bit must byte-equal \
12511                     self.limits.is_some() — a presence-bit drift would \
12512                     silently split the paired StandardLayout::verify \
12513                     per-`:limits` shape gate's traversal head from \
12514                     the peer render::servico_m2_overlay M2 overlay \
12515                     emitter's traversal head from the peer \
12516                     Caixa::declared_servico_slots M2 declared-slot \
12517                     enumerator's presence probe",
12518                ),
12519            }
12520            assert_eq!(
12521                c.limits().is_some(),
12522                c.limits.is_some(),
12523                "Caixa::limits().is_some() must byte-equal \
12524                 self.limits.is_some() — a presence-bit drift would \
12525                 silently split every downstream Option<&LimitsSpec> \
12526                 consumer's partition on the engine-default arm",
12527            );
12528        }
12529    }
12530
12531    #[test]
12532    fn declared_servico_slots_limits_arm_routes_through_accessor() {
12533        // Composition pin: [`Caixa::declared_servico_slots`]'s
12534        // `:limits` presence-probe arm must key off [`Caixa::limits`],
12535        // not the raw `self.limits.is_some()` field-probe. Structurally:
12536        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
12537        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
12538        // (the presence bit is `Some`, so the M2 kind-coherence gate
12539        // must surface the slot as "declared" even when every per-axis
12540        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
12541        // push the label (the "author omitted the slot entirely"
12542        // partition). The pair jointly pins the accessor + declared-
12543        // slot enumerator composition: any future silent detour that
12544        // had the accessor collapse `Some(LimitsSpec::default())` to
12545        // `None` (a `.filter(|l| !l.is_empty())` projection) would
12546        // silently absorb the "declared but empty" arm at the
12547        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
12548        // kind-coherence gate would silently accept a
12549        // struct-literal `Caixa` carrying the drift.
12550        //
12551        // Peer of the sibling per-`Caixa`
12552        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
12553        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
12554        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
12555        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
12556        // enumerator gate must route through the substrate-primitive
12557        // typed dispatch" discipline extended onto the outer top-level
12558        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
12559        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
12560        // composition-pin family.
12561        use crate::LimitsSpec;
12562        let c = caixa_with_limits(Some(LimitsSpec::default()));
12563        let slots = c.declared_servico_slots();
12564        assert!(
12565            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
12566            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
12567             when `:limits` is Some (even for LimitsSpec::default()) \
12568             — the accessor and the enumerator gate must route through \
12569             the same substrate-primitive typed dispatch on the outer \
12570             :limits presence bit (got slots={slots:?})",
12571        );
12572        let c = caixa_with_limits(None);
12573        let slots = c.declared_servico_slots();
12574        assert!(
12575            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
12576            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
12577             when `:limits` is None — the author-omitted arm must \
12578             route through the accessor's None-return unchanged (got \
12579             slots={slots:?})",
12580        );
12581    }
12582
12583    #[test]
12584    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
12585        // Composition pin: [`crate::render::servico_m2_overlay`]'s
12586        // per-`:limits` M2 overlay emit arm must key off
12587        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
12588        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
12589        // Some(64 MiB), .. default }), .. }` must surface the
12590        // `M2_KEY_LIMITS` key with the per-axis
12591        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
12592        // limits: Some(LimitsSpec::default()), .. }` must omit the
12593        // key entirely (the `.is_empty()`-gated inner arm elides an
12594        // empty composite even when the outer presence bit is `Some`),
12595        // and a `Caixa { limits: None, .. }` must also omit the key
12596        // (the "author omitted the slot entirely" partition). The
12597        // three-fixture family jointly pins the accessor + M2 overlay
12598        // emitter composition: any future silent detour that had the
12599        // accessor return a fresh-cloned copy on the `Some` arm (a
12600        // `LimitsSpec::clone()` projection) would silently break the
12601        // reference-identity pin the peer per-axis
12602        // `serde_yaml::to_value(limits)` projection reads from.
12603        use crate::LimitsSpec;
12604        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
12605        let c = caixa_with_limits(Some(LimitsSpec {
12606            memory: Some(64 * 1024 * 1024),
12607            ..Default::default()
12608        }));
12609        let overlay = servico_m2_overlay(&c).unwrap();
12610        assert!(
12611            overlay.contains_key(M2_KEY_LIMITS),
12612            "servico_m2_overlay must surface M2_KEY_LIMITS when \
12613             `:limits` carries a non-empty composite — the accessor \
12614             and the M2 overlay emitter must route through the same \
12615             substrate-primitive typed dispatch on the outer :limits \
12616             composite (got overlay={overlay:?})",
12617        );
12618        let c = caixa_with_limits(Some(LimitsSpec::default()));
12619        let overlay = servico_m2_overlay(&c).unwrap();
12620        assert!(
12621            !overlay.contains_key(M2_KEY_LIMITS),
12622            "servico_m2_overlay must omit M2_KEY_LIMITS when \
12623             `:limits` is Some(LimitsSpec::default()) — the empty \
12624             composite's `.is_empty()`-gated inner arm must elide \
12625             the key regardless of the outer presence bit (got \
12626             overlay={overlay:?})",
12627        );
12628        let c = caixa_with_limits(None);
12629        let overlay = servico_m2_overlay(&c).unwrap();
12630        assert!(
12631            !overlay.contains_key(M2_KEY_LIMITS),
12632            "servico_m2_overlay must omit M2_KEY_LIMITS when \
12633             `:limits` is None — the author-omitted arm must route \
12634             through the accessor's None-return unchanged (got \
12635             overlay={overlay:?})",
12636        );
12637    }
12638
12639    #[test]
12640    fn limits_projects_option_ref_by_borrow() {
12641        // The by-borrow pin: [`Caixa::limits`] returns
12642        // `Option<&LimitsSpec>` by borrow — the returned reference
12643        // borrows the underlying `Option<LimitsSpec>` storage of the
12644        // `:limits` slot and the accessor must not clone the backing
12645        // composite on every call. Peer of the sibling
12646        // `deps_projects_slice_by_borrow` (ad34b4e) /
12647        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
12648        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
12649        // extended here to the outer [`Caixa`] `Option<&Composite>`-
12650        // return axis: the accessor's returned reference must borrow
12651        // from `&self` (the returned reference's lifetime is tied to
12652        // `&self`), and calling the accessor twice on the same
12653        // [`Caixa`] must yield references that are pointer-equal (the
12654        // underlying byte-buffer is the storage `LimitsSpec`'s
12655        // allocation, not a fresh copy) as well as value-equal
12656        // (idempotent, no side effects on `&self`).
12657        //
12658        // Pins against a future silent detour that returned an owned
12659        // `LimitsSpec` (which would type-check via the `Clone` impl
12660        // but silently clone on every call), a `&LimitsSpec` panic-
12661        // return on the `None` arm (which would collapse the load-
12662        // bearing `Option` presence-bit into a runtime panic), or a
12663        // one-arm-only accessor that returned a saturating composite
12664        // on some sentinel input.
12665        use crate::LimitsSpec;
12666        use std::time::Duration;
12667        for limits in [
12668            Some(LimitsSpec::default()),
12669            Some(LimitsSpec {
12670                memory: Some(64 * 1024 * 1024),
12671                fuel: Some(1_000_000),
12672                wall_clock: Some(Duration::from_secs(30)),
12673                cpu: Some(500),
12674            }),
12675        ] {
12676            let c = caixa_with_limits(limits.clone());
12677            let first = c.limits().unwrap();
12678            let second = c.limits().unwrap();
12679            assert_eq!(
12680                first, second,
12681                "Caixa::limits must be idempotent — two successive \
12682                 calls on the same &self must return the same \
12683                 &LimitsSpec",
12684            );
12685            assert!(
12686                std::ptr::eq(first, second),
12687                "Caixa::limits must borrow the underlying \
12688                 Option<LimitsSpec> storage — two successive calls \
12689                 must return references with the same backing pointer \
12690                 (a fresh LimitsSpec clone would change the pointer \
12691                 on every call)",
12692            );
12693            assert_eq!(
12694                Some(first),
12695                limits.as_ref(),
12696                "Caixa::limits must return :limits verbatim by borrow \
12697                 — got {first:?}, expected {:?}",
12698                limits.as_ref(),
12699            );
12700        }
12701        let c = caixa_with_limits(None);
12702        assert!(
12703            c.limits().is_none(),
12704            "Caixa::limits must return None when :limits is absent — \
12705             the author-omitted arm must project through the \
12706             accessor's Option::None unchanged",
12707        );
12708    }
12709
12710    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
12711
12712    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
12713        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12714        c.behavior = behavior;
12715        c
12716    }
12717
12718    #[test]
12719    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
12720        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
12721        // composite optional-composite-reference-shape pin:
12722        // [`Caixa::behavior`] must return the `:behavior` typed
12723        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
12724        // reference over the same backing storage the raw
12725        // `self.behavior.as_ref()` field access borrows from, byte-equal
12726        // across every representative fixture in the accept-set — the
12727        // author-omitted `None` shape (the "runtime-default applies"
12728        // partition every downstream Servico M2 overlay emitter treats
12729        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
12730        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
12731        // every per-callback path is `None`, so the peer M2 overlay
12732        // emitter's `.is_empty()`-gated projection still emits nothing
12733        // but the outer presence-bit is `Some`, so
12734        // [`Caixa::declared_servico_slots`] still pushes the
12735        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
12736        // (only `:on-state-change` set — the canonical shape a caixa
12737        // that only wires the hot-upgrade migration path carries), and
12738        // a fully-populated composite (every per-callback path set —
12739        // the canonical shape a fully-instrumented gen_server-shaped
12740        // Servico carries).
12741        //
12742        // Peer of the sibling
12743        // `limits_returns_limits_option_ref_verbatim_across_permutations`
12744        // (b2bd9d7) opening fixture-family + reference-identity +
12745        // presence-bit tetrad pin on the outer top-level [`Caixa`]
12746        // `Option<&Composite>`-return sub-family — extended here to the
12747        // second axis of that sub-family so both of the currently-lifted
12748        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
12749        // `:behavior`) carry the same "byte-equal, borrow-shared,
12750        // presence-bit-preserved" outer-accessor discipline.
12751        //
12752        // Pins against a future silent detour that returned a fresh-
12753        // cloned [`crate::BehaviorSpec`] copy (which would type-check
12754        // via the `Clone` impl but silently break every downstream
12755        // caller that relied on the reference sharing the composite's
12756        // backing identity), a reference to an operator-resolved
12757        // overlay (a future per-cluster `:behavior-overrides` slot —
12758        // its resolution must land at exactly this accessor body, not
12759        // silently divert the raw slot away from a second consumer), a
12760        // `None` → `Some(BehaviorSpec::default)` cluster-default
12761        // projection (which would collapse the load-bearing
12762        // "author-omitted `:behavior` ⇒ runtime-default applies"
12763        // partition the peer [`crate::render::servico_m2_overlay`]
12764        // emitter, the peer [`Caixa::declared_servico_slots`]
12765        // enumerator, and the cross-slot
12766        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
12767        // gate all read), or a callback-shuffled projection (a future
12768        // detour that swapped `on_init` and `on_terminate` through the
12769        // accessor would silently split the paired
12770        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
12771        // traversal input from the peer `servico_m2_overlay` emitter's
12772        // projection input from the cross-slot `:state-change`
12773        // composition gate's traversal input).
12774        use crate::BehaviorSpec;
12775        use std::path::PathBuf;
12776        let fixtures: Vec<Option<BehaviorSpec>> = vec![
12777            None,
12778            Some(BehaviorSpec::default()),
12779            Some(BehaviorSpec {
12780                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
12781                ..Default::default()
12782            }),
12783            Some(BehaviorSpec {
12784                on_init: Some(PathBuf::from("lib/init.lisp")),
12785                on_call: Some(PathBuf::from("lib/handlers.lisp")),
12786                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
12787                on_info: Some(PathBuf::from("lib/handlers.lisp")),
12788                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
12789                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
12790            }),
12791        ];
12792        for behavior in fixtures {
12793            let c = caixa_with_behavior(behavior.clone());
12794            assert_eq!(
12795                c.behavior(),
12796                behavior.as_ref(),
12797                "Caixa::behavior must return :behavior verbatim (got \
12798                 {:?}, expected {:?})",
12799                c.behavior(),
12800                behavior.as_ref(),
12801            );
12802            match (c.behavior(), c.behavior.as_ref()) {
12803                (Some(a), Some(b)) => assert!(
12804                    std::ptr::eq(a, b),
12805                    "Caixa::behavior accessor and self.behavior.as_ref() \
12806                     field access must borrow the same backing storage \
12807                     — the accessor is the substrate-primitive typed \
12808                     dispatch every downstream Servico-M2-overlay \
12809                     composite consumer must route through, and a \
12810                     reference-identity split would silently break \
12811                     every consumer that relied on the borrow sharing \
12812                     the composite's storage",
12813                ),
12814                (None, None) => {}
12815                _ => panic!(
12816                    "Caixa::behavior presence bit must byte-equal \
12817                     self.behavior.is_some() — a presence-bit drift \
12818                     would silently split the paired \
12819                     StandardLayout::verify per-`:behavior` shape \
12820                     gate's traversal head from the peer \
12821                     render::servico_m2_overlay M2 overlay emitter's \
12822                     traversal head from the cross-slot \
12823                     validate_upgrade_from_against_behavior \
12824                     composition gate's traversal head from the peer \
12825                     Caixa::declared_servico_slots M2 declared-slot \
12826                     enumerator's presence probe",
12827                ),
12828            }
12829            assert_eq!(
12830                c.behavior().is_some(),
12831                c.behavior.is_some(),
12832                "Caixa::behavior().is_some() must byte-equal \
12833                 self.behavior.is_some() — a presence-bit drift would \
12834                 silently split every downstream Option<&BehaviorSpec> \
12835                 consumer's partition on the runtime-default arm",
12836            );
12837        }
12838    }
12839
12840    #[test]
12841    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
12842        // Composition pin: [`Caixa::declared_servico_slots`]'s
12843        // `:behavior` presence-probe arm must key off
12844        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
12845        // field-probe. Structurally: a `Caixa { behavior:
12846        // Some(BehaviorSpec::default()), .. }` must still push
12847        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
12848        // presence bit is `Some`, so the M2 kind-coherence gate must
12849        // surface the slot as "declared" even when every per-callback
12850        // path is unset), and a `Caixa { behavior: None, .. }` must
12851        // NOT push the label (the "author omitted the slot entirely"
12852        // partition). The pair jointly pins the accessor + declared-
12853        // slot enumerator composition: any future silent detour that
12854        // had the accessor collapse `Some(BehaviorSpec::default())`
12855        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
12856        // silently absorb the "declared but empty" arm at the
12857        // accessor boundary and the
12858        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
12859        // kind-coherence gate would silently accept a struct-literal
12860        // `Caixa` carrying the drift.
12861        //
12862        // Peer of the sibling
12863        // `declared_servico_slots_limits_arm_routes_through_accessor`
12864        // (b2bd9d7) composition pin on the sibling `:limits` outer-
12865        // `Option<&LimitsSpec>` arm of the same
12866        // [`Caixa::declared_servico_slots`] M2 declared-slot
12867        // enumerator's traversal — same "the enumerator gate must
12868        // route through the substrate-primitive typed dispatch"
12869        // discipline extended onto the outer top-level [`Caixa`]
12870        // `Option<&BehaviorSpec>`-composition surface.
12871        use crate::BehaviorSpec;
12872        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
12873        let slots = c.declared_servico_slots();
12874        assert!(
12875            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
12876            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
12877             when `:behavior` is Some (even for BehaviorSpec::default()) \
12878             — the accessor and the enumerator gate must route through \
12879             the same substrate-primitive typed dispatch on the outer \
12880             :behavior presence bit (got slots={slots:?})",
12881        );
12882        let c = caixa_with_behavior(None);
12883        let slots = c.declared_servico_slots();
12884        assert!(
12885            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
12886            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
12887             when `:behavior` is None — the author-omitted arm must \
12888             route through the accessor's None-return unchanged (got \
12889             slots={slots:?})",
12890        );
12891    }
12892
12893    #[test]
12894    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
12895        // Composition pin: [`crate::render::servico_m2_overlay`]'s
12896        // per-`:behavior` M2 overlay emit arm must key off
12897        // [`Caixa::behavior`], not the raw `&caixa.behavior`
12898        // field-borrow. Structurally: a `Caixa { behavior:
12899        // Some(BehaviorSpec { on_state_change: Some(...), .. default
12900        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
12901        // per-callback `onStateChange` sub-mapping in the overlay, a
12902        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
12903        // must omit the key entirely (the `.is_empty()`-gated inner
12904        // arm elides an empty composite even when the outer presence
12905        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
12906        // also omit the key (the "author omitted the slot entirely"
12907        // partition). The three-fixture family jointly pins the
12908        // accessor + M2 overlay emitter composition: any future
12909        // silent detour that had the accessor return a fresh-cloned
12910        // copy on the `Some` arm (a `BehaviorSpec::clone()`
12911        // projection) would silently break the reference-identity
12912        // pin the peer per-callback `serde_yaml::to_value(behavior)`
12913        // projection reads from.
12914        //
12915        // Peer of the sibling
12916        // `servico_m2_overlay_limits_arm_routes_through_accessor`
12917        // (b2bd9d7) composition pin on the sibling `:limits` outer-
12918        // `Option<&LimitsSpec>` arm of the same
12919        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
12920        // traversal — same "the emitter must route through the
12921        // substrate-primitive typed dispatch on the outer composite"
12922        // discipline extended onto the outer top-level [`Caixa`]
12923        // `Option<&BehaviorSpec>`-composition surface.
12924        use crate::BehaviorSpec;
12925        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
12926        use std::path::PathBuf;
12927        let c = caixa_with_behavior(Some(BehaviorSpec {
12928            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
12929            ..Default::default()
12930        }));
12931        let overlay = servico_m2_overlay(&c).unwrap();
12932        assert!(
12933            overlay.contains_key(M2_KEY_BEHAVIOR),
12934            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
12935             `:behavior` carries a non-empty composite — the accessor \
12936             and the M2 overlay emitter must route through the same \
12937             substrate-primitive typed dispatch on the outer :behavior \
12938             composite (got overlay={overlay:?})",
12939        );
12940        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
12941        let overlay = servico_m2_overlay(&c).unwrap();
12942        assert!(
12943            !overlay.contains_key(M2_KEY_BEHAVIOR),
12944            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
12945             `:behavior` is Some(BehaviorSpec::default()) — the empty \
12946             composite's `.is_empty()`-gated inner arm must elide the \
12947             key regardless of the outer presence bit (got \
12948             overlay={overlay:?})",
12949        );
12950        let c = caixa_with_behavior(None);
12951        let overlay = servico_m2_overlay(&c).unwrap();
12952        assert!(
12953            !overlay.contains_key(M2_KEY_BEHAVIOR),
12954            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
12955             `:behavior` is None — the author-omitted arm must route \
12956             through the accessor's None-return unchanged (got \
12957             overlay={overlay:?})",
12958        );
12959    }
12960
12961    #[test]
12962    fn behavior_projects_option_ref_by_borrow() {
12963        // The by-borrow pin: [`Caixa::behavior`] returns
12964        // `Option<&BehaviorSpec>` by borrow — the returned reference
12965        // borrows the underlying `Option<BehaviorSpec>` storage of the
12966        // `:behavior` slot and the accessor must not clone the backing
12967        // composite on every call. Peer of the sibling
12968        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
12969        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
12970        // return sub-family — extended here to the second axis of the
12971        // same sub-family: the accessor's returned reference must
12972        // borrow from `&self` (the returned reference's lifetime is
12973        // tied to `&self`), and calling the accessor twice on the same
12974        // [`Caixa`] must yield references that are pointer-equal (the
12975        // underlying byte-buffer is the storage `BehaviorSpec`'s
12976        // allocation, not a fresh copy) as well as value-equal
12977        // (idempotent, no side effects on `&self`).
12978        //
12979        // Pins against a future silent detour that returned an owned
12980        // `BehaviorSpec` (which would type-check via the `Clone` impl
12981        // but silently clone on every call), a `&BehaviorSpec` panic-
12982        // return on the `None` arm (which would collapse the load-
12983        // bearing `Option` presence-bit into a runtime panic), or a
12984        // one-arm-only accessor that returned a saturating composite
12985        // on some sentinel input.
12986        use crate::BehaviorSpec;
12987        use std::path::PathBuf;
12988        for behavior in [
12989            Some(BehaviorSpec::default()),
12990            Some(BehaviorSpec {
12991                on_init: Some(PathBuf::from("lib/init.lisp")),
12992                on_call: Some(PathBuf::from("lib/handlers.lisp")),
12993                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
12994                on_info: Some(PathBuf::from("lib/handlers.lisp")),
12995                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
12996                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
12997            }),
12998        ] {
12999            let c = caixa_with_behavior(behavior.clone());
13000            let first = c.behavior().unwrap();
13001            let second = c.behavior().unwrap();
13002            assert_eq!(
13003                first, second,
13004                "Caixa::behavior must be idempotent — two successive \
13005                 calls on the same &self must return the same \
13006                 &BehaviorSpec",
13007            );
13008            assert!(
13009                std::ptr::eq(first, second),
13010                "Caixa::behavior must borrow the underlying \
13011                 Option<BehaviorSpec> storage — two successive calls \
13012                 must return references with the same backing pointer \
13013                 (a fresh BehaviorSpec clone would change the pointer \
13014                 on every call)",
13015            );
13016            assert_eq!(
13017                Some(first),
13018                behavior.as_ref(),
13019                "Caixa::behavior must return :behavior verbatim by \
13020                 borrow — got {first:?}, expected {:?}",
13021                behavior.as_ref(),
13022            );
13023        }
13024        let c = caixa_with_behavior(None);
13025        assert!(
13026            c.behavior().is_none(),
13027            "Caixa::behavior must return None when :behavior is absent \
13028             — the author-omitted arm must project through the \
13029             accessor's Option::None unchanged",
13030        );
13031    }
13032
13033    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
13034
13035    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
13036        use crate::aplicacao::{Membro, WitContract};
13037        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13038        c.kind = CaixaKind::Aplicacao;
13039        c.membros = vec![Membro {
13040            caixa: "a".into(),
13041            versao: "^0.1".into(),
13042        }];
13043        c.contratos = vec![WitContract {
13044            de: "a".into(),
13045            para: "a".into(),
13046            wit: "wasi:http/proxy".into(),
13047            endpoint: Some("/x".into()),
13048            subject: None,
13049            slot: None,
13050        }];
13051        c.politicas = politicas;
13052        c
13053    }
13054
13055    #[test]
13056    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
13057        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
13058        // composite optional-composite-reference-shape pin:
13059        // [`Caixa::politicas`] must return the `:politicas` typed
13060        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
13061        // reference over the same backing storage the raw
13062        // `self.politicas.as_ref()` field access borrows from,
13063        // byte-equal across every representative fixture in the
13064        // accept-set — the author-omitted `None` shape (the "cluster-
13065        // default applies" partition every downstream mesh-artifact
13066        // emitter treats as "emit no `:politicas` overlay"), the
13067        // empty-composite `Some(MeshPolicy { .. default })` shape
13068        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
13069        // per-axis mesh-policy scalar is `None`, so the peer inner
13070        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
13071        // caixa-mesh overlay elides every per-axis emit but the outer
13072        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
13073        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
13074        // single-axis fixture (only `:timeout` set — the canonical
13075        // shape a latency-sensitive Aplicacao carries), and a
13076        // fully-populated composite (every per-axis mesh-policy
13077        // scalar set — the canonical shape a fully-governed
13078        // Aplicacao carries).
13079        //
13080        // Pins against a future silent detour that returned a fresh-
13081        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
13082        // type-check via the `Clone` impl but silently break every
13083        // downstream caller that relied on the reference sharing the
13084        // composite's backing identity), a reference to an operator-
13085        // resolved overlay (the future per-cluster
13086        // `:politicas-overrides` slot — its resolution must land at
13087        // exactly this accessor body, not silently divert the raw
13088        // slot away from the peer [`Caixa::declared_mesh_slots`]
13089        // enumerator's presence probe), a
13090        // `None` → `Some(MeshPolicy::default)` cluster-default
13091        // projection (which would collapse the load-bearing
13092        // "author-omitted `:politicas` ⇒ cluster-default applies"
13093        // partition the peer [`Caixa::declared_mesh_slots`]
13094        // enumerator and the peer [`Caixa::aplicacao_view`]
13095        // Aplicacao-composition seed both read), or an axis-shuffled
13096        // projection (a future detour that swapped `timeout` and
13097        // `retries` through the accessor would silently split the
13098        // paired [`Caixa::aplicacao_view`] seed's fold input from the
13099        // sibling M3 mesh-artifact emitter's projection input).
13100        //
13101        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
13102        // composite-reference accessor pin on the substrate primitive
13103        // — peer of the sibling
13104        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13105        // (b2bd9d7) and
13106        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
13107        // (35d8b52) opening tetrad pins on the outer top-level
13108        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
13109        // here to the first of the three M3 mesh-slot axes so the
13110        // opening third of the outer `Option<&Composite>` sub-family
13111        // carries the same "byte-equal, borrow-shared, presence-bit-
13112        // preserved" outer-accessor discipline.
13113        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
13114        use std::time::Duration;
13115        let fixtures: Vec<Option<MeshPolicy>> = vec![
13116            None,
13117            Some(MeshPolicy::default()),
13118            Some(MeshPolicy {
13119                timeout: Some(Duration::from_secs(30)),
13120                ..Default::default()
13121            }),
13122            Some(MeshPolicy {
13123                timeout: Some(Duration::from_secs(30)),
13124                retries: Some(3),
13125                circuit_breaker: Some(CircuitBreaker {
13126                    max_failures: 5,
13127                    window: Duration::from_secs(60),
13128                }),
13129                mtls_required: Some(true),
13130                rate_limit: Some(RateLimit {
13131                    rate: 100,
13132                    window: Duration::from_secs(1),
13133                }),
13134            }),
13135        ];
13136        for politicas in fixtures {
13137            let c = caixa_aplicacao_with_politicas(politicas.clone());
13138            assert_eq!(
13139                c.politicas(),
13140                politicas.as_ref(),
13141                "Caixa::politicas must return :politicas verbatim (got \
13142                 {:?}, expected {:?})",
13143                c.politicas(),
13144                politicas.as_ref(),
13145            );
13146            match (c.politicas(), c.politicas.as_ref()) {
13147                (Some(a), Some(b)) => assert!(
13148                    std::ptr::eq(a, b),
13149                    "Caixa::politicas accessor and self.politicas.as_ref() \
13150                     field access must borrow the same backing storage \
13151                     — the accessor is the substrate-primitive typed \
13152                     dispatch every downstream Aplicacao-mesh-overlay \
13153                     composite consumer must route through, and a \
13154                     reference-identity split would silently break \
13155                     every consumer that relied on the borrow sharing \
13156                     the composite's storage",
13157                ),
13158                (None, None) => {}
13159                _ => panic!(
13160                    "Caixa::politicas presence bit must byte-equal \
13161                     self.politicas.is_some() — a presence-bit drift \
13162                     would silently split the paired \
13163                     Caixa::aplicacao_view Aplicacao-composition seed's \
13164                     traversal head from the peer \
13165                     Caixa::declared_mesh_slots M3 declared-slot \
13166                     enumerator's presence probe",
13167                ),
13168            }
13169            assert_eq!(
13170                c.politicas().is_some(),
13171                c.politicas.is_some(),
13172                "Caixa::politicas().is_some() must byte-equal \
13173                 self.politicas.is_some() — a presence-bit drift would \
13174                 silently split every downstream Option<&MeshPolicy> \
13175                 consumer's partition on the cluster-default arm",
13176            );
13177        }
13178    }
13179
13180    #[test]
13181    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
13182        // Composition pin: [`Caixa::declared_mesh_slots`]'s
13183        // `:politicas` presence-probe arm must key off
13184        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
13185        // field-probe. Structurally: a `Caixa { politicas:
13186        // Some(MeshPolicy::default()), .. }` must still push
13187        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
13188        // presence bit is `Some`, so the M3 kind-coherence gate must
13189        // surface the slot as "declared" even when every per-axis
13190        // scalar is unset), and a `Caixa { politicas: None, .. }` must
13191        // NOT push the label (the "author omitted the slot entirely"
13192        // partition). The pair jointly pins the accessor + declared-
13193        // slot enumerator composition: any future silent detour that
13194        // had the accessor collapse `Some(MeshPolicy::default())` to
13195        // `None` (a `.filter(|p| !p.is_empty())` projection) would
13196        // silently absorb the "declared but empty" arm at the
13197        // accessor boundary and the
13198        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
13199        // coherence gate would silently accept a struct-literal
13200        // `Caixa` carrying the drift.
13201        //
13202        // Peer of the sibling
13203        // `declared_servico_slots_limits_arm_routes_through_accessor`
13204        // (b2bd9d7) and
13205        // `declared_servico_slots_behavior_arm_routes_through_accessor`
13206        // (35d8b52) composition pins on the sibling `:limits` /
13207        // `:behavior` outer-`Option<&Composite>` arms of the peer
13208        // [`Caixa::declared_servico_slots`] M2 declared-slot
13209        // enumerator's traversal — same "the enumerator gate must
13210        // route through the substrate-primitive typed dispatch"
13211        // discipline extended onto the outer top-level [`Caixa`] M3
13212        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
13213        // enumerator carries the same routing invariant as its M2
13214        // sibling.
13215        use crate::aplicacao::MeshPolicy;
13216        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
13217        let slots = c.declared_mesh_slots();
13218        assert!(
13219            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
13220            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
13221             when `:politicas` is Some (even for MeshPolicy::default()) \
13222             — the accessor and the enumerator gate must route through \
13223             the same substrate-primitive typed dispatch on the outer \
13224             :politicas presence bit (got slots={slots:?})",
13225        );
13226        let c = caixa_aplicacao_with_politicas(None);
13227        let slots = c.declared_mesh_slots();
13228        assert!(
13229            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
13230            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
13231             when `:politicas` is None — the author-omitted arm must \
13232             route through the accessor's None-return unchanged (got \
13233             slots={slots:?})",
13234        );
13235    }
13236
13237    #[test]
13238    fn aplicacao_view_politicas_arm_folds_through_accessor() {
13239        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
13240        // Aplicacao-composition seed must fold through
13241        // [`Caixa::politicas`], not the raw
13242        // `self.politicas.clone().unwrap_or_default()` field-borrow.
13243        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
13244        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
13245        // must surface a projected [`crate::AplicacaoSpec`] whose
13246        // `politicas().timeout()` field byte-equals the outer
13247        // composite's `timeout` scalar (the fold must project the
13248        // authored composite verbatim), a `Caixa { politicas:
13249        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
13250        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
13251        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
13252        // fold's empty-composite arm collapses to the same default the
13253        // author-omitted arm does), and a `Caixa { politicas: None,
13254        // kind: Aplicacao, .. }` must surface an
13255        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
13256        // [`crate::aplicacao::MeshPolicy::default`] (the "author
13257        // omitted the slot entirely" arm folds through the
13258        // `unwrap_or_default` onto the cluster-default). The triad
13259        // jointly pins the accessor + Aplicacao-composition seed
13260        // composition: any future silent detour that had the accessor
13261        // divert the raw slot away from the seed's fold (an operator-
13262        // resolved overlay's default-fold arm silently differing from
13263        // the raw slot's default-fold arm) would silently split the
13264        // build-time mesh-artifact emission gate from the caixa-mesh
13265        // renderer's Aplicacao-view input at the composition boundary.
13266        use crate::aplicacao::MeshPolicy;
13267        use std::time::Duration;
13268        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
13269            timeout: Some(Duration::from_secs(30)),
13270            ..Default::default()
13271        }));
13272        let view = c.aplicacao_view().unwrap();
13273        assert_eq!(
13274            view.politicas().timeout(),
13275            Some(Duration::from_secs(30)),
13276            "Caixa::aplicacao_view must fold the authored :politicas \
13277             :timeout scalar through the accessor verbatim onto the \
13278             projected AplicacaoSpec — a future silent detour at the \
13279             seed's fold arm would surface here as a projected-scalar \
13280             drift (got {:?})",
13281            view.politicas().timeout(),
13282        );
13283        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
13284        let view = c.aplicacao_view().unwrap();
13285        assert_eq!(
13286            view.politicas(),
13287            &MeshPolicy::default(),
13288            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
13289             through the accessor onto MeshPolicy::default — the empty- \
13290             composite arm collapses to the same default the author- \
13291             omitted arm does (got {:?})",
13292            view.politicas(),
13293        );
13294        let c = caixa_aplicacao_with_politicas(None);
13295        let view = c.aplicacao_view().unwrap();
13296        assert_eq!(
13297            view.politicas(),
13298            &MeshPolicy::default(),
13299            "Caixa::aplicacao_view must fold None through the accessor's \
13300             unwrap_or_default onto MeshPolicy::default — the author- \
13301             omitted arm must route through the accessor's None-return \
13302             unchanged (got {:?})",
13303            view.politicas(),
13304        );
13305    }
13306
13307    #[test]
13308    fn politicas_projects_option_ref_by_borrow() {
13309        // The by-borrow pin: [`Caixa::politicas`] returns
13310        // `Option<&MeshPolicy>` by borrow — the returned reference
13311        // borrows the underlying `Option<MeshPolicy>` storage of the
13312        // `:politicas` slot and the accessor must not clone the
13313        // backing composite on every call. Peer of the sibling
13314        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
13315        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
13316        // pins on the outer top-level [`Caixa`]
13317        // `Option<&Composite>`-return sub-family — extended here to
13318        // the third axis of the same sub-family: the accessor's
13319        // returned reference must borrow from `&self` (the returned
13320        // reference's lifetime is tied to `&self`), and calling the
13321        // accessor twice on the same [`Caixa`] must yield references
13322        // that are pointer-equal (the underlying byte-buffer is the
13323        // storage `MeshPolicy`'s allocation, not a fresh copy) as
13324        // well as value-equal (idempotent, no side effects on
13325        // `&self`).
13326        //
13327        // Pins against a future silent detour that returned an owned
13328        // `MeshPolicy` (which would type-check via the `Clone` impl
13329        // but silently clone on every call), a `&MeshPolicy` panic-
13330        // return on the `None` arm (which would collapse the load-
13331        // bearing `Option` presence-bit into a runtime panic), or a
13332        // one-arm-only accessor that returned a saturating composite
13333        // on some sentinel input.
13334        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
13335        use std::time::Duration;
13336        for politicas in [
13337            Some(MeshPolicy::default()),
13338            Some(MeshPolicy {
13339                timeout: Some(Duration::from_secs(30)),
13340                retries: Some(3),
13341                circuit_breaker: Some(CircuitBreaker {
13342                    max_failures: 5,
13343                    window: Duration::from_secs(60),
13344                }),
13345                mtls_required: Some(true),
13346                rate_limit: Some(RateLimit {
13347                    rate: 100,
13348                    window: Duration::from_secs(1),
13349                }),
13350            }),
13351        ] {
13352            let c = caixa_aplicacao_with_politicas(politicas.clone());
13353            let first = c.politicas().unwrap();
13354            let second = c.politicas().unwrap();
13355            assert_eq!(
13356                first, second,
13357                "Caixa::politicas must be idempotent — two successive \
13358                 calls on the same &self must return the same \
13359                 &MeshPolicy",
13360            );
13361            assert!(
13362                std::ptr::eq(first, second),
13363                "Caixa::politicas must borrow the underlying \
13364                 Option<MeshPolicy> storage — two successive calls \
13365                 must return references with the same backing pointer \
13366                 (a fresh MeshPolicy clone would change the pointer on \
13367                 every call)",
13368            );
13369            assert_eq!(
13370                Some(first),
13371                politicas.as_ref(),
13372                "Caixa::politicas must return :politicas verbatim by \
13373                 borrow — got {first:?}, expected {:?}",
13374                politicas.as_ref(),
13375            );
13376        }
13377        let c = caixa_aplicacao_with_politicas(None);
13378        assert!(
13379            c.politicas().is_none(),
13380            "Caixa::politicas must return None when :politicas is \
13381             absent — the author-omitted arm must project through the \
13382             accessor's Option::None unchanged",
13383        );
13384    }
13385
13386    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
13387
13388    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
13389        use crate::aplicacao::{Membro, WitContract};
13390        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13391        c.kind = CaixaKind::Aplicacao;
13392        c.membros = vec![Membro {
13393            caixa: "a".into(),
13394            versao: "^0.1".into(),
13395        }];
13396        c.contratos = vec![WitContract {
13397            de: "a".into(),
13398            para: "a".into(),
13399            wit: "wasi:http/proxy".into(),
13400            endpoint: Some("/x".into()),
13401            subject: None,
13402            slot: None,
13403        }];
13404        c.placement = placement;
13405        c
13406    }
13407
13408    #[test]
13409    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
13410        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
13411        // composite optional-composite-reference-shape pin:
13412        // [`Caixa::placement`] must return the `:placement` typed
13413        // `Option<Placement>` verbatim as an `Option<&Placement>`
13414        // reference over the same backing storage the raw
13415        // `self.placement.as_ref()` field access borrows from,
13416        // byte-equal across every representative fixture in the
13417        // accept-set — the author-omitted `None` shape (the
13418        // "cluster-default applies" partition every downstream mesh-
13419        // artifact emitter treats as "emit no `:placement` overlay"),
13420        // the empty-composite `Some(Placement { .. default })` shape
13421        // (`estrategia: SingleNode`, empty clusters, no shard-key /
13422        // affinity — the outer presence-bit is `Some` so
13423        // [`Caixa::declared_mesh_slots`] still pushes the
13424        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
13425        // `Replicated`-on-two-clusters fixture (the canonical shape a
13426        // stateless HTTP Aplicacao carries), and a fully-populated
13427        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
13428        // shape a stateful Akka-style cluster-sharding Aplicacao
13429        // carries).
13430        //
13431        // Pins against a future silent detour that returned a fresh-
13432        // cloned [`crate::aplicacao::Placement`] copy (which would
13433        // type-check via the `Clone` impl but silently break every
13434        // downstream caller that relied on the reference sharing the
13435        // composite's backing identity), a reference to an operator-
13436        // resolved overlay (the future per-cluster
13437        // `:placement-overrides` slot — its resolution must land at
13438        // exactly this accessor body, not silently divert the raw
13439        // slot away from the peer [`Caixa::declared_mesh_slots`]
13440        // enumerator's presence probe), a `None` →
13441        // `Some(Placement::default)` cluster-default projection (which
13442        // would collapse the load-bearing "author-omitted `:placement`
13443        // ⇒ cluster-default applies" partition the peer
13444        // [`Caixa::declared_mesh_slots`] enumerator and the peer
13445        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
13446        // read), or an axis-shuffled projection (a future detour that
13447        // swapped `clusters` and `affinity` through the accessor would
13448        // silently split the paired [`Caixa::aplicacao_view`] seed's
13449        // fold input from the sibling M3 mesh-artifact emitter's
13450        // projection input).
13451        //
13452        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
13453        // composite-reference accessor pin on the substrate primitive
13454        // — peer of the sibling
13455        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13456        // (b2bd9d7),
13457        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
13458        // (35d8b52), and
13459        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
13460        // (5d23d29) opening triad pins on the outer top-level
13461        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
13462        // here to the second of the three M3 mesh-slot axes so the
13463        // opening four-fifths of the outer `Option<&Composite>` sub-
13464        // family carries the same "byte-equal, borrow-shared,
13465        // presence-bit-preserved" outer-accessor discipline.
13466        use crate::aplicacao::{Placement, PlacementStrategy};
13467        let fixtures: Vec<Option<Placement>> = vec![
13468            None,
13469            Some(Placement::default()),
13470            Some(Placement {
13471                estrategia: PlacementStrategy::Replicated,
13472                clusters: vec!["rio".into(), "sao-paulo".into()],
13473                affinity: None,
13474                shard_key: None,
13475            }),
13476            Some(Placement {
13477                estrategia: PlacementStrategy::Sharded,
13478                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
13479                affinity: Some("data-locality".into()),
13480                shard_key: Some("$tenantId".into()),
13481            }),
13482        ];
13483        for placement in fixtures {
13484            let c = caixa_aplicacao_with_placement(placement.clone());
13485            assert_eq!(
13486                c.placement(),
13487                placement.as_ref(),
13488                "Caixa::placement must return :placement verbatim (got \
13489                 {:?}, expected {:?})",
13490                c.placement(),
13491                placement.as_ref(),
13492            );
13493            match (c.placement(), c.placement.as_ref()) {
13494                (Some(a), Some(b)) => assert!(
13495                    std::ptr::eq(a, b),
13496                    "Caixa::placement accessor and self.placement.as_ref() \
13497                     field access must borrow the same backing storage \
13498                     — the accessor is the substrate-primitive typed \
13499                     dispatch every downstream Aplicacao-distribution- \
13500                     overlay composite consumer must route through, and \
13501                     a reference-identity split would silently break \
13502                     every consumer that relied on the borrow sharing \
13503                     the composite's storage",
13504                ),
13505                (None, None) => {}
13506                _ => panic!(
13507                    "Caixa::placement presence bit must byte-equal \
13508                     self.placement.is_some() — a presence-bit drift \
13509                     would silently split the paired \
13510                     Caixa::aplicacao_view Aplicacao-composition seed's \
13511                     traversal head from the peer \
13512                     Caixa::declared_mesh_slots M3 declared-slot \
13513                     enumerator's presence probe",
13514                ),
13515            }
13516            assert_eq!(
13517                c.placement().is_some(),
13518                c.placement.is_some(),
13519                "Caixa::placement().is_some() must byte-equal \
13520                 self.placement.is_some() — a presence-bit drift would \
13521                 silently split every downstream Option<&Placement> \
13522                 consumer's partition on the cluster-default arm",
13523            );
13524        }
13525    }
13526
13527    #[test]
13528    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
13529        // Composition pin: [`Caixa::declared_mesh_slots`]'s
13530        // `:placement` presence-probe arm must key off
13531        // [`Caixa::placement`], not the raw `self.placement.is_some()`
13532        // field-probe. Structurally: a `Caixa { placement:
13533        // Some(Placement::default()), .. }` must still push
13534        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
13535        // presence bit is `Some`, so the M3 kind-coherence gate must
13536        // surface the slot as "declared" even when every per-axis
13537        // scalar defers to the cluster-default arm), and a `Caixa {
13538        // placement: None, .. }` must NOT push the label (the "author
13539        // omitted the slot entirely" partition). The pair jointly pins
13540        // the accessor + declared-slot enumerator composition: any
13541        // future silent detour that had the accessor collapse
13542        // `Some(Placement::default())` to `None` (a `.filter(|p|
13543        // p.clusters().is_empty().not())` projection) would silently
13544        // absorb the "declared but empty" arm at the accessor boundary
13545        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
13546        // kind-coherence gate would silently accept a struct-literal
13547        // `Caixa` carrying the drift.
13548        //
13549        // Peer of the sibling
13550        // `declared_servico_slots_limits_arm_routes_through_accessor`
13551        // (b2bd9d7),
13552        // `declared_servico_slots_behavior_arm_routes_through_accessor`
13553        // (35d8b52), and
13554        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
13555        // (5d23d29) composition pins on the sibling `:limits` /
13556        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
13557        // — same "the enumerator gate must route through the
13558        // substrate-primitive typed dispatch" discipline extended onto
13559        // the second of the three M3 mesh-slot axes so the
13560        // [`Caixa::declared_mesh_slots`] enumerator carries the same
13561        // routing invariant on the `:placement` arm as the peer
13562        // `:politicas` arm.
13563        use crate::aplicacao::Placement;
13564        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
13565        let slots = c.declared_mesh_slots();
13566        assert!(
13567            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
13568            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
13569             when `:placement` is Some (even for Placement::default()) \
13570             — the accessor and the enumerator gate must route through \
13571             the same substrate-primitive typed dispatch on the outer \
13572             :placement presence bit (got slots={slots:?})",
13573        );
13574        let c = caixa_aplicacao_with_placement(None);
13575        let slots = c.declared_mesh_slots();
13576        assert!(
13577            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
13578            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
13579             when `:placement` is None — the author-omitted arm must \
13580             route through the accessor's None-return unchanged (got \
13581             slots={slots:?})",
13582        );
13583    }
13584
13585    #[test]
13586    fn aplicacao_view_placement_arm_folds_through_accessor() {
13587        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
13588        // Aplicacao-composition seed must fold through
13589        // [`Caixa::placement`], not the raw
13590        // `self.placement.clone().unwrap_or_default()` field-borrow.
13591        // Structurally: a `Caixa { placement: Some(Placement {
13592        // estrategia: Replicated, clusters: ["rio"], .. default }),
13593        // kind: Aplicacao, .. }` must surface a projected
13594        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
13595        // `placement().clusters()` byte-equal the outer composite's
13596        // authored values (the fold must project the authored
13597        // composite verbatim), a `Caixa { placement:
13598        // Some(Placement::default()), kind: Aplicacao, .. }` must
13599        // surface an [`crate::AplicacaoSpec`] whose `placement()`
13600        // byte-equals [`crate::aplicacao::Placement::default`] (the
13601        // fold's empty-composite arm collapses to the same default
13602        // the author-omitted arm does), and a `Caixa { placement:
13603        // None, kind: Aplicacao, .. }` must surface an
13604        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
13605        // [`crate::aplicacao::Placement::default`] (the "author
13606        // omitted the slot entirely" arm folds through the
13607        // `unwrap_or_default` onto the cluster-default). The triad
13608        // jointly pins the accessor + Aplicacao-composition seed
13609        // composition: any future silent detour that had the accessor
13610        // divert the raw slot away from the seed's fold (an operator-
13611        // resolved overlay's default-fold arm silently differing from
13612        // the raw slot's default-fold arm) would silently split the
13613        // build-time distribution-artifact emission gate from the
13614        // caixa-mesh renderer's Aplicacao-view input at the
13615        // composition boundary.
13616        use crate::aplicacao::{Placement, PlacementStrategy};
13617        let c = caixa_aplicacao_with_placement(Some(Placement {
13618            estrategia: PlacementStrategy::Replicated,
13619            clusters: vec!["rio".into()],
13620            affinity: None,
13621            shard_key: None,
13622        }));
13623        let view = c.aplicacao_view().unwrap();
13624        assert_eq!(
13625            view.placement().estrategia(),
13626            PlacementStrategy::Replicated,
13627            "Caixa::aplicacao_view must fold the authored :placement \
13628             :estrategia scalar through the accessor verbatim onto the \
13629             projected AplicacaoSpec — a future silent detour at the \
13630             seed's fold arm would surface here as a projected-scalar \
13631             drift (got {:?})",
13632            view.placement().estrategia(),
13633        );
13634        assert_eq!(
13635            view.placement().clusters(),
13636            &["rio"],
13637            "Caixa::aplicacao_view must fold the authored :placement \
13638             :clusters list through the accessor verbatim onto the \
13639             projected AplicacaoSpec — a future silent detour at the \
13640             seed's fold arm would surface here as a projected-list \
13641             drift (got {:?})",
13642            view.placement().clusters(),
13643        );
13644        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
13645        let view = c.aplicacao_view().unwrap();
13646        assert_eq!(
13647            view.placement(),
13648            &Placement::default(),
13649            "Caixa::aplicacao_view must fold Some(Placement::default()) \
13650             through the accessor onto Placement::default — the empty- \
13651             composite arm collapses to the same default the author- \
13652             omitted arm does (got {:?})",
13653            view.placement(),
13654        );
13655        let c = caixa_aplicacao_with_placement(None);
13656        let view = c.aplicacao_view().unwrap();
13657        assert_eq!(
13658            view.placement(),
13659            &Placement::default(),
13660            "Caixa::aplicacao_view must fold None through the accessor's \
13661             unwrap_or_default onto Placement::default — the author- \
13662             omitted arm must route through the accessor's None-return \
13663             unchanged (got {:?})",
13664            view.placement(),
13665        );
13666    }
13667
13668    #[test]
13669    fn placement_projects_option_ref_by_borrow() {
13670        // The by-borrow pin: [`Caixa::placement`] returns
13671        // `Option<&Placement>` by borrow — the returned reference
13672        // borrows the underlying `Option<Placement>` storage of the
13673        // `:placement` slot and the accessor must not clone the
13674        // backing composite on every call. Peer of the sibling
13675        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
13676        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
13677        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
13678        // pins on the outer top-level [`Caixa`]
13679        // `Option<&Composite>`-return sub-family — extended here to
13680        // the fourth axis of the same sub-family: the accessor's
13681        // returned reference must borrow from `&self` (the returned
13682        // reference's lifetime is tied to `&self`), and calling the
13683        // accessor twice on the same [`Caixa`] must yield references
13684        // that are pointer-equal (the underlying byte-buffer is the
13685        // storage `Placement`'s allocation, not a fresh copy) as well
13686        // as value-equal (idempotent, no side effects on `&self`).
13687        //
13688        // Pins against a future silent detour that returned an owned
13689        // `Placement` (which would type-check via the `Clone` impl
13690        // but silently clone on every call), a `&Placement` panic-
13691        // return on the `None` arm (which would collapse the load-
13692        // bearing `Option` presence-bit into a runtime panic), or a
13693        // one-arm-only accessor that returned a saturating composite
13694        // on some sentinel input.
13695        use crate::aplicacao::{Placement, PlacementStrategy};
13696        for placement in [
13697            Some(Placement::default()),
13698            Some(Placement {
13699                estrategia: PlacementStrategy::Sharded,
13700                clusters: vec!["rio".into(), "sao-paulo".into()],
13701                affinity: Some("data-locality".into()),
13702                shard_key: Some("$tenantId".into()),
13703            }),
13704        ] {
13705            let c = caixa_aplicacao_with_placement(placement.clone());
13706            let first = c.placement().unwrap();
13707            let second = c.placement().unwrap();
13708            assert_eq!(
13709                first, second,
13710                "Caixa::placement must be idempotent — two successive \
13711                 calls on the same &self must return the same \
13712                 &Placement",
13713            );
13714            assert!(
13715                std::ptr::eq(first, second),
13716                "Caixa::placement must borrow the underlying \
13717                 Option<Placement> storage — two successive calls \
13718                 must return references with the same backing pointer \
13719                 (a fresh Placement clone would change the pointer on \
13720                 every call)",
13721            );
13722            assert_eq!(
13723                Some(first),
13724                placement.as_ref(),
13725                "Caixa::placement must return :placement verbatim by \
13726                 borrow — got {first:?}, expected {:?}",
13727                placement.as_ref(),
13728            );
13729        }
13730        let c = caixa_aplicacao_with_placement(None);
13731        assert!(
13732            c.placement().is_none(),
13733            "Caixa::placement must return None when :placement is \
13734             absent — the author-omitted arm must project through the \
13735             accessor's Option::None unchanged",
13736        );
13737    }
13738
13739    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
13740
13741    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
13742        use crate::aplicacao::{Membro, WitContract};
13743        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13744        c.kind = CaixaKind::Aplicacao;
13745        c.membros = vec![Membro {
13746            caixa: "a".into(),
13747            versao: "^0.1".into(),
13748        }];
13749        c.contratos = vec![WitContract {
13750            de: "a".into(),
13751            para: "a".into(),
13752            wit: "wasi:http/proxy".into(),
13753            endpoint: Some("/x".into()),
13754            subject: None,
13755            slot: None,
13756        }];
13757        c.entrada = entrada;
13758        c
13759    }
13760
13761    #[test]
13762    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
13763        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
13764        // composite optional-composite-reference-shape pin:
13765        // [`Caixa::entrada`] must return the `:entrada` typed
13766        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
13767        // reference over the same backing storage the raw
13768        // `self.entrada.as_ref()` field access borrows from,
13769        // byte-equal across every representative fixture in the
13770        // accept-set — the author-omitted `None` shape (the
13771        // "cluster-internal Aplicacao" partition every downstream
13772        // Gateway-API emitter treats as "emit no listener + no
13773        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
13774        // (empty `paths` — the resolved-paths fallback the peer
13775        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
13776        // onto the substrate catch-all), and a fully-populated
13777        // multi-path-with-non-default-port fixture (the canonical
13778        // shape a public HTTP Aplicacao carries).
13779        //
13780        // Pins against a future silent detour that returned a fresh-
13781        // cloned [`crate::aplicacao::Entrada`] copy (which would
13782        // type-check via the `Clone` impl but silently break every
13783        // downstream caller that relied on the reference sharing the
13784        // composite's backing identity), a reference to an operator-
13785        // resolved overlay (the future per-cluster
13786        // `:entrada-overrides` slot — its resolution must land at
13787        // exactly this accessor body, not silently divert the raw
13788        // slot away from the peer [`Caixa::declared_mesh_slots`]
13789        // enumerator's presence probe), or an axis-shuffled projection
13790        // (a future detour that swapped `host` and `para` through the
13791        // accessor would silently split the paired
13792        // [`Caixa::aplicacao_view`] seed's forward input from the
13793        // sibling M3 gateway-artifact emitter's projection input).
13794        //
13795        // Fifth and final outer top-level [`Caixa`]
13796        // `Option<&Composite>`-return composite-reference accessor pin
13797        // on the substrate primitive — peer of the sibling
13798        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13799        // (b2bd9d7),
13800        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
13801        // (35d8b52),
13802        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
13803        // (5d23d29), and
13804        // `placement_returns_placement_option_ref_verbatim_across_permutations`
13805        // (4fb8074) opening tetrad pins on the outer top-level
13806        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
13807        // here to the third and final M3 mesh-slot axis so the closed
13808        // outer `Option<&Composite>` sub-family carries the same
13809        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
13810        // accessor discipline across all five arms.
13811        use crate::aplicacao::Entrada;
13812        let fixtures: Vec<Option<Entrada>> = vec![
13813            None,
13814            Some(Entrada {
13815                host: "checkout.quero.cloud".into(),
13816                para: "gateway".into(),
13817                paths: Vec::new(),
13818                port: crate::DEFAULT_SERVICO_PORT,
13819            }),
13820            Some(Entrada {
13821                host: "api.pleme.io".into(),
13822                para: "public-api".into(),
13823                paths: vec!["/v1".into(), "/v2".into()],
13824                port: 8080,
13825            }),
13826        ];
13827        for entrada in fixtures {
13828            let c = caixa_aplicacao_with_entrada(entrada.clone());
13829            assert_eq!(
13830                c.entrada(),
13831                entrada.as_ref(),
13832                "Caixa::entrada must return :entrada verbatim (got \
13833                 {:?}, expected {:?})",
13834                c.entrada(),
13835                entrada.as_ref(),
13836            );
13837            match (c.entrada(), c.entrada.as_ref()) {
13838                (Some(a), Some(b)) => assert!(
13839                    std::ptr::eq(a, b),
13840                    "Caixa::entrada accessor and self.entrada.as_ref() \
13841                     field access must borrow the same backing storage \
13842                     — the accessor is the substrate-primitive typed \
13843                     dispatch every downstream Aplicacao-external- \
13844                     gateway composite consumer must route through, and \
13845                     a reference-identity split would silently break \
13846                     every consumer that relied on the borrow sharing \
13847                     the composite's storage",
13848                ),
13849                (None, None) => {}
13850                _ => panic!(
13851                    "Caixa::entrada presence bit must byte-equal \
13852                     self.entrada.is_some() — a presence-bit drift \
13853                     would silently split the paired \
13854                     Caixa::aplicacao_view Aplicacao-composition seed's \
13855                     traversal head from the peer \
13856                     Caixa::declared_mesh_slots M3 declared-slot \
13857                     enumerator's presence probe",
13858                ),
13859            }
13860            assert_eq!(
13861                c.entrada().is_some(),
13862                c.entrada.is_some(),
13863                "Caixa::entrada().is_some() must byte-equal \
13864                 self.entrada.is_some() — a presence-bit drift would \
13865                 silently split every downstream Option<&Entrada> \
13866                 consumer's partition on the cluster-internal arm",
13867            );
13868        }
13869    }
13870
13871    #[test]
13872    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
13873        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
13874        // presence-probe arm must key off [`Caixa::entrada`], not the
13875        // raw `self.entrada.is_some()` field-probe. Structurally: a
13876        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
13877        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
13878        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
13879        // presence bit is `Some`, so the M3 kind-coherence gate must
13880        // surface the slot as "declared" even when every per-axis
13881        // scalar defers to the substrate catch-all / default port),
13882        // and a `Caixa { entrada: None, .. }` must NOT push the label
13883        // (the "author omitted the slot entirely" partition). The pair
13884        // jointly pins the accessor + declared-slot enumerator
13885        // composition: any future silent detour that had the accessor
13886        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
13887        // `.filter(|e| !e.paths.is_empty())` projection) would silently
13888        // absorb the "declared but empty-paths" arm at the accessor
13889        // boundary and the
13890        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
13891        // coherence gate would silently accept a struct-literal
13892        // `Caixa` carrying the drift.
13893        //
13894        // Peer of the sibling
13895        // `declared_servico_slots_limits_arm_routes_through_accessor`
13896        // (b2bd9d7),
13897        // `declared_servico_slots_behavior_arm_routes_through_accessor`
13898        // (35d8b52),
13899        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
13900        // (5d23d29), and
13901        // `declared_mesh_slots_placement_arm_routes_through_accessor`
13902        // (4fb8074) composition pins on the sibling `:limits` /
13903        // `:behavior` / `:politicas` / `:placement` outer-
13904        // `Option<&Composite>` arms — same "the enumerator gate must
13905        // route through the substrate-primitive typed dispatch"
13906        // discipline extended onto the third and final M3 mesh-slot
13907        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
13908        // carries the routing invariant on every M3 mesh-slot arm.
13909        use crate::aplicacao::Entrada;
13910        let c = caixa_aplicacao_with_entrada(Some(Entrada {
13911            host: "checkout.quero.cloud".into(),
13912            para: "gateway".into(),
13913            paths: Vec::new(),
13914            port: crate::DEFAULT_SERVICO_PORT,
13915        }));
13916        let slots = c.declared_mesh_slots();
13917        assert!(
13918            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
13919            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
13920             `:entrada` is Some (even for empty-paths / default-port) \
13921             — the accessor and the enumerator gate must route through \
13922             the same substrate-primitive typed dispatch on the outer \
13923             :entrada presence bit (got slots={slots:?})",
13924        );
13925        let c = caixa_aplicacao_with_entrada(None);
13926        let slots = c.declared_mesh_slots();
13927        assert!(
13928            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
13929            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
13930             when `:entrada` is None — the author-omitted arm must \
13931             route through the accessor's None-return unchanged (got \
13932             slots={slots:?})",
13933        );
13934    }
13935
13936    #[test]
13937    fn aplicacao_view_entrada_arm_folds_through_accessor() {
13938        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
13939        // Aplicacao-composition seed must fold through
13940        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
13941        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
13942        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
13943        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
13944        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
13945        // equals the outer composite's authored value (the fold must
13946        // project the authored composite verbatim), and a `Caixa {
13947        // entrada: None, kind: Aplicacao, .. }` must surface an
13948        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
13949        // "author omitted the slot entirely" arm folds through the
13950        // accessor's `Option::cloned` onto the same `None` presence
13951        // bit — unlike the peer `:politicas` / `:placement` arms
13952        // `:entrada` has no cluster-default fold, the omitted arm
13953        // stays omitted). The pair jointly pins the accessor +
13954        // Aplicacao-composition seed composition: any future silent
13955        // detour that had the accessor divert the raw slot away from
13956        // the seed's fold (an operator-resolved overlay's forward arm
13957        // silently differing from the raw slot's forward arm) would
13958        // silently split the build-time gateway-artifact emission gate
13959        // from the caixa-mesh renderer's Aplicacao-view input at the
13960        // composition boundary.
13961        use crate::aplicacao::Entrada;
13962        let authored = Entrada {
13963            host: "api.pleme.io".into(),
13964            para: "public-api".into(),
13965            paths: vec!["/v1".into()],
13966            port: 8080,
13967        };
13968        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
13969        let view = c.aplicacao_view().unwrap();
13970        assert_eq!(
13971            view.entrada(),
13972            Some(&authored),
13973            "Caixa::aplicacao_view must fold the authored :entrada \
13974             composite through the accessor verbatim onto the \
13975             projected AplicacaoSpec — a future silent detour at the \
13976             seed's fold arm would surface here as a projected- \
13977             composite drift (got {:?})",
13978            view.entrada(),
13979        );
13980        let c = caixa_aplicacao_with_entrada(None);
13981        let view = c.aplicacao_view().unwrap();
13982        assert!(
13983            view.entrada().is_none(),
13984            "Caixa::aplicacao_view must fold None through the \
13985             accessor's Option::cloned onto None — the author- \
13986             omitted arm must route through the accessor's None-return \
13987             unchanged (got {:?})",
13988            view.entrada(),
13989        );
13990    }
13991
13992    #[test]
13993    fn entrada_projects_option_ref_by_borrow() {
13994        // The by-borrow pin: [`Caixa::entrada`] returns
13995        // `Option<&Entrada>` by borrow — the returned reference
13996        // borrows the underlying `Option<Entrada>` storage of the
13997        // `:entrada` slot and the accessor must not clone the backing
13998        // composite on every call. Peer of the sibling
13999        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
14000        // `behavior_projects_option_ref_by_borrow` (35d8b52),
14001        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
14002        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
14003        // borrow pins on the outer top-level [`Caixa`]
14004        // `Option<&Composite>`-return sub-family — extended here to
14005        // the fifth and final axis of the same sub-family, closing
14006        // the discipline: the accessor's returned reference must
14007        // borrow from `&self` (the returned reference's lifetime is
14008        // tied to `&self`), and calling the accessor twice on the
14009        // same [`Caixa`] must yield references that are pointer-equal
14010        // (the underlying byte-buffer is the storage `Entrada`'s
14011        // allocation, not a fresh copy) as well as value-equal
14012        // (idempotent, no side effects on `&self`).
14013        //
14014        // Pins against a future silent detour that returned an owned
14015        // `Entrada` (which would type-check via the `Clone` impl but
14016        // silently clone on every call), a `&Entrada` panic-return on
14017        // the `None` arm (which would collapse the load-bearing
14018        // `Option` presence-bit into a runtime panic), or a one-arm-
14019        // only accessor that returned a saturating composite on some
14020        // sentinel input.
14021        use crate::aplicacao::Entrada;
14022        for entrada in [
14023            Some(Entrada {
14024                host: "checkout.quero.cloud".into(),
14025                para: "gateway".into(),
14026                paths: Vec::new(),
14027                port: crate::DEFAULT_SERVICO_PORT,
14028            }),
14029            Some(Entrada {
14030                host: "api.pleme.io".into(),
14031                para: "public-api".into(),
14032                paths: vec!["/v1".into(), "/v2".into()],
14033                port: 8080,
14034            }),
14035        ] {
14036            let c = caixa_aplicacao_with_entrada(entrada.clone());
14037            let first = c.entrada().unwrap();
14038            let second = c.entrada().unwrap();
14039            assert_eq!(
14040                first, second,
14041                "Caixa::entrada must be idempotent — two successive \
14042                 calls on the same &self must return the same &Entrada",
14043            );
14044            assert!(
14045                std::ptr::eq(first, second),
14046                "Caixa::entrada must borrow the underlying \
14047                 Option<Entrada> storage — two successive calls must \
14048                 return references with the same backing pointer (a \
14049                 fresh Entrada clone would change the pointer on every \
14050                 call)",
14051            );
14052            assert_eq!(
14053                Some(first),
14054                entrada.as_ref(),
14055                "Caixa::entrada must return :entrada verbatim by \
14056                 borrow — got {first:?}, expected {:?}",
14057                entrada.as_ref(),
14058            );
14059        }
14060        let c = caixa_aplicacao_with_entrada(None);
14061        assert!(
14062            c.entrada().is_none(),
14063            "Caixa::entrada must return None when :entrada is absent \
14064             — the author-omitted arm must project through the \
14065             accessor's Option::None unchanged",
14066        );
14067    }
14068
14069    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
14070
14071    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
14072        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14073        c.estrategia = estrategia;
14074        c
14075    }
14076
14077    #[test]
14078    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
14079        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
14080        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
14081        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
14082        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
14083        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
14084        // over the same discriminant the raw `self.estrategia` field
14085        // access carries, byte-equal across every representative fixture
14086        // in the accept-set — the author-omitted `None` shape (the
14087        // "defer to [`RestartStrategy::default`] through the
14088        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
14089        // every non-`Supervisor`-kind `defcaixa` carries by
14090        // `#[serde(default)]`), and each of the four closed-set variants
14091        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
14092        // / [`RestartStrategy::RestForOne`] /
14093        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
14094        // partitions on.
14095        //
14096        // Pins against a future silent detour that re-derived the
14097        // strategy from a peer axis (an accidental fallback to
14098        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
14099        // collapse that read the outer `:children` list-length axis into
14100        // the strategy discriminator at the accessor boundary), a
14101        // stale-derive detour that substituted [`RestartStrategy::default`]
14102        // when the outer `Option` held `None` (which would silently
14103        // collapse the load-bearing "author explicitly declared
14104        // `:estrategia OneForOne`" vs "author omitted the slot and
14105        // inherited the default" partition the [`Self::declared_supervisor_slots`]
14106        // presence-probe reads — the enumerator gate would still push
14107        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
14108        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
14109        // kind-coherence gate's traversal head from the
14110        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
14111        // composition head), a reference to an operator-resolved overlay
14112        // (the future per-cluster `:estrategia-overrides` slot — its
14113        // resolution must land at exactly this accessor body, not
14114        // silently divert the raw slot away from a second consumer), or
14115        // an axis-remap projection (a future detour that mapped
14116        // `OneForAll` through the accessor onto `OneForOne` would
14117        // silently split every downstream sibling-restart-strategy
14118        // consumer's per-arm fan-out).
14119        //
14120        // First outer top-level [`Caixa`] `Option<Copy>`-return
14121        // supervisor-tree-slot flat-spread accessor pin on the substrate
14122        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
14123        // projection pattern the sibling per-`Caixa` `:max-restarts` /
14124        // `:restart-window` future outer-scalar pins fold on. Peer of
14125        // the inner-altitude
14126        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
14127        // (eafb619) pin on the post-composition [`SupervisorSpec`]
14128        // altitude — same "the substrate-primitive accessor must byte-
14129        // equal the raw field access verbatim across every author-
14130        // declared value" discipline extended onto the pre-composition
14131        // outer author-surface [`Caixa`] altitude. Peer of the closed
14132        // outer-`Caixa` `Option<&Composite>` composite-reference family
14133        // the sibling `limits` / `behavior` / `politicas` / `placement` /
14134        // `entrada`
14135        // `..._returns_..._option_ref_verbatim_across_permutations` pins
14136        // already carry on the outer `Option<&Composite>` altitude.
14137        use crate::supervisor::RestartStrategy;
14138        let fixtures: Vec<Option<RestartStrategy>> = vec![
14139            None,
14140            Some(RestartStrategy::OneForOne),
14141            Some(RestartStrategy::OneForAll),
14142            Some(RestartStrategy::RestForOne),
14143            Some(RestartStrategy::SimpleOneForOne),
14144        ];
14145        for estrategia in fixtures {
14146            let c = caixa_with_estrategia(estrategia);
14147            assert_eq!(
14148                c.estrategia(),
14149                estrategia,
14150                "Caixa::estrategia must return :estrategia verbatim (got \
14151                 {:?}, expected {:?})",
14152                c.estrategia(),
14153                estrategia,
14154            );
14155            assert_eq!(
14156                c.estrategia(),
14157                c.estrategia,
14158                "Caixa::estrategia accessor and self.estrategia field \
14159                 access must byte-equal — the accessor is the substrate-\
14160                 primitive typed dispatch every downstream supervisor-\
14161                 tree flat-spread consumer must route through, and a \
14162                 discriminant split would silently break every consumer \
14163                 that relied on the accessor sharing the field's own \
14164                 Option<Copy> shape",
14165            );
14166            assert_eq!(
14167                c.estrategia().is_some(),
14168                c.estrategia.is_some(),
14169                "Caixa::estrategia().is_some() must byte-equal \
14170                 self.estrategia.is_some() — a presence-bit drift would \
14171                 silently split the paired Caixa::declared_supervisor_slots \
14172                 presence-probe arm from the Caixa::supervisor_view \
14173                 unwrap_or_default() fold's composition input",
14174            );
14175        }
14176    }
14177
14178    #[test]
14179    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
14180        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
14181        // `:estrategia` presence-probe arm must key off
14182        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
14183        // field-probe. Structurally: every `Caixa { estrategia:
14184        // Some(RestartStrategy::_), .. }` variant must push
14185        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
14186        // (the presence bit is `Some` for every closed-set variant, so
14187        // the M2 supervisor-tree kind-coherence gate must surface the
14188        // slot as "declared" regardless of which variant the author
14189        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
14190        // the label (the "author omitted the slot entirely, deferring
14191        // to [`RestartStrategy::default`] through the supervisor_view
14192        // fold" partition). The pair jointly pins the accessor +
14193        // declared-slot enumerator composition: any future silent detour
14194        // that had the accessor collapse `Some(RestartStrategy::default())`
14195        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
14196        // projection) would silently absorb the "declared but default-
14197        // valued" arm at the accessor boundary and the
14198        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
14199        // coherence gate would silently accept a struct-literal `Caixa`
14200        // carrying the drift.
14201        //
14202        // Peer of the sibling per-`Caixa`
14203        // `declared_servico_slots_limits_arm_routes_through_accessor`
14204        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
14205        // `Option<&LimitsSpec>` composition axis — same "the enumerator
14206        // gate must route through the substrate-primitive typed
14207        // dispatch" discipline extended onto the flat-spread M2
14208        // supervisor-tree `Option<RestartStrategy>`-composition surface,
14209        // opening the outer-`Caixa` supervisor-tree-slot arm of the
14210        // composition-pin family.
14211        use crate::supervisor::RestartStrategy;
14212        for estrategia in [
14213            RestartStrategy::OneForOne,
14214            RestartStrategy::OneForAll,
14215            RestartStrategy::RestForOne,
14216            RestartStrategy::SimpleOneForOne,
14217        ] {
14218            let c = caixa_with_estrategia(Some(estrategia));
14219            let slots = c.declared_supervisor_slots();
14220            assert!(
14221                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
14222                "declared_supervisor_slots must push \
14223                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
14224                 Some({estrategia:?}) — the accessor and the enumerator \
14225                 gate must route through the same substrate-primitive \
14226                 typed dispatch on the outer :estrategia presence bit \
14227                 (got slots={slots:?})",
14228            );
14229        }
14230        let c = caixa_with_estrategia(None);
14231        let slots = c.declared_supervisor_slots();
14232        assert!(
14233            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
14234            "declared_supervisor_slots must NOT push \
14235             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
14236             — the author-omitted arm must route through the accessor's \
14237             None-return unchanged (got slots={slots:?})",
14238        );
14239    }
14240
14241    #[test]
14242    fn supervisor_view_estrategia_arm_routes_through_accessor() {
14243        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
14244        // [`SupervisorSpec`] construction arm must key off
14245        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
14246        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
14247        // for every `:kind Supervisor` `Caixa` carrying an author-
14248        // declared `Some(RestartStrategy::_)` variant, the composed
14249        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
14250        // outer accessor's declared variant unchanged; and for a
14251        // `:kind Supervisor` `Caixa` carrying `None`, the composed
14252        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
14253        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
14254        // arm the flat-spread `unwrap_or_default()` fold projects to on
14255        // the author-omitted arm — this is the *composition* between the
14256        // outer `Option<RestartStrategy>` accessor's presence-bit
14257        // surface and the inner post-composition non-`Option`
14258        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
14259        // pins the accessor + supervisor_view composition: any future
14260        // silent detour that had the accessor promote `None` to
14261        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
14262        // projection) would silently collapse the two arms into one at
14263        // the accessor boundary and the [`Self::declared_supervisor_slots`]
14264        // presence probe would silently drift from the composition site.
14265        //
14266        // Peer of the sibling M2 supervisor-slot post-composition
14267        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
14268        // pin on the [`SupervisorSpec::validate`] altitude — this pin
14269        // extends that inner-altitude accessor-routing discipline onto
14270        // the pre-composition outer author-surface [`Caixa`] altitude,
14271        // pinning the composition edge between the flat-spread outer
14272        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
14273        // `RestartStrategy` axes.
14274        use crate::CaixaKind;
14275        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
14276        for estrategia in [
14277            RestartStrategy::OneForOne,
14278            RestartStrategy::OneForAll,
14279            RestartStrategy::RestForOne,
14280            RestartStrategy::SimpleOneForOne,
14281        ] {
14282            let mut c = caixa_with_estrategia(Some(estrategia));
14283            c.kind = CaixaKind::Supervisor;
14284            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
14285            // shape partition through the [`gen_platform::IsVariant`]
14286            // derive-generated
14287            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
14288            // than the raw `matches!(estrategia, RestartStrategy::
14289            // SimpleOneForOne)` open-coded pattern-match — same closed-
14290            // set-typed-enum arm-discriminator dispatch discipline the
14291            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
14292            // convergence (915a934) extended onto its two paired positive
14293            // / negated `matches!` sites and the peer
14294            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
14295            // predicate convergence (766ec63) extended onto the M3 mesh-
14296            // slot per-`:placement` distribution-strategy discriminator
14297            // axis. See the sibling `supervisor::tests::
14298            // round_trip_all_strategies` and
14299            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
14300            // fixtures — the three sites (all test-only,
14301            // acknowledged in 915a934's Prior-commits footnote as the
14302            // outstanding follow-up) now consult one typed dispatch on
14303            // the substrate primitive.
14304            c.children = if estrategia.is_simple_one_for_one() {
14305                Vec::new()
14306            } else {
14307                vec![ChildSpec {
14308                    caixa: "worker".into(),
14309                    versao: "^0.1".into(),
14310                    restart: RestartPolicy::Permanent,
14311                }]
14312            };
14313            let view = c.supervisor_view().expect(
14314                "supervisor_view must materialize a SupervisorSpec for a \
14315                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
14316            );
14317            assert_eq!(
14318                view.estrategia(),
14319                c.estrategia().unwrap(),
14320                "supervisor_view must carry the outer Caixa::estrategia() \
14321                 declared variant onto the composed SupervisorSpec.estrategia \
14322                 field verbatim on the Some arm (got {:?}, expected {:?})",
14323                view.estrategia(),
14324                c.estrategia().unwrap(),
14325            );
14326        }
14327        // The author-omitted arm: outer `None` → composed
14328        // `RestartStrategy::default()` through the flat-spread
14329        // `unwrap_or_default()` fold.
14330        let mut c = caixa_with_estrategia(None);
14331        c.kind = CaixaKind::Supervisor;
14332        // Populate children so the sibling supervisor slots are coherent
14333        // for the [`Self::supervisor_view`] projection; the `:estrategia`
14334        // arm still defers to [`RestartStrategy::default`] on the
14335        // author-omitted arm even when the sibling slots carry values.
14336        c.children = vec![ChildSpec {
14337            caixa: "worker".into(),
14338            versao: "^0.1".into(),
14339            restart: RestartPolicy::Permanent,
14340        }];
14341        let view = c.supervisor_view().expect(
14342            "supervisor_view must materialize a SupervisorSpec for a \
14343             :kind Supervisor Caixa carrying a None `:estrategia` slot",
14344        );
14345        assert_eq!(
14346            view.estrategia(),
14347            RestartStrategy::default(),
14348            "supervisor_view must project the outer Caixa::estrategia() \
14349             None arm onto RestartStrategy::default() through the flat-\
14350             spread unwrap_or_default() fold (got {:?}, expected {:?})",
14351            view.estrategia(),
14352            RestartStrategy::default(),
14353        );
14354        assert!(
14355            c.estrategia().is_none(),
14356            "Caixa::estrategia() must remain None on the author-omitted \
14357             arm — the supervisor_view fold must not mutate the outer \
14358             flat-spread presence bit",
14359        );
14360    }
14361
14362    #[test]
14363    fn estrategia_projects_option_by_copy() {
14364        // The by-`Copy` pin: [`Caixa::estrategia`] returns
14365        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
14366        // the accessor does not borrow `&self` past the call (no
14367        // lifetime on the return type), and calling the accessor twice
14368        // on the same [`Caixa`] must yield discriminant-equal values
14369        // (idempotent, no side effects on `&self`). Peer of the sibling
14370        // outer-`Caixa` `Option<&Composite>` by-borrow
14371        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
14372        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
14373        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
14374        // `placement_projects_option_ref_by_borrow` (4fb8074) /
14375        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
14376        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
14377        // extended here to the outer-`Caixa` `Option<Copy>`-return
14378        // flat-spread axis. The `Copy` discipline replaces the pointer-
14379        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
14380        // `Copy` discriminant is definitionally the same discriminant, so
14381        // the axis reduces to discriminant equality).
14382        //
14383        // Pins against a future silent detour that returned a fresh
14384        // `Option<&RestartStrategy>` (which would type-check but silently
14385        // introduce a borrow of `&self` past the call, collapsing the
14386        // load-bearing "no lifetime on the return type" `Copy` projection
14387        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
14388        // read side effect that flipped the outer discriminant on
14389        // successive calls, or an axis-remap projection that returned a
14390        // different variant than the field storage.
14391        use crate::supervisor::RestartStrategy;
14392        for estrategia in [
14393            Some(RestartStrategy::OneForOne),
14394            Some(RestartStrategy::OneForAll),
14395            Some(RestartStrategy::RestForOne),
14396            Some(RestartStrategy::SimpleOneForOne),
14397        ] {
14398            let c = caixa_with_estrategia(estrategia);
14399            let first = c.estrategia();
14400            let second = c.estrategia();
14401            assert_eq!(
14402                first, second,
14403                "Caixa::estrategia must be idempotent — two successive \
14404                 calls on the same &self must return the same \
14405                 Option<RestartStrategy>",
14406            );
14407            assert_eq!(
14408                first, estrategia,
14409                "Caixa::estrategia must return :estrategia verbatim by \
14410                 Copy — got {first:?}, expected {estrategia:?}",
14411            );
14412        }
14413        let c = caixa_with_estrategia(None);
14414        assert!(
14415            c.estrategia().is_none(),
14416            "Caixa::estrategia must return None when :estrategia is \
14417             absent — the author-omitted arm must project through the \
14418             accessor's Option::None unchanged",
14419        );
14420    }
14421
14422    // ── Caixa::max_restarts / Caixa::restart_window —
14423    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
14424    //    (Option<u32> / Option<&str>) folding on the ed04d3c
14425    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
14426
14427    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
14428        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14429        c.max_restarts = max_restarts;
14430        c
14431    }
14432
14433    fn caixa_supervisor_with_max_restarts_and_window(
14434        max_restarts: Option<u32>,
14435        restart_window: Option<&str>,
14436    ) -> Caixa {
14437        use crate::CaixaKind;
14438        use crate::supervisor::{ChildSpec, RestartPolicy};
14439        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
14440        c.kind = CaixaKind::Supervisor;
14441        c.max_restarts = max_restarts;
14442        c.restart_window = restart_window.map(str::to_string);
14443        c.children = vec![ChildSpec {
14444            caixa: "worker".into(),
14445            versao: "^0.1".into(),
14446            restart: RestartPolicy::Permanent,
14447        }];
14448        c
14449    }
14450
14451    #[test]
14452    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
14453        // Value-shape pin: [`Caixa::max_restarts`] returns the
14454        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
14455        // from the typed slot's own storage, byte-equal across the
14456        // author-omitted `None` arm (the "defer to the
14457        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
14458        // `{intensity, 5, 60}` default" partition every
14459        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
14460        // and each of the representative fixtures in the accept-set —
14461        // `0` (the zero-floor arm the peer
14462        // [`crate::supervisor::SupervisorSpec::validate`]
14463        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
14464        // the post-composition altitude — the accessor must ship the
14465        // raw slot verbatim so struct-literal fixtures continue to
14466        // expose the zero at the accessor boundary), the OTP-canonical
14467        // `5` default (`{intensity, 5, 60}` worker-supervisor from
14468        // Learn You Some Erlang), `1000` (the
14469        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
14470        // upper-bound gate accepts on the boundary), `u32::MAX` (a
14471        // past-the-cap sentinel that the substrate-primitive accessor
14472        // must still ship verbatim). Second outer top-level
14473        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
14474        // pin — folds on the sibling
14475        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
14476        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
14477        // onto the sibling `Option<u32>` restart-budget-count arm.
14478        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
14479        for max_restarts in fixtures {
14480            let c = caixa_with_max_restarts(max_restarts);
14481            assert_eq!(
14482                c.max_restarts(),
14483                max_restarts,
14484                "Caixa::max_restarts must return :max-restarts verbatim \
14485                 (got {:?}, expected {max_restarts:?})",
14486                c.max_restarts(),
14487            );
14488            assert_eq!(
14489                c.max_restarts(),
14490                c.max_restarts,
14491                "Caixa::max_restarts accessor and self.max_restarts \
14492                 field access must byte-equal — a presence-bit or count \
14493                 drift would silently split the paired \
14494                 Caixa::declared_supervisor_slots presence-probe arm \
14495                 from the Caixa::supervisor_view unwrap_or(5) fold's \
14496                 composition input",
14497            );
14498        }
14499    }
14500
14501    #[test]
14502    fn max_restarts_projects_option_by_copy() {
14503        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
14504        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
14505        // borrow `&self` past the call (no lifetime on the return type),
14506        // and calling the accessor twice on the same [`Caixa`] must
14507        // yield equal values (idempotent, no side effects). Peer of the
14508        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
14509        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
14510        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
14511            let c = caixa_with_max_restarts(max_restarts);
14512            let first = c.max_restarts();
14513            let second = c.max_restarts();
14514            assert_eq!(
14515                first, second,
14516                "Caixa::max_restarts must be idempotent — two successive \
14517                 calls on the same &self must return the same Option<u32>",
14518            );
14519            assert_eq!(
14520                first, max_restarts,
14521                "Caixa::max_restarts must return :max-restarts verbatim \
14522                 by Copy — got {first:?}, expected {max_restarts:?}",
14523            );
14524        }
14525    }
14526
14527    #[test]
14528    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
14529        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
14530        // `:max-restarts` presence-probe arm must key off
14531        // [`Caixa::max_restarts`], not the raw
14532        // `self.max_restarts.is_some()` field-probe. Structurally: every
14533        // `Caixa { max_restarts: Some(_), .. }` variant must push
14534        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
14535        // list (the presence bit is `Some` for every representative
14536        // count, so the M2 kind-coherence gate must surface the slot as
14537        // "declared"), and a `Caixa { max_restarts: None, .. }` must
14538        // NOT push the label. Peer of the sibling
14539        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
14540        // (ed04d3c) composition pin — same routing-through-accessor
14541        // discipline extended onto the sibling flat-spread `Option<u32>`
14542        // arm.
14543        for max_restarts in [0u32, 5, 1000, u32::MAX] {
14544            let c = caixa_with_max_restarts(Some(max_restarts));
14545            let slots = c.declared_supervisor_slots();
14546            assert!(
14547                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
14548                "declared_supervisor_slots must push \
14549                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
14550                 is Some({max_restarts}) — the accessor and the \
14551                 enumerator gate must route through the same \
14552                 substrate-primitive typed dispatch on the outer \
14553                 :max-restarts presence bit (got slots={slots:?})",
14554            );
14555        }
14556        let c = caixa_with_max_restarts(None);
14557        let slots = c.declared_supervisor_slots();
14558        assert!(
14559            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
14560            "declared_supervisor_slots must NOT push \
14561             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
14562             None — the author-omitted arm must route through the \
14563             accessor's None-return unchanged (got slots={slots:?})",
14564        );
14565    }
14566
14567    #[test]
14568    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
14569        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
14570        // [`SupervisorSpec`] construction arm must key off
14571        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
14572        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
14573        // every `:kind Supervisor` `Caixa` carrying an author-declared
14574        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
14575        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
14576        // carrying `None`, the composed [`SupervisorSpec`]'s
14577        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
14578        // of the sibling
14579        // `supervisor_view_estrategia_arm_routes_through_accessor`
14580        // (ed04d3c) composition pin.
14581        for max_restarts in [1u32, 5, 1000] {
14582            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
14583            let view = c.supervisor_view().expect(
14584                "supervisor_view must materialize a SupervisorSpec for a \
14585                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
14586            );
14587            assert_eq!(
14588                view.max_restarts(),
14589                max_restarts,
14590                "supervisor_view must carry the outer \
14591                 Caixa::max_restarts() Some arm onto the composed \
14592                 SupervisorSpec.max_restarts field verbatim (got {}, \
14593                 expected {max_restarts})",
14594                view.max_restarts(),
14595            );
14596        }
14597        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
14598        let view = c.supervisor_view().expect(
14599            "supervisor_view must materialize a SupervisorSpec for a \
14600             :kind Supervisor Caixa carrying a None :max-restarts",
14601        );
14602        assert_eq!(
14603            view.max_restarts(),
14604            5,
14605            "supervisor_view must project the outer \
14606             Caixa::max_restarts() None arm onto the OTP-canonical \
14607             {{intensity, 5, 60}} default (5) through the flat-spread \
14608             unwrap_or(5) fold (got {})",
14609            view.max_restarts(),
14610        );
14611        assert!(
14612            c.max_restarts().is_none(),
14613            "Caixa::max_restarts() must remain None on the author-\
14614             omitted arm — the supervisor_view fold must not mutate \
14615             the outer flat-spread presence bit",
14616        );
14617    }
14618
14619    #[test]
14620    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
14621        // Value-shape pin: [`Caixa::restart_window`] returns the
14622        // `:restart-window` typed `Option<String>` verbatim as an
14623        // `Option<&str>`, borrowed from the typed slot's own storage,
14624        // byte-equal across the author-omitted `None` arm and each of
14625        // the representative fixtures in the accept-set — the canonical
14626        // `"60s"` from `{intensity, 5, 60}`, the sibling
14627        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
14628        // / `"0s"`) the shared codec's positive-set sweep pin covers,
14629        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
14630        // seconds drift the sibling [`Self::validate_restart_window`]
14631        // gate refuses; the accessor must ship the raw slot verbatim
14632        // so struct-literal fixtures continue to expose the drift at
14633        // the accessor boundary). Third outer top-level [`Caixa`]
14634        // supervisor-tree flat-spread pin — extends the sub-family onto
14635        // the sibling `Option<&str>` raw-duration-string arm.
14636        for window in [
14637            None,
14638            Some("60s"),
14639            Some("5m"),
14640            Some("1h"),
14641            Some("500ms"),
14642            Some("1.5s"),
14643            Some(""),
14644        ] {
14645            let c = caixa_with_restart_window(window);
14646            assert_eq!(
14647                c.restart_window(),
14648                window,
14649                "Caixa::restart_window must return :restart-window \
14650                 verbatim as Option<&str> (got {:?}, expected {window:?})",
14651                c.restart_window(),
14652            );
14653            assert_eq!(
14654                c.restart_window(),
14655                c.restart_window.as_deref(),
14656                "Caixa::restart_window accessor and \
14657                 self.restart_window.as_deref() field access must \
14658                 byte-equal — a byte-level drift would silently split \
14659                 the paired Caixa::declared_supervisor_slots \
14660                 presence-probe arm from the \
14661                 Caixa::validate_restart_window shared-codec gate and \
14662                 the Caixa::supervisor_view soft-swallowing fold",
14663            );
14664        }
14665    }
14666
14667    #[test]
14668    fn restart_window_projects_slice_by_borrow() {
14669        // The by-borrow pin: [`Caixa::restart_window`] returns
14670        // `Option<&str>` by borrow — the returned string slice borrows
14671        // the underlying `Option<String>` storage of the `:restart-window`
14672        // slot and the accessor must not clone on every call. Peer of
14673        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
14674        // by-borrow pins on the universal-axis scalar family
14675        // (`licenca_projects_option_ref_by_borrow` /
14676        // `descricao_projects_option_ref_by_borrow` and siblings) —
14677        // extended onto the M2 supervisor-tree flat-spread
14678        // `Option<&str>` raw-duration-string axis.
14679        for window in [None, Some("60s"), Some("5m"), Some("")] {
14680            let c = caixa_with_restart_window(window);
14681            let first = c.restart_window();
14682            let second = c.restart_window();
14683            assert_eq!(
14684                first, second,
14685                "Caixa::restart_window must be idempotent — two \
14686                 successive calls on the same &self must return the \
14687                 same Option<&str>",
14688            );
14689            if let (Some(a), Some(b)) = (first, second) {
14690                assert_eq!(
14691                    a.as_ptr(),
14692                    b.as_ptr(),
14693                    "Caixa::restart_window must borrow the underlying \
14694                     String storage — two successive Some-arm calls must \
14695                     return slices with the same backing pointer (a fresh \
14696                     String clone would change the pointer on every call)",
14697                );
14698            }
14699            assert_eq!(
14700                first, window,
14701                "Caixa::restart_window must return :restart-window \
14702                 verbatim by borrow — got {first:?}, expected {window:?}",
14703            );
14704        }
14705    }
14706
14707    #[test]
14708    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
14709        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
14710        // `:restart-window` presence-probe arm must key off
14711        // [`Caixa::restart_window`], not the raw
14712        // `self.restart_window.is_some()` field-probe. Structurally:
14713        // every `Caixa { restart_window: Some(_), .. }` must push
14714        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
14715        // list, and a `Caixa { restart_window: None, .. }` must NOT
14716        // push the label. Peer of the sibling
14717        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
14718        // routing pin.
14719        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
14720            let c = caixa_with_restart_window(Some(window));
14721            let slots = c.declared_supervisor_slots();
14722            assert!(
14723                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
14724                "declared_supervisor_slots must push \
14725                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
14726                 `:restart-window` is Some({window:?}) — the accessor \
14727                 and the enumerator gate must route through the same \
14728                 substrate-primitive typed dispatch on the outer \
14729                 :restart-window presence bit (got slots={slots:?})",
14730            );
14731        }
14732        let c = caixa_with_restart_window(None);
14733        let slots = c.declared_supervisor_slots();
14734        assert!(
14735            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
14736            "declared_supervisor_slots must NOT push \
14737             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
14738             is None — the author-omitted arm must route through the \
14739             accessor's None-return unchanged (got slots={slots:?})",
14740        );
14741    }
14742
14743    #[test]
14744    fn validate_restart_window_arm_routes_through_accessor() {
14745        // Composition pin: [`Caixa::validate_restart_window`]'s
14746        // shared-codec fold arm must key off [`Caixa::restart_window`],
14747        // not the raw `self.restart_window.as_deref()` field-projection.
14748        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
14749        // express no reset" canonical shape); (2) a canonical `Some`
14750        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
14751        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
14752        // .. })` carrying the offending raw string verbatim. The three
14753        // arms jointly pin that the validator's raw-string binding is
14754        // the accessor's return, not a peer projection — any future
14755        // silent detour that had the accessor collapse `Some("")` to
14756        // `None` would silently absorb the empty-after-trim refusal
14757        // case at the accessor boundary.
14758        caixa_with_restart_window(None)
14759            .validate_restart_window()
14760            .expect("None :restart-window must validate through the accessor");
14761        caixa_with_restart_window(Some("60s"))
14762            .validate_restart_window()
14763            .expect("canonical :restart-window \"60s\" must validate through the accessor");
14764        let err = caixa_with_restart_window(Some("1.5s"))
14765            .validate_restart_window()
14766            .expect_err("fractional-seconds :restart-window must fail through the accessor");
14767        assert!(
14768            matches!(
14769                err,
14770                ManifestError::RestartWindowMalformed { ref restart_window, .. }
14771                    if restart_window == "1.5s"
14772            ),
14773            "validator must carry the offending raw string verbatim \
14774             from the accessor's borrowed &str (got {err:?})",
14775        );
14776    }
14777
14778    #[test]
14779    fn supervisor_view_restart_window_arm_routes_through_accessor() {
14780        // Composition pin: [`Caixa::supervisor_view`]'s
14781        // per-`:restart-window` [`SupervisorSpec`] construction arm
14782        // must key off [`Caixa::restart_window`]'s soft-swallowing
14783        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
14784        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
14785        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
14786        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
14787        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
14788        // (the shared codec's canonical parse); (3) codec-rejected
14789        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
14790        // (the soft-swallow preserving the view's best-effort shape).
14791        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
14792        let view = c.supervisor_view().expect("Supervisor kind has a view");
14793        assert_eq!(
14794            view.restart_window(),
14795            None,
14796            "supervisor_view must project outer None :restart-window \
14797             onto None on the composed SupervisorSpec (never-reset \
14798             sentinel) through the accessor's None-return unchanged",
14799        );
14800
14801        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
14802        let view = c.supervisor_view().expect("Supervisor kind has a view");
14803        assert_eq!(
14804            view.restart_window(),
14805            Some(std::time::Duration::from_secs(60)),
14806            "supervisor_view must fold outer Some(\"60s\") through the \
14807             shared duration_codec into Duration::from_secs(60) on the \
14808             composed SupervisorSpec (accessor's Some(&str) → codec \
14809             parse → Some(Duration))",
14810        );
14811
14812        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
14813        let view = c.supervisor_view().expect("Supervisor kind has a view");
14814        assert_eq!(
14815            view.restart_window(),
14816            None,
14817            "supervisor_view must soft-swallow the shared-codec parse \
14818             failure to None (the view's best-effort shape the sibling \
14819             manifest-level validate_restart_window surfaces as \
14820             RestartWindowMalformed); the accessor's raw-string return \
14821             is the single input every downstream consumer keys off",
14822        );
14823    }
14824
14825    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
14826
14827    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
14828        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14829        c.upgrade_from = upgrade_from;
14830        c
14831    }
14832
14833    #[test]
14834    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
14835        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
14836        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
14837        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
14838        // typed `Vec<UpgradeFromEntry>` verbatim as a
14839        // `&[UpgradeFromEntry]` slice-view over the same backing
14840        // buffer the raw `self.upgrade_from.as_slice()` field access
14841        // borrows from, element-equal across every representative
14842        // fixture in the accept-set — `[]` (the "no hot-upgrade path
14843        // declared" arm every `defcaixa` without an `:upgrade-from`
14844        // block carries; `#[serde(default)]` folds an omitted slot
14845        // onto `Vec::new()`), a canonical single-entry `Restart`
14846        // fixture (the shape most Servicos carry — a single prior
14847        // version with the fallback strategy), a canonical multi-
14848        // entry list carrying every typed instruction variant
14849        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
14850        // `Restart`), and a past-the-guard sentinel — a duplicate-
14851        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
14852        // ([`crate::upgrade::validate_upgrade_from`] rejects through
14853        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
14854        // ship the raw slot verbatim so struct-literal fixtures
14855        // continue to expose the duplicate at the accessor boundary).
14856        //
14857        // Pins against a future silent detour that returned an owned
14858        // `Vec<UpgradeFromEntry>` (which would type-check but silently
14859        // clone on every accessor call, breaking the zero-cost
14860        // projection every peer sibling slice accessor carries), a
14861        // `[dup, dup] → [dup]` dedup collapse (which would silently
14862        // absorb the `DuplicateFrom` refusal case at the accessor
14863        // boundary and the [`crate::StandardLayout::verify`] cross-
14864        // entry gate would silently accept a struct-literal `Caixa`
14865        // carrying the drift), a reference to an operator-resolved
14866        // overlay (the future per-cluster `:upgrade-overrides` slot
14867        // — its resolution must land at exactly this accessor body,
14868        // not silently divert the raw slot away from a second
14869        // consumer), or an axis-shuffled projection (a future detour
14870        // that reordered entries through the accessor would silently
14871        // split the paired [`crate::StandardLayout::verify`] per-
14872        // `:upgrade-from` shape gate's traversal input from the peer
14873        // [`crate::render::servico_m2_overlay`] emitter's projection
14874        // input, since the operator's hot-upgrade dispatch matches
14875        // per-`:from` and axis reordering would silently split the
14876        // per-entry script-path existence probe's iteration order
14877        // from the M2 overlay emitter's serialized-entry order).
14878        //
14879        // First outer top-level [`Caixa`] `&[Composite]`-return
14880        // slice accessor pin on the substrate primitive for M2 / M3
14881        // typed-slot vec-carry axes — opens the outer-`Caixa`
14882        // `&[Composite]` composite-slice projection pattern the
14883        // sibling `:children` [`crate::supervisor::ChildSpec`] /
14884        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
14885        // [`crate::aplicacao::WitContract`] future outer-composite-
14886        // slice pins fold on. Peer of the closed outer-`Caixa`
14887        // scalar `Option<&Composite>` composite-reference family the
14888        // sibling `limits` / `behavior` / `politicas` / `placement`
14889        // / `entrada` `..._returns_..._option_ref_verbatim_across_
14890        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
14891        // the "byte-equal, borrow-shared" outer-accessor discipline
14892        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
14893        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
14894        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
14895            vec![],
14896            vec![UpgradeFromEntry {
14897                from: "0.0.1".into(),
14898                instructions: vec![UpgradeInstruction::Restart],
14899            }],
14900            vec![
14901                UpgradeFromEntry {
14902                    from: "0.0.1".into(),
14903                    instructions: vec![
14904                        UpgradeInstruction::LoadModule {
14905                            module: "demo".into(),
14906                        },
14907                        UpgradeInstruction::SoftPurge {
14908                            module: "demo".into(),
14909                        },
14910                    ],
14911                },
14912                UpgradeFromEntry {
14913                    from: "0.0.2".into(),
14914                    instructions: vec![
14915                        UpgradeInstruction::StateChange {
14916                            script: "servicos/upgrade.lisp".into(),
14917                        },
14918                        UpgradeInstruction::Purge {
14919                            module: "demo".into(),
14920                        },
14921                        UpgradeInstruction::Restart,
14922                    ],
14923                },
14924            ],
14925            vec![
14926                UpgradeFromEntry {
14927                    from: "0.1.0".into(),
14928                    instructions: vec![UpgradeInstruction::Restart],
14929                },
14930                UpgradeFromEntry {
14931                    from: "0.1.0".into(),
14932                    instructions: vec![UpgradeInstruction::Restart],
14933                },
14934            ],
14935        ];
14936        for upgrade_from in fixtures {
14937            let c = caixa_with_upgrade_from(upgrade_from.clone());
14938            assert_eq!(
14939                c.upgrade_from(),
14940                upgrade_from.as_slice(),
14941                "Caixa::upgrade_from must return :upgrade-from \
14942                 verbatim (got {:?}, expected {upgrade_from:?})",
14943                c.upgrade_from(),
14944            );
14945            assert_eq!(
14946                c.upgrade_from(),
14947                c.upgrade_from.as_slice(),
14948                "Caixa::upgrade_from must element-equal the raw \
14949                 `self.upgrade_from.as_slice()` field access across \
14950                 every value in the Vec<UpgradeFromEntry> accept-set",
14951            );
14952            assert_eq!(
14953                c.upgrade_from().is_empty(),
14954                c.upgrade_from.is_empty(),
14955                "Caixa::upgrade_from().is_empty() must byte-equal \
14956                 self.upgrade_from.is_empty() — a presence-bit drift \
14957                 would silently split the paired \
14958                 Caixa::declared_servico_slots M2 declared-slot \
14959                 enumerator's presence probe from the peer \
14960                 crate::render::servico_m2_overlay M2 overlay \
14961                 emitter's presence gate",
14962            );
14963        }
14964    }
14965
14966    #[test]
14967    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
14968        // Composition pin: [`Caixa::declared_servico_slots`]'s
14969        // `:upgrade-from` presence-probe arm must key off
14970        // [`Caixa::upgrade_from`], not the raw
14971        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
14972        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
14973        // instructions: vec![Restart] }], .. }` must push
14974        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
14975        // (the presence bit is non-empty, so the M2 kind-coherence
14976        // gate must surface the slot as "declared"), and a `Caixa {
14977        // upgrade_from: vec![], .. }` must NOT push the label (the
14978        // "author omitted the slot entirely" arm — the empty-slice
14979        // partition the serde-default folds onto). The pair jointly
14980        // pins the accessor + declared-slot enumerator composition:
14981        // any future silent detour that had the accessor collapse
14982        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
14983        // is_empty())` projection) would silently absorb the
14984        // "declared but degenerate" arm at the accessor boundary and
14985        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
14986        // coherence gate would silently accept a struct-literal
14987        // `Caixa` carrying the drift.
14988        //
14989        // Peer of the sibling
14990        // `declared_servico_slots_limits_arm_routes_through_accessor`
14991        // (b2bd9d7) and
14992        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14993        // (35d8b52) composition pins on the sibling `:limits` /
14994        // `:behavior` outer-`Option<&Composite>` arms — same "the
14995        // enumerator gate must route through the substrate-primitive
14996        // typed dispatch" discipline extended onto the third M2
14997        // Servico-runtime slot axis, closing the enumerator's routing
14998        // invariant on every M2 arm.
14999        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15000        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
15001            from: "0.0.1".into(),
15002            instructions: vec![UpgradeInstruction::Restart],
15003        }]);
15004        let slots = c.declared_servico_slots();
15005        assert!(
15006            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
15007            "declared_servico_slots must push \
15008             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
15009             non-empty — the accessor and the enumerator gate must \
15010             route through the same substrate-primitive typed \
15011             dispatch on the outer :upgrade-from presence bit (got \
15012             slots={slots:?})",
15013        );
15014        let c = caixa_with_upgrade_from(vec![]);
15015        let slots = c.declared_servico_slots();
15016        assert!(
15017            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
15018            "declared_servico_slots must NOT push \
15019             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
15020             empty — the author-omitted arm must route through the \
15021             accessor's empty-slice return unchanged (got \
15022             slots={slots:?})",
15023        );
15024    }
15025
15026    #[test]
15027    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
15028        // Composition pin: [`crate::render::servico_m2_overlay`]'s
15029        // per-`:upgrade-from` M2 overlay emit arm must key off
15030        // [`Caixa::upgrade_from`], not the raw
15031        // `!caixa.upgrade_from.is_empty()` presence gate + the
15032        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
15033        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
15034        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
15035        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
15036        // sequence in the overlay (the emitter fans onto the serde
15037        // slice-serialization), and a `Caixa { upgrade_from: vec![],
15038        // .. }` must omit the key entirely (the empty-slice
15039        // partition — the `!.is_empty()` outer gate elides the key
15040        // when the author omitted the slot). The pair jointly pins
15041        // the accessor + M2 overlay emitter composition: any future
15042        // silent detour that had the accessor return a fresh-cloned
15043        // `Vec<UpgradeFromEntry>` copy would silently break the
15044        // reference-identity pin the peer per-entry
15045        // `serde_yaml::to_value(caixa.upgrade_from())` projection
15046        // reads from — the projection would clone once per accessor
15047        // call instead of borrowing the storage buffer verbatim.
15048        //
15049        // Peer of the sibling
15050        // `servico_m2_overlay_limits_arm_routes_through_accessor`
15051        // (b2bd9d7) and
15052        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
15053        // (35d8b52) composition pins on the sibling `:limits` /
15054        // `:behavior` outer-`Option<&Composite>` arms — same "the
15055        // M2 overlay emitter must route through the substrate-
15056        // primitive typed dispatch" discipline extended onto the
15057        // third M2 Servico-runtime slot axis, closing the overlay
15058        // emitter's routing invariant on every M2 arm.
15059        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
15060        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15061        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
15062            from: "0.0.1".into(),
15063            instructions: vec![UpgradeInstruction::Restart],
15064        }]);
15065        let overlay = servico_m2_overlay(&c).unwrap();
15066        assert!(
15067            overlay.contains_key(M2_KEY_UPGRADE_FROM),
15068            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
15069             `:upgrade-from` is non-empty — the accessor and the M2 \
15070             overlay emitter must route through the same substrate- \
15071             primitive typed dispatch on the outer :upgrade-from \
15072             slice (got overlay={overlay:?})",
15073        );
15074        let c = caixa_with_upgrade_from(vec![]);
15075        let overlay = servico_m2_overlay(&c).unwrap();
15076        assert!(
15077            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
15078            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
15079             `:upgrade-from` is empty — the empty-slice partition \
15080             must route through the accessor's empty-slice return \
15081             unchanged (got overlay={overlay:?})",
15082        );
15083    }
15084
15085    #[test]
15086    fn upgrade_from_projects_slice_by_borrow() {
15087        // The by-borrow pin: [`Caixa::upgrade_from`] returns
15088        // `&[UpgradeFromEntry]` by borrow — the returned slice
15089        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
15090        // the `:upgrade-from` slot and the accessor must not clone
15091        // the backing `Vec` on every call. Peer of the sibling
15092        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
15093        // (`autores_projects_slice_by_borrow` b5d813f,
15094        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
15095        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15096        // `exe_projects_slice_by_borrow` 65d9527,
15097        // `servicos_projects_slice_by_borrow` 611f78b,
15098        // `deps_projects_slice_by_borrow` ad34b4e,
15099        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
15100        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
15101        // axes — extended here to the first outer-`Caixa`
15102        // composite-element `&[Composite]` axis: the accessor's
15103        // returned slice must borrow from `&self` (the returned
15104        // reference's lifetime is tied to `&self`), and calling the
15105        // accessor twice on the same [`Caixa`] must yield slices
15106        // that are pointer-equal (the underlying byte-buffer is the
15107        // storage `Vec`'s allocation, not a fresh copy) as well as
15108        // value-equal (idempotent, no side effects on `&self`).
15109        //
15110        // Pins against a future silent detour that returned an owned
15111        // `Vec<UpgradeFromEntry>` (which would type-check but
15112        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
15113        // return (which would leak the backing `Vec`'s
15114        // grow/push/reserve surface no downstream consumer reaches
15115        // for), or a one-arm-only accessor that returned a
15116        // saturating value on some sentinel input.
15117        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15118        for upgrade_from in [
15119            vec![],
15120            vec![UpgradeFromEntry {
15121                from: "0.0.1".into(),
15122                instructions: vec![UpgradeInstruction::Restart],
15123            }],
15124            vec![
15125                UpgradeFromEntry {
15126                    from: "0.0.1".into(),
15127                    instructions: vec![UpgradeInstruction::Restart],
15128                },
15129                UpgradeFromEntry {
15130                    from: "0.0.2".into(),
15131                    instructions: vec![UpgradeInstruction::SoftPurge {
15132                        module: "demo".into(),
15133                    }],
15134                },
15135            ],
15136        ] {
15137            let c = caixa_with_upgrade_from(upgrade_from.clone());
15138            let first = c.upgrade_from();
15139            let second = c.upgrade_from();
15140            assert_eq!(
15141                first, second,
15142                "Caixa::upgrade_from must be idempotent — two \
15143                 successive calls on the same &self must return the \
15144                 same &[UpgradeFromEntry]",
15145            );
15146            assert_eq!(
15147                first.as_ptr(),
15148                second.as_ptr(),
15149                "Caixa::upgrade_from must borrow the underlying \
15150                 Vec<UpgradeFromEntry> storage — two successive calls \
15151                 must return slices with the same backing pointer (a \
15152                 fresh Vec<UpgradeFromEntry> clone would change the \
15153                 pointer on every call)",
15154            );
15155            assert_eq!(
15156                first,
15157                upgrade_from.as_slice(),
15158                "Caixa::upgrade_from must return :upgrade-from \
15159                 verbatim by borrow — got {first:?}, expected \
15160                 {upgrade_from:?}",
15161            );
15162        }
15163    }
15164
15165    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
15166
15167    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
15168        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15169        c.children = children;
15170        c
15171    }
15172
15173    #[test]
15174    fn children_returns_children_slice_verbatim_across_permutations() {
15175        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
15176        // outer-composite `&[ChildSpec]`-return slice-shape pin:
15177        // [`Caixa::children`] must return the `:children` typed
15178        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
15179        // the same backing buffer the raw `self.children.as_slice()`
15180        // field access borrows from, element-equal across every
15181        // representative fixture in the accept-set — `[]` (the "no
15182        // static children declared" arm every non-`Supervisor`-kind
15183        // `defcaixa` carries by `#[serde(default)]` and every
15184        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
15185        // a canonical single-child `Permanent` fixture (the shape
15186        // most `OneForOne` supervisors carry — a single long-running
15187        // worker child), a canonical multi-child list carrying every
15188        // typed restart-policy variant (`Permanent` / `Transient` /
15189        // `Temporary`), and a past-the-guard sentinel — a duplicate
15190        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
15191        // ([`crate::SupervisorSpec::validate`] rejects through
15192        // `DuplicateChildNome { nome: "w" }` but the accessor must
15193        // ship the raw slot verbatim so struct-literal fixtures
15194        // continue to expose the duplicate at the accessor boundary).
15195        //
15196        // Pins against a future silent detour that returned an owned
15197        // `Vec<ChildSpec>` (which would type-check but silently clone
15198        // on every accessor call, breaking the zero-cost projection
15199        // every peer sibling slice accessor carries), a `[dup, dup] →
15200        // [dup]` dedup collapse (which would silently absorb the
15201        // `DuplicateChildNome` refusal case at the accessor boundary
15202        // and the [`crate::StandardLayout::verify`] cross-child gate
15203        // would silently accept a struct-literal `Caixa` carrying the
15204        // drift), a reference to an operator-resolved overlay (the
15205        // future per-cluster `:children-overrides` slot — its
15206        // resolution must land at exactly this accessor body, not
15207        // silently divert the raw slot away from a second consumer),
15208        // or an axis-shuffled projection (a future detour that
15209        // reordered children through the accessor would silently
15210        // split the paired [`crate::StandardLayout::verify`] per-
15211        // supervisor gate's traversal input from the peer
15212        // [`Self::supervisor_view`] fold-in path's clone-order input,
15213        // since the OTP `RestForOne` restart strategy dispatches on
15214        // declared child order and axis reordering would silently
15215        // split the operator's per-cluster restart-fan-out order
15216        // from the caixa.lisp source-order).
15217        //
15218        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
15219        // accessor pin on the substrate primitive for M2 / M3 typed-
15220        // slot vec-carry axes — folds on the outer-`Caixa`
15221        // `&[Composite]` composite-slice sub-family the sibling
15222        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
15223        // (2a1f907) pin opened, peer at the outer altitude of the
15224        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
15225        // (bc92bce) accessor on the same OTP-supervisor static-child-
15226        // list axis.
15227        use crate::supervisor::{ChildSpec, RestartPolicy};
15228        let fixtures: Vec<Vec<ChildSpec>> = vec![
15229            vec![],
15230            vec![ChildSpec {
15231                caixa: "worker".into(),
15232                versao: "^0.1".into(),
15233                restart: RestartPolicy::Permanent,
15234            }],
15235            vec![
15236                ChildSpec {
15237                    caixa: "worker-a".into(),
15238                    versao: "^0.1".into(),
15239                    restart: RestartPolicy::Permanent,
15240                },
15241                ChildSpec {
15242                    caixa: "worker-b".into(),
15243                    versao: "^0.1".into(),
15244                    restart: RestartPolicy::Transient,
15245                },
15246                ChildSpec {
15247                    caixa: "worker-c".into(),
15248                    versao: "^0.1".into(),
15249                    restart: RestartPolicy::Temporary,
15250                },
15251            ],
15252            vec![
15253                ChildSpec {
15254                    caixa: "w".into(),
15255                    versao: "^0.1".into(),
15256                    restart: RestartPolicy::Permanent,
15257                },
15258                ChildSpec {
15259                    caixa: "w".into(),
15260                    versao: "^0.1".into(),
15261                    restart: RestartPolicy::Permanent,
15262                },
15263            ],
15264        ];
15265        for children in fixtures {
15266            let c = caixa_with_children(children.clone());
15267            assert_eq!(
15268                c.children(),
15269                children.as_slice(),
15270                "Caixa::children must return :children verbatim \
15271                 (got {:?}, expected {children:?})",
15272                c.children(),
15273            );
15274            assert_eq!(
15275                c.children(),
15276                c.children.as_slice(),
15277                "Caixa::children must element-equal the raw \
15278                 `self.children.as_slice()` field access across \
15279                 every value in the Vec<ChildSpec> accept-set",
15280            );
15281            assert_eq!(
15282                c.children().is_empty(),
15283                c.children.is_empty(),
15284                "Caixa::children().is_empty() must byte-equal \
15285                 self.children.is_empty() — a presence-bit drift \
15286                 would silently split the paired \
15287                 Caixa::declared_supervisor_slots supervisor-tree \
15288                 declared-slot enumerator's presence probe from the \
15289                 peer Caixa::supervisor_view typed-view composer's \
15290                 fold-in path",
15291            );
15292        }
15293    }
15294
15295    #[test]
15296    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
15297        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15298        // `:children` presence-probe arm must key off
15299        // [`Caixa::children`], not the raw
15300        // `!self.children.is_empty()` field-probe. Structurally: a
15301        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
15302        // "^0.1", restart: Permanent }], .. }` must push
15303        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
15304        // (the presence bit is non-empty, so the supervisor-tree
15305        // kind-coherence gate must surface the slot as "declared"),
15306        // and a `Caixa { children: vec![], .. }` must NOT push the
15307        // label (the "author omitted the slot entirely" arm — the
15308        // empty-slice partition the serde-default folds onto). The
15309        // pair jointly pins the accessor + declared-slot enumerator
15310        // composition: any future silent detour that had the accessor
15311        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
15312        // "__reserved__")` projection) would silently absorb the
15313        // "declared but degenerate" arm at the accessor boundary and
15314        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
15315        // kind-coherence gate would silently accept a struct-literal
15316        // `Caixa` carrying the drift.
15317        //
15318        // Peer of the sibling
15319        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
15320        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
15321        // same "the enumerator gate must route through the substrate-
15322        // primitive typed dispatch" discipline extended onto the
15323        // supervisor-tree `:children` composite-slice arm.
15324        use crate::supervisor::{ChildSpec, RestartPolicy};
15325        let c = caixa_with_children(vec![ChildSpec {
15326            caixa: "w".into(),
15327            versao: "^0.1".into(),
15328            restart: RestartPolicy::Permanent,
15329        }]);
15330        let slots = c.declared_supervisor_slots();
15331        assert!(
15332            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
15333            "declared_supervisor_slots must push \
15334             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
15335             non-empty — the accessor and the enumerator gate must \
15336             route through the same substrate-primitive typed \
15337             dispatch on the outer :children presence bit (got \
15338             slots={slots:?})",
15339        );
15340        let c = caixa_with_children(vec![]);
15341        let slots = c.declared_supervisor_slots();
15342        assert!(
15343            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
15344            "declared_supervisor_slots must NOT push \
15345             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
15346             empty — the author-omitted arm must route through the \
15347             accessor's empty-slice return unchanged (got \
15348             slots={slots:?})",
15349        );
15350    }
15351
15352    #[test]
15353    fn supervisor_view_children_arm_routes_through_accessor() {
15354        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
15355        // fold-in arm must key off [`Caixa::children`], not the raw
15356        // `self.children.clone()` field-clone. Structurally: a `Caixa {
15357        // kind: Supervisor, estrategia: Some(OneForOne), children:
15358        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
15359        // per-child list through the accessor into the typed
15360        // [`SupervisorSpec`] view's `children` field verbatim — every
15361        // entry the accessor surfaces must land in the view's
15362        // `children` slot in the same order. The pair jointly pins the
15363        // accessor + view-composer composition: any future silent
15364        // detour that had the accessor return a fresh-cloned
15365        // `Vec<ChildSpec>` copy would silently break the reference-
15366        // identity pin the peer `supervisor_view` fold-in path reads
15367        // from — the fold would clone once more per accessor call
15368        // instead of borrowing the storage buffer verbatim once.
15369        //
15370        // Peer of the sibling
15371        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
15372        // family) composition pin on the peer kind-gate arm — same
15373        // "the view composer must route through the substrate-
15374        // primitive typed dispatch" discipline extended onto the
15375        // per-`:children` fold-in arm, closing the supervisor-view
15376        // composer's routing invariant on the composite-slice input.
15377        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
15378        let mut c = caixa_with_children(vec![
15379            ChildSpec {
15380                caixa: "worker-a".into(),
15381                versao: "^0.1".into(),
15382                restart: RestartPolicy::Permanent,
15383            },
15384            ChildSpec {
15385                caixa: "worker-b".into(),
15386                versao: "^0.1".into(),
15387                restart: RestartPolicy::Transient,
15388            },
15389        ]);
15390        c.kind = crate::CaixaKind::Supervisor;
15391        c.estrategia = Some(RestartStrategy::OneForOne);
15392        let view = c
15393            .supervisor_view()
15394            .expect("Supervisor kind must produce a supervisor_view");
15395        assert_eq!(
15396            view.children(),
15397            c.children(),
15398            "supervisor_view must fold Caixa::children verbatim into \
15399             SupervisorSpec::children — the accessor and the view \
15400             composer must route through the same substrate-primitive \
15401             typed dispatch on the outer :children slice (got view \
15402             children={:?}, expected {:?})",
15403            view.children(),
15404            c.children(),
15405        );
15406    }
15407
15408    #[test]
15409    fn children_projects_slice_by_borrow() {
15410        // The by-borrow pin: [`Caixa::children`] returns
15411        // `&[ChildSpec]` by borrow — the returned slice borrows the
15412        // underlying `Vec<ChildSpec>` storage of the `:children` slot
15413        // and the accessor must not clone the backing `Vec` on every
15414        // call. Peer of the sibling outer top-level [`Caixa`]
15415        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
15416        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
15417        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15418        // `exe_projects_slice_by_borrow` 65d9527,
15419        // `servicos_projects_slice_by_borrow` 611f78b,
15420        // `deps_projects_slice_by_borrow` ad34b4e,
15421        // `deps_dev_projects_slice_by_borrow` f7fd81e,
15422        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
15423        // sibling outer top-level [`Caixa`] scalar-element and
15424        // composite-element `&[T]` axes — folds on the outer-`Caixa`
15425        // composite-element `&[Composite]` axis: the accessor's
15426        // returned slice must borrow from `&self` (the returned
15427        // reference's lifetime is tied to `&self`), and calling the
15428        // accessor twice on the same [`Caixa`] must yield slices
15429        // that are pointer-equal (the underlying byte-buffer is the
15430        // storage `Vec`'s allocation, not a fresh copy) as well as
15431        // value-equal (idempotent, no side effects on `&self`).
15432        //
15433        // Pins against a future silent detour that returned an owned
15434        // `Vec<ChildSpec>` (which would type-check but silently clone
15435        // on every call), a `&Vec<ChildSpec>` return (which would leak
15436        // the backing `Vec`'s grow/push/reserve surface no downstream
15437        // consumer reaches for), or a one-arm-only accessor that
15438        // returned a saturating value on some sentinel input.
15439        use crate::supervisor::{ChildSpec, RestartPolicy};
15440        for children in [
15441            vec![],
15442            vec![ChildSpec {
15443                caixa: "w".into(),
15444                versao: "^0.1".into(),
15445                restart: RestartPolicy::Permanent,
15446            }],
15447            vec![
15448                ChildSpec {
15449                    caixa: "worker-a".into(),
15450                    versao: "^0.1".into(),
15451                    restart: RestartPolicy::Permanent,
15452                },
15453                ChildSpec {
15454                    caixa: "worker-b".into(),
15455                    versao: "^0.1".into(),
15456                    restart: RestartPolicy::Transient,
15457                },
15458            ],
15459        ] {
15460            let c = caixa_with_children(children.clone());
15461            let first = c.children();
15462            let second = c.children();
15463            assert_eq!(
15464                first, second,
15465                "Caixa::children must be idempotent — two successive \
15466                 calls on the same &self must return the same \
15467                 &[ChildSpec]",
15468            );
15469            assert_eq!(
15470                first.as_ptr(),
15471                second.as_ptr(),
15472                "Caixa::children must borrow the underlying \
15473                 Vec<ChildSpec> storage — two successive calls must \
15474                 return slices with the same backing pointer (a fresh \
15475                 Vec<ChildSpec> clone would change the pointer on \
15476                 every call)",
15477            );
15478            assert_eq!(
15479                first,
15480                children.as_slice(),
15481                "Caixa::children must return :children verbatim by \
15482                 borrow — got {first:?}, expected {children:?}",
15483            );
15484        }
15485    }
15486
15487    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
15488
15489    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
15490        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15491        c.kind = CaixaKind::Aplicacao;
15492        c.membros = membros;
15493        c
15494    }
15495
15496    #[test]
15497    fn membros_returns_membros_slice_verbatim_across_permutations() {
15498        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
15499        // composite `&[Membro]`-return slice-shape pin:
15500        // [`Caixa::membros`] must return the `:membros` typed
15501        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
15502        // same backing buffer the raw `self.membros.as_slice()` field
15503        // access borrows from, element-equal across every
15504        // representative fixture in the accept-set — `[]` (the "no
15505        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
15506        // carries by `#[serde(default)]` and every partially-authored
15507        // Aplicacao carries before the
15508        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
15509        // canonical single-member fixture (the shape a minimal
15510        // Aplicacao carries — one Servico wrapping one contained
15511        // computation), a canonical multi-member list carrying three
15512        // distinct entries (the canonical checkout-shape Aplicacao —
15513        // cart / pricing / auth — every canonical example carries), and
15514        // a past-the-guard sentinel — a duplicate `:caixa`
15515        // `[("cart", ...), ("cart", ...)]` entry pair
15516        // ([`crate::AplicacaoSpec::validate`] rejects through
15517        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
15518        // the raw slot verbatim so struct-literal fixtures continue to
15519        // expose the duplicate at the accessor boundary).
15520        //
15521        // Pins against a future silent detour that returned an owned
15522        // `Vec<Membro>` (which would type-check but silently clone on
15523        // every accessor call, breaking the zero-cost projection every
15524        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
15525        // dedup collapse (which would silently absorb the
15526        // `DuplicateMembro` refusal case at the accessor boundary and
15527        // the [`crate::StandardLayout::verify`] cross-member gate would
15528        // silently accept a struct-literal `Caixa` carrying the drift),
15529        // a reference to an operator-resolved overlay (the future per-
15530        // cluster `:membros-overrides` slot — its resolution must land
15531        // at exactly this accessor body, not silently divert the raw
15532        // slot away from a second consumer), or an axis-shuffled
15533        // projection (a future detour that reordered members through
15534        // the accessor would silently split the paired
15535        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
15536        // traversal input from the peer [`Self::aplicacao_view`] fold-
15537        // in path's clone-order input, since the canonical `:contratos`
15538        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
15539        // read the member set through the same slice).
15540        //
15541        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
15542        // accessor pin on the substrate primitive for M2 / M3 typed-
15543        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
15544        // arm of the `&[Composite]` composite-slice sub-family the
15545        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
15546        // (2a1f907) and
15547        // `children_returns_children_slice_verbatim_across_permutations`
15548        // (c17b51e) pins opened, peer at the outer altitude of the
15549        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
15550        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
15551        // list axis.
15552        use crate::aplicacao::Membro;
15553        let fixtures: Vec<Vec<Membro>> = vec![
15554            vec![],
15555            vec![Membro {
15556                caixa: "cart".into(),
15557                versao: "^0.1".into(),
15558            }],
15559            vec![
15560                Membro {
15561                    caixa: "cart".into(),
15562                    versao: "^0.1".into(),
15563                },
15564                Membro {
15565                    caixa: "pricing".into(),
15566                    versao: "^0.2".into(),
15567                },
15568                Membro {
15569                    caixa: "auth".into(),
15570                    versao: "^1.0".into(),
15571                },
15572            ],
15573            vec![
15574                Membro {
15575                    caixa: "cart".into(),
15576                    versao: "^0.1".into(),
15577                },
15578                Membro {
15579                    caixa: "cart".into(),
15580                    versao: "^0.1".into(),
15581                },
15582            ],
15583        ];
15584        for membros in fixtures {
15585            let c = caixa_aplicacao_with_membros(membros.clone());
15586            assert_eq!(
15587                c.membros(),
15588                membros.as_slice(),
15589                "Caixa::membros must return :membros verbatim \
15590                 (got {:?}, expected {membros:?})",
15591                c.membros(),
15592            );
15593            assert_eq!(
15594                c.membros(),
15595                c.membros.as_slice(),
15596                "Caixa::membros must element-equal the raw \
15597                 `self.membros.as_slice()` field access across every \
15598                 value in the Vec<Membro> accept-set",
15599            );
15600            assert_eq!(
15601                c.membros().is_empty(),
15602                c.membros.is_empty(),
15603                "Caixa::membros().is_empty() must byte-equal \
15604                 self.membros.is_empty() — a presence-bit drift would \
15605                 silently split the paired Caixa::declared_mesh_slots \
15606                 mesh declared-slot enumerator's presence probe from \
15607                 the peer Caixa::aplicacao_view typed-view composer's \
15608                 fold-in path",
15609            );
15610        }
15611    }
15612
15613    #[test]
15614    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
15615        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
15616        // presence-probe arm must key off [`Caixa::membros`], not the
15617        // raw `!self.membros.is_empty()` field-probe. Structurally: a
15618        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
15619        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
15620        // declared-slot list (the presence bit is non-empty, so the
15621        // mesh kind-coherence gate must surface the slot as
15622        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
15623        // push the label (the "author omitted the slot entirely" arm
15624        // — the empty-slice partition the serde-default folds onto).
15625        // The pair jointly pins the accessor + declared-slot
15626        // enumerator composition: any future silent detour that had
15627        // the accessor collapse `[Membro { .. }]` to `[]` (a
15628        // `.filter(|m| m.nome() != "__reserved__")` projection) would
15629        // silently absorb the "declared but degenerate" arm at the
15630        // accessor boundary and the
15631        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
15632        // coherence gate would silently accept a struct-literal
15633        // `Caixa` carrying the drift.
15634        //
15635        // Peer of the sibling
15636        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
15637        // (2a1f907) and
15638        // `declared_supervisor_slots_children_arm_routes_through_accessor`
15639        // (c17b51e) composition pins on the M2 `:upgrade-from` /
15640        // `:children` composite-slice arms — same "the enumerator gate
15641        // must route through the substrate-primitive typed dispatch"
15642        // discipline extended onto the M3 `:membros` composite-slice
15643        // arm, opening the M3 arm of the declared-slot enumerator's
15644        // routing invariant.
15645        use crate::aplicacao::Membro;
15646        let c = caixa_aplicacao_with_membros(vec![Membro {
15647            caixa: "cart".into(),
15648            versao: "^0.1".into(),
15649        }]);
15650        let slots = c.declared_mesh_slots();
15651        assert!(
15652            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
15653            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
15654             `:membros` is non-empty — the accessor and the enumerator \
15655             gate must route through the same substrate-primitive \
15656             typed dispatch on the outer :membros presence bit (got \
15657             slots={slots:?})",
15658        );
15659        let c = caixa_aplicacao_with_membros(vec![]);
15660        let slots = c.declared_mesh_slots();
15661        assert!(
15662            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
15663            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
15664             when `:membros` is empty — the author-omitted arm must \
15665             route through the accessor's empty-slice return unchanged \
15666             (got slots={slots:?})",
15667        );
15668    }
15669
15670    #[test]
15671    fn aplicacao_view_membros_arm_routes_through_accessor() {
15672        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
15673        // fold-in arm must key off [`Caixa::membros`], not the raw
15674        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
15675        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
15676        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
15677        // member list through the accessor into the typed
15678        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
15679        // every entry the accessor surfaces must land in the view's
15680        // `membros` slot in the same order. The pair jointly pins the
15681        // accessor + view-composer composition: any future silent
15682        // detour that had the accessor return a fresh-cloned
15683        // `Vec<Membro>` copy would silently break the reference-
15684        // identity pin the peer `aplicacao_view` fold-in path reads
15685        // from — the fold would clone once more per accessor call
15686        // instead of borrowing the storage buffer verbatim once.
15687        //
15688        // Peer of the sibling
15689        // `aplicacao_view_politicas_arm_folds_through_accessor`
15690        // (5d23d29) /
15691        // `aplicacao_view_placement_arm_folds_through_accessor`
15692        // (4fb8074) /
15693        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
15694        // composition pins on the M3 `:politicas` / `:placement` /
15695        // `:entrada` outer-`Option<&Composite>` arms — extended here to
15696        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
15697        // closing the aplicacao-view composer's routing invariant on
15698        // the composite-slice input.
15699        use crate::aplicacao::Membro;
15700        let c = caixa_aplicacao_with_membros(vec![
15701            Membro {
15702                caixa: "cart".into(),
15703                versao: "^0.1".into(),
15704            },
15705            Membro {
15706                caixa: "pricing".into(),
15707                versao: "^0.2".into(),
15708            },
15709        ]);
15710        let view = c
15711            .aplicacao_view()
15712            .expect("Aplicacao kind must produce an aplicacao_view");
15713        assert_eq!(
15714            view.membros(),
15715            c.membros(),
15716            "aplicacao_view must fold Caixa::membros verbatim into \
15717             AplicacaoSpec::membros — the accessor and the view \
15718             composer must route through the same substrate-primitive \
15719             typed dispatch on the outer :membros slice (got view \
15720             membros={:?}, expected {:?})",
15721            view.membros(),
15722            c.membros(),
15723        );
15724    }
15725
15726    #[test]
15727    fn membros_projects_slice_by_borrow() {
15728        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
15729        // borrow — the returned slice borrows the underlying
15730        // `Vec<Membro>` storage of the `:membros` slot and the
15731        // accessor must not clone the backing `Vec` on every call.
15732        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
15733        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
15734        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
15735        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15736        // `exe_projects_slice_by_borrow` 65d9527,
15737        // `servicos_projects_slice_by_borrow` 611f78b,
15738        // `deps_projects_slice_by_borrow` ad34b4e,
15739        // `deps_dev_projects_slice_by_borrow` f7fd81e,
15740        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
15741        // `children_projects_slice_by_borrow` c17b51e) on the sibling
15742        // outer top-level [`Caixa`] scalar-element and composite-
15743        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
15744        // slot composite-element `&[Composite]` axis: the accessor's
15745        // returned slice must borrow from `&self` (the returned
15746        // reference's lifetime is tied to `&self`), and calling the
15747        // accessor twice on the same [`Caixa`] must yield slices that
15748        // are pointer-equal (the underlying byte-buffer is the storage
15749        // `Vec`'s allocation, not a fresh copy) as well as value-equal
15750        // (idempotent, no side effects on `&self`).
15751        //
15752        // Pins against a future silent detour that returned an owned
15753        // `Vec<Membro>` (which would type-check but silently clone on
15754        // every call), a `&Vec<Membro>` return (which would leak the
15755        // backing `Vec`'s grow/push/reserve surface no downstream
15756        // consumer reaches for), or a one-arm-only accessor that
15757        // returned a saturating value on some sentinel input.
15758        use crate::aplicacao::Membro;
15759        for membros in [
15760            vec![],
15761            vec![Membro {
15762                caixa: "cart".into(),
15763                versao: "^0.1".into(),
15764            }],
15765            vec![
15766                Membro {
15767                    caixa: "cart".into(),
15768                    versao: "^0.1".into(),
15769                },
15770                Membro {
15771                    caixa: "pricing".into(),
15772                    versao: "^0.2".into(),
15773                },
15774            ],
15775        ] {
15776            let c = caixa_aplicacao_with_membros(membros.clone());
15777            let first = c.membros();
15778            let second = c.membros();
15779            assert_eq!(
15780                first, second,
15781                "Caixa::membros must be idempotent — two successive \
15782                 calls on the same &self must return the same &[Membro]",
15783            );
15784            assert_eq!(
15785                first.as_ptr(),
15786                second.as_ptr(),
15787                "Caixa::membros must borrow the underlying Vec<Membro> \
15788                 storage — two successive calls must return slices with \
15789                 the same backing pointer (a fresh Vec<Membro> clone \
15790                 would change the pointer on every call)",
15791            );
15792            assert_eq!(
15793                first,
15794                membros.as_slice(),
15795                "Caixa::membros must return :membros verbatim by borrow \
15796                 — got {first:?}, expected {membros:?}",
15797            );
15798        }
15799    }
15800
15801    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
15802
15803    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
15804        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15805        c.kind = CaixaKind::Aplicacao;
15806        c.contratos = contratos;
15807        c
15808    }
15809
15810    fn contrato_http_for_test(
15811        de: &str,
15812        para: &str,
15813        endpoint: &str,
15814    ) -> crate::aplicacao::WitContract {
15815        crate::aplicacao::WitContract {
15816            de: de.into(),
15817            para: para.into(),
15818            wit: "wasi:http/proxy".into(),
15819            endpoint: Some(endpoint.into()),
15820            subject: None,
15821            slot: None,
15822        }
15823    }
15824
15825    #[test]
15826    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
15827        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
15828        // composite `&[WitContract]`-return slice-shape pin:
15829        // [`Caixa::contratos`] must return the `:contratos` typed
15830        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
15831        // over the same backing buffer the raw
15832        // `self.contratos.as_slice()` field access borrows from,
15833        // element-equal across every representative fixture in the
15834        // accept-set — `[]` (the "no contracts declared" arm every
15835        // non-`Aplicacao`-kind `defcaixa` carries by
15836        // `#[serde(default)]` and every leaf-Aplicacao with a single
15837        // member carries), a canonical single-edge fixture (the
15838        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
15839        // edge), and a canonical multi-edge fixture with three distinct
15840        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
15841        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
15842        //
15843        // Pins against a future silent detour that returned an owned
15844        // `Vec<WitContract>` (which would type-check but silently clone
15845        // on every accessor call, breaking the zero-cost projection
15846        // every peer sibling slice accessor carries), an axis-shuffled
15847        // projection (a future detour that reordered edges through the
15848        // accessor would silently split the paired
15849        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
15850        // traversal input from the peer [`Self::aplicacao_view`] fold-
15851        // in path's clone-order input, since every canonical
15852        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
15853        // seed dispatch reads the edge set through the same slice),
15854        // or a reference to an operator-resolved overlay (the future
15855        // per-cluster `:contratos-overrides` slot — its resolution
15856        // must land at exactly this accessor body, not silently divert
15857        // the raw slot away from a second consumer).
15858        //
15859        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
15860        // accessor pin on the substrate primitive for M2 / M3 typed-
15861        // slot vec-carry axes — closes the outer-`Caixa`
15862        // `&[Composite]` composite-slice sub-family the sibling M2
15863        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
15864        // (2a1f907) and
15865        // `children_returns_children_slice_verbatim_across_permutations`
15866        // (c17b51e) pins opened and the M3
15867        // `membros_returns_membros_slice_verbatim_across_permutations`
15868        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
15869        // slot arm of the composite-slice sub-family. Peer at the outer
15870        // altitude of the closed inner-
15871        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
15872        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
15873        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
15874            vec![],
15875            vec![contrato_http_for_test("cart", "catalog", "/items")],
15876            vec![
15877                contrato_http_for_test("cart", "catalog", "/items"),
15878                contrato_http_for_test("cart", "pricing", "/price"),
15879                contrato_http_for_test("cart", "auth", "/whoami"),
15880            ],
15881        ];
15882        for contratos in fixtures {
15883            let c = caixa_aplicacao_with_contratos(contratos.clone());
15884            assert_eq!(
15885                c.contratos(),
15886                contratos.as_slice(),
15887                "Caixa::contratos must return :contratos verbatim \
15888                 (got {:?}, expected {contratos:?})",
15889                c.contratos(),
15890            );
15891            assert_eq!(
15892                c.contratos(),
15893                c.contratos.as_slice(),
15894                "Caixa::contratos must element-equal the raw \
15895                 `self.contratos.as_slice()` field access across every \
15896                 value in the Vec<WitContract> accept-set",
15897            );
15898            assert_eq!(
15899                c.contratos().is_empty(),
15900                c.contratos.is_empty(),
15901                "Caixa::contratos().is_empty() must byte-equal \
15902                 self.contratos.is_empty() — a presence-bit drift would \
15903                 silently split the paired Caixa::declared_mesh_slots \
15904                 mesh declared-slot enumerator's presence probe from \
15905                 the peer Caixa::aplicacao_view typed-view composer's \
15906                 fold-in path",
15907            );
15908        }
15909    }
15910
15911    #[test]
15912    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
15913        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
15914        // presence-probe arm must key off [`Caixa::contratos`], not the
15915        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
15916        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
15917        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
15918        // presence bit is non-empty, so the mesh kind-coherence gate
15919        // must surface the slot as "declared"), and a `Caixa {
15920        // contratos: vec![], .. }` must NOT push the label (the "author
15921        // omitted the slot entirely" arm — the empty-slice partition
15922        // the serde-default folds onto). The pair jointly pins the
15923        // accessor + declared-slot enumerator composition: any future
15924        // silent detour that had the accessor collapse
15925        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
15926        // "__reserved__")` projection) would silently absorb the
15927        // "declared but degenerate" arm at the accessor boundary and
15928        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
15929        // coherence gate would silently accept a struct-literal
15930        // `Caixa` carrying the drift.
15931        //
15932        // Peer of the sibling
15933        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
15934        // (2a1f907),
15935        // `declared_supervisor_slots_children_arm_routes_through_accessor`
15936        // (c17b51e), and
15937        // `declared_mesh_slots_membros_arm_routes_through_accessor`
15938        // (0f26987) composition pins on the M2 `:upgrade-from` /
15939        // `:children` / M3 `:membros` composite-slice arms — same "the
15940        // enumerator gate must route through the substrate-primitive
15941        // typed dispatch" discipline extended onto the M3 `:contratos`
15942        // composite-slice arm, closing the M3 mesh-slot arm of the
15943        // declared-slot enumerator's routing invariant on the
15944        // composite-slice inputs.
15945        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
15946            "cart", "catalog", "/items",
15947        )]);
15948        let slots = c.declared_mesh_slots();
15949        assert!(
15950            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
15951            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
15952             `:contratos` is non-empty — the accessor and the enumerator \
15953             gate must route through the same substrate-primitive \
15954             typed dispatch on the outer :contratos presence bit (got \
15955             slots={slots:?})",
15956        );
15957        let c = caixa_aplicacao_with_contratos(vec![]);
15958        let slots = c.declared_mesh_slots();
15959        assert!(
15960            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
15961            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
15962             when `:contratos` is empty — the author-omitted arm must \
15963             route through the accessor's empty-slice return unchanged \
15964             (got slots={slots:?})",
15965        );
15966    }
15967
15968    #[test]
15969    fn aplicacao_view_contratos_arm_routes_through_accessor() {
15970        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
15971        // fold-in arm must key off [`Caixa::contratos`], not the raw
15972        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
15973        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
15974        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
15975        // per-edge list through the accessor into the typed
15976        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
15977        // every entry the accessor surfaces must land in the view's
15978        // `contratos` slot in the same order. The pair jointly pins
15979        // the accessor + view-composer composition: a future silent
15980        // detour that had the accessor shuffle or drop an edge would
15981        // silently split the paired declared-slot enumerator's
15982        // presence bit from the typed-view composer's edge-list, a
15983        // two-consumer split at the enumerator and the view composer
15984        // far from the source `caixa.lisp`.
15985        //
15986        // Peer of the sibling
15987        // `aplicacao_view_membros_arm_routes_through_accessor`
15988        // (0f26987) composition pin on the M3 `:membros` outer-
15989        // `&[Composite]` composite-slice arm, closing the aplicacao-
15990        // view composer's routing invariant on the composite-slice
15991        // inputs at the outer altitude.
15992        let c = caixa_aplicacao_with_contratos(vec![
15993            contrato_http_for_test("cart", "catalog", "/items"),
15994            contrato_http_for_test("cart", "pricing", "/price"),
15995        ]);
15996        let view = c
15997            .aplicacao_view()
15998            .expect("Aplicacao kind must produce an aplicacao_view");
15999        assert_eq!(
16000            view.contratos(),
16001            c.contratos(),
16002            "aplicacao_view must fold Caixa::contratos verbatim into \
16003             AplicacaoSpec::contratos — the accessor and the view \
16004             composer must route through the same substrate-primitive \
16005             typed dispatch on the outer :contratos slice (got view \
16006             contratos={:?}, expected {:?})",
16007            view.contratos(),
16008            c.contratos(),
16009        );
16010    }
16011
16012    #[test]
16013    fn contratos_projects_slice_by_borrow() {
16014        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
16015        // by borrow — the returned slice borrows the underlying
16016        // `Vec<WitContract>` storage of the `:contratos` slot and the
16017        // accessor must not clone the backing `Vec` on every call.
16018        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
16019        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
16020        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16021        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16022        // `exe_projects_slice_by_borrow` 65d9527,
16023        // `servicos_projects_slice_by_borrow` 611f78b,
16024        // `deps_projects_slice_by_borrow` ad34b4e,
16025        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16026        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
16027        // `children_projects_slice_by_borrow` c17b51e,
16028        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
16029        // outer top-level [`Caixa`] scalar-element and composite-
16030        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
16031        // composite-element `&[Composite]` axis on the by-borrow pin:
16032        // the accessor's returned slice must borrow from `&self` (the
16033        // returned reference's lifetime is tied to `&self`), and
16034        // calling the accessor twice on the same [`Caixa`] must yield
16035        // slices that are pointer-equal (the underlying byte-buffer is
16036        // the storage `Vec`'s allocation, not a fresh copy) as well as
16037        // value-equal (idempotent, no side effects on `&self`).
16038        //
16039        // Pins against a future silent detour that returned an owned
16040        // `Vec<WitContract>` (which would type-check but silently clone
16041        // on every call), a `&Vec<WitContract>` return (which would
16042        // leak the backing `Vec`'s grow/push/reserve surface no
16043        // downstream consumer reaches for), or a one-arm-only accessor
16044        // that returned a saturating value on some sentinel input.
16045        for contratos in [
16046            vec![],
16047            vec![contrato_http_for_test("cart", "catalog", "/items")],
16048            vec![
16049                contrato_http_for_test("cart", "catalog", "/items"),
16050                contrato_http_for_test("cart", "pricing", "/price"),
16051            ],
16052        ] {
16053            let c = caixa_aplicacao_with_contratos(contratos.clone());
16054            let first = c.contratos();
16055            let second = c.contratos();
16056            assert_eq!(
16057                first, second,
16058                "Caixa::contratos must be idempotent — two successive \
16059                 calls on the same &self must return the same \
16060                 &[WitContract]",
16061            );
16062            assert_eq!(
16063                first.as_ptr(),
16064                second.as_ptr(),
16065                "Caixa::contratos must borrow the underlying \
16066                 Vec<WitContract> storage — two successive calls must \
16067                 return slices with the same backing pointer (a fresh \
16068                 Vec<WitContract> clone would change the pointer on \
16069                 every call)",
16070            );
16071            assert_eq!(
16072                first,
16073                contratos.as_slice(),
16074                "Caixa::contratos must return :contratos verbatim by \
16075                 borrow — got {first:?}, expected {contratos:?}",
16076            );
16077        }
16078    }
16079
16080    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
16081
16082    #[test]
16083    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
16084        // Load-bearing invariant: every multi-word top-level [`Caixa`]
16085        // serde-derived JSON key routes through a lifted `&'static str`
16086        // const. The Rust field names are `snake_case`
16087        // (`deps_dev` / `upgrade_from` / `max_restarts` /
16088        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
16089        // "camelCase")]` derive attribute maps each to the camelCase
16090        // byte-string the [`Caixa::to_lisp`] round-trip's
16091        // `serde_json::to_value(self)` step lands under before
16092        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
16093        // to the kebab-case `:deps-dev` / `:upgrade-from` /
16094        // `:max-restarts` / `:restart-window` author surface. Serialize
16095        // a fully-populated [`Caixa`] and pin that each canonical
16096        // byte-sequence appears verbatim in the JSON — a future
16097        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
16098        // verbatim-field-name flip at the derive attribute (any of
16099        // which would silently break every [`Caixa::to_lisp`]
16100        // round-trip and the future M4 operator-side manifest ingest's
16101        // `Value::get(<key>)` navigation) surfaces here as a build-time
16102        // test failure at `manifest.rs`, not as an apply-time
16103        // `.get(<stale-canonical-const>)` returning `None` far from the
16104        // derive-attr drift's commit. Same discipline the sibling
16105        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
16106        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
16107        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
16108        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
16109        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
16110        // [`UpgradeFromEntry`] per-entry axes — extended here to the
16111        // enclosing M0 [`Caixa`] top-level axis so the last of the four
16112        // multi-word top-level [`Caixa`] serde-derived JSON keys
16113        // (`depsDev`) joins the substrate's "one canonical byte-string
16114        // per typed serialized-key axis" discipline.
16115        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16116        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16117        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16118        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
16119        c.upgrade_from = vec![UpgradeFromEntry {
16120            from: "0.0.1".into(),
16121            instructions: vec![UpgradeInstruction::Restart],
16122        }];
16123        c.estrategia = Some(RestartStrategy::OneForOne);
16124        c.max_restarts = Some(3);
16125        c.restart_window = Some("60s".into());
16126        c.children = vec![ChildSpec {
16127            caixa: "child".into(),
16128            versao: "^0.1".into(),
16129            restart: RestartPolicy::Permanent,
16130        }];
16131        let json = serde_json::to_string(&c).unwrap();
16132        for key in [
16133            crate::render::CAIXA_KEY_DEPS_DEV,
16134            crate::render::M2_KEY_UPGRADE_FROM,
16135            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16136            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16137        ] {
16138            let quoted = format!("\"{key}\"");
16139            assert!(
16140                json.contains(&quoted),
16141                "serialized Caixa must carry the lifted top-level \
16142                 multi-word byte-sequence {quoted} verbatim in the JSON \
16143                 emission (got: {json})",
16144            );
16145        }
16146    }
16147
16148    #[test]
16149    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
16150        // Cross-axis drift-detection pin: a future collapse of the four
16151        // canonical [`Caixa`] top-level multi-word byte-strings onto the
16152        // same value (e.g. an accidental copy-paste flip of
16153        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
16154        // `"upgradeFrom"`) would silently reroute every downstream
16155        // `Value::get(<key>)` probe on one axis onto the sibling axis's
16156        // top-level entry and pass every propagation-probe test that
16157        // expected only the stale axis's value. Peer of the sibling
16158        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
16159        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
16160        let all = [
16161            crate::render::CAIXA_KEY_DEPS_DEV,
16162            crate::render::M2_KEY_UPGRADE_FROM,
16163            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16164            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16165        ];
16166        for (i, a) in all.iter().enumerate() {
16167            for b in all.iter().skip(i + 1) {
16168                assert_ne!(
16169                    a, b,
16170                    "Caixa top-level multi-word key consts must be \
16171                     pairwise-distinct canonical byte-sequences — got \
16172                     `{a}` == `{b}`",
16173                );
16174            }
16175        }
16176    }
16177
16178    #[test]
16179    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
16180        // Shape-pin: every [`Caixa`] top-level multi-word key const must
16181        // be a lowerCamelCase byte-sequence (no `snake_case`
16182        // underscores, no `kebab-case` hyphens, no leading colon, no
16183        // `PascalCase` leading capital, no whitespace / dots) — the
16184        // canonical shape the `#[serde(rename_all = "camelCase")]`
16185        // derive produces on [`Caixa`]. A future flip to a
16186        // non-camelCase attribute at the derive surfaces both here
16187        // (this test fails on the stale-constant shape) and at
16188        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16189        // (that test fails on the mismatch between const and derive).
16190        // Peer with `membro_key_consts_are_lower_camel_case_shape`
16191        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
16192        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
16193        for key in [
16194            crate::render::CAIXA_KEY_DEPS_DEV,
16195            crate::render::M2_KEY_UPGRADE_FROM,
16196            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16197            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16198        ] {
16199            assert!(
16200                !key.is_empty(),
16201                "Caixa top-level multi-word key const must be non-empty \
16202                 (got {key:?})"
16203            );
16204            let first = key.chars().next().unwrap();
16205            assert!(
16206                first.is_ascii_lowercase(),
16207                "Caixa top-level multi-word key const must lead with an \
16208                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
16209            );
16210            assert!(
16211                key.chars().all(|c| c.is_ascii_alphanumeric()),
16212                "Caixa top-level multi-word key const must be \
16213                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
16214                 whitespace (got {key:?})",
16215            );
16216        }
16217    }
16218
16219    #[test]
16220    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
16221        // Scalar-value pin: the byte-string the
16222        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
16223        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
16224        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
16225        // → `depsTest` matching a hypothetical per-test-target
16226        // vocabulary flip) lands as an edit to exactly one const AND
16227        // one derive attribute — the sibling
16228        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16229        // pin already ties the const to the derive attribute, so a
16230        // rebrand that touches only one side of the pair fails at
16231        // caixa-core build time. Same "scalar-value pin per const"
16232        // discipline the sibling
16233        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
16234        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
16235        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
16236        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
16237    }
16238
16239    #[test]
16240    fn caixa_key_deps_pins_canonical_byte_string() {
16241        // Scalar-value pin: the byte-string the
16242        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
16243        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
16244        // on the two-list dep-graph serialized-key axis — the sibling
16245        // pin covers the multi-word `deps_dev → depsDev` camelCase
16246        // arm, this pin covers the single-word `deps → deps` no-op arm
16247        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
16248        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
16249        // axis and the emitted JSON key equals the source-side field
16250        // name byte-for-byte). A future [`crate::Caixa::deps`] field
16251        // rename (`deps` → `dependencies` matching Cargo's verbatim
16252        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
16253        // hypothetical per-runtime-target vocabulary flip) OR an added
16254        // `#[serde(rename = "…")]` explicit override lands as an edit
16255        // to exactly one const AND one derive-attr / field name — the
16256        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
16257        // pin ties the const to the emitted JSON key, so a rebrand
16258        // that touches only one side of the pair fails at caixa-core
16259        // build time.
16260        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
16261    }
16262
16263    #[test]
16264    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
16265        // Load-bearing invariant on the single-word `deps` top-level
16266        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
16267        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
16268        // `serde_json::to_value(self)` step emits. Serialize a
16269        // populated [`Caixa`] whose `:deps` slot carries at least one
16270        // entry (the `#[serde(default)]` attribute on the field emits
16271        // an empty `[]` even without members, but a non-empty vec
16272        // additionally covers the codec's per-`Dep`-entry emission
16273        // path) and pin that `"deps"` appears verbatim in the JSON
16274        // emission — a future accidental `rename_all = "snake_case"` /
16275        // `"kebab-case"` flip at the derive attribute (or an added
16276        // `#[serde(rename = "…")]` explicit override on the field, or
16277        // a Rust field rename) would break every [`Caixa::to_lisp`]
16278        // round-trip and the future M4 operator-side manifest ingest's
16279        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
16280        // build-time test failure at `manifest.rs`, not as an
16281        // apply-time `.get(<stale-canonical-const>)` returning `None`
16282        // far from the drift's commit. Peer of the sibling
16283        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16284        // multi-word pin on the same M0 [`Caixa`] top-level
16285        // serialized-key axis, extended here to the single-word arm
16286        // the multi-word test's `rename_all = "camelCase"` sweep can't
16287        // reach (single-word `deps → deps` is a no-op the multi-word
16288        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
16289        // `\"restartWindow\"` byte-scan can never observe).
16290        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16291        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
16292        let json = serde_json::to_string(&c).unwrap();
16293        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
16294        assert!(
16295            json.contains(&quoted),
16296            "serialized Caixa must carry the lifted top-level `deps` \
16297             byte-sequence {quoted} verbatim in the JSON emission (got: \
16298             {json})",
16299        );
16300    }
16301
16302    #[test]
16303    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
16304        // Cross-axis drift-detection pin on the two-list dep-graph
16305        // renderer-side wire-key axis: a future collapse of the
16306        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
16307        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
16308        // same value (e.g. an accidental copy-paste flip of
16309        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
16310        // reroute every downstream `Value::get(<key>)` probe on one
16311        // axis onto the sibling axis's dep-list and pass every
16312        // propagation-probe test that expected only the stale axis's
16313        // value — a dev-only dep would land in the runtime closure at
16314        // publish time, or a runtime dep would be excluded from the
16315        // published lacre. Peer of the sibling four-way distinct pin
16316        // on the top-level multi-word tetrad
16317        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
16318        // and the two-way pin on the sibling
16319        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
16320        // author-facing arm (4da6fba's test), extended here to the
16321        // renderer-side wire-key arm of the same two-list dep-graph
16322        // axis so both halves of the "one canonical byte-string per
16323        // typed axis per (author, wire)" grid carry the same
16324        // distinct-ness discipline.
16325        assert_ne!(
16326            crate::render::CAIXA_KEY_DEPS,
16327            crate::render::CAIXA_KEY_DEPS_DEV,
16328            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
16329             canonical byte-sequences on the two-list dep-graph \
16330             renderer-side wire-key axis"
16331        );
16332    }
16333}