Skip to main content

caixa_core/
manifest.rs

1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4use tatara_lisp::DeriveTataraDomain;
5
6use thiserror::Error;
7
8use crate::{
9    CaixaKind, Dep,
10    behavior::BehaviorSpec,
11    dep::DepError,
12    limits::LimitsSpec,
13    render::{
14        PathShapeViolation, is_computeunit_yaml_extension, is_git_repo_url, is_lisp_extension,
15        is_sandboxed_relative_path,
16    },
17    supervisor::SupervisorSpec,
18    upgrade::UpgradeFromEntry,
19};
20
21/// Top-level manifest for a caixa (a tatara-lisp package).
22///
23/// Authored as `caixa.lisp`:
24///
25/// ```lisp
26/// (defcaixa
27///   :nome        "pangea-tatara-aws"
28///   :versao      "0.1.0"
29///   :kind        Biblioteca
30///   :edicao      "2026"
31///   :descricao   "AWS provider caixa for tatara-lisp"
32///   :repositorio "github:pleme-io/pangea-tatara-aws"
33///   :licenca     "MIT"
34///   :autores     ("pleme-io")
35///   :etiquetas   ("iac" "aws" "pangea")
36///   :deps        ((:nome "caixa-teia"    :versao "^0.1")
37///                 (:nome "iac-forge-ir"  :versao "^0.5"))
38///   :deps-dev    ((:nome "tatara-check"  :versao "*"))
39///   :bibliotecas ("lib/pangea-tatara-aws.lisp"))
40/// ```
41///
42/// Because `Caixa` derives [`tatara_lisp::domain::TataraDomain`], the manifest
43/// is parsed directly by the tatara-lisp compiler — an ill-formed manifest is
44/// a compile error, not a runtime error.
45#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone, PartialEq)]
46#[serde(rename_all = "camelCase")]
47#[tatara(keyword = "defcaixa")]
48pub struct Caixa {
49    /// Package name — the canonical string used in `:deps`, the registry, and
50    /// the default lib/exe entry names.
51    pub nome: String,
52
53    /// Package version — a semver literal like `"0.1.0"`. Parsed lazily via
54    /// [`crate::CaixaVersion::parse`].
55    pub versao: String,
56
57    /// What this caixa produces. See [`CaixaKind`].
58    pub kind: CaixaKind,
59
60    /// Language edition — determines macro surface + compatibility flags.
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub edicao: Option<String>,
63
64    /// Free-form description shown in the registry listing.
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub descricao: Option<String>,
67
68    /// Homepage or repo URL.
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub repositorio: Option<String>,
71
72    /// SPDX license expression — `"MIT"`, `"Apache-2.0 OR MIT"`, etc.
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub licenca: Option<String>,
75
76    /// Authors — free-form strings.
77    #[serde(default)]
78    pub autores: Vec<String>,
79
80    /// Topical tags used for registry search.
81    #[serde(default)]
82    pub etiquetas: Vec<String>,
83
84    /// Runtime dependencies.
85    #[serde(default)]
86    pub deps: Vec<Dep>,
87
88    /// Development-only dependencies (tests, lint, bench).
89    #[serde(default)]
90    pub deps_dev: Vec<Dep>,
91
92    /// Paths to executable entry points (relative to the package root).
93    /// Required when `:kind Binario`.
94    #[serde(default)]
95    pub exe: Vec<String>,
96
97    /// Paths to library entry points (relative to the package root).
98    /// First entry is the canonical `lib/<nome>.lisp`; when omitted under
99    /// `:kind Biblioteca`, the layout check expects `lib/<nome>.lisp`.
100    #[serde(default)]
101    pub bibliotecas: Vec<String>,
102
103    /// Paths to service manifests (relative to the package root).
104    /// Required when `:kind Servico`.
105    #[serde(default)]
106    pub servicos: Vec<String>,
107
108    // ── M2 typed-substrate extensions per theory/ABSORPTION-ROADMAP.md ──
109    //
110    // All four are optional + default to "absent"; existing caixas
111    // round-trip unchanged. Each maps onto a prior-art primitive named
112    // in theory/INSPIRATIONS.md:
113    //
114    //   :limits        — Lunatic per-process limits (§III.1)
115    //   :behavior      — OTP gen_server callbacks  (§II.3)
116    //   :upgrade-from  — OTP appup migration       (§II.4)
117    //   :estrategia    — OTP supervisor strategy   (§II.2 + §III.2)
118    //   :children      — OTP supervisor children    (§II.2 + §III.2)
119    //
120    // The supervisor slots are flat on Caixa (vs nested under a
121    // SupervisorSpec sub-form) to keep tatara-lisp authoring at one
122    // level of nesting; SupervisorSpec exists for validation +
123    // composition convenience (`Caixa::supervisor_view()`).
124    /// Lunatic-style per-process resource limits. None = unbounded.
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub limits: Option<LimitsSpec>,
127
128    /// OTP-shaped behavior callbacks for Servico-kind caixas.
129    /// Authored as `(:on-init "..." :on-call "..." …)`.
130    #[serde(default, skip_serializing_if = "Option::is_none")]
131    pub behavior: Option<BehaviorSpec>,
132
133    /// OTP appup — declarative upgrade instructions per prior version.
134    /// Empty list = no hot-upgrade path declared (caller falls back to
135    /// `:Restart` strategy).
136    #[serde(default)]
137    pub upgrade_from: Vec<UpgradeFromEntry>,
138
139    /// OTP supervisor strategy. Required when `:kind Supervisor`;
140    /// ignored otherwise.
141    #[serde(default, skip_serializing_if = "Option::is_none")]
142    pub estrategia: Option<crate::supervisor::RestartStrategy>,
143
144    /// Max restarts before the supervisor itself fails. Defaults via
145    /// SupervisorSpec at validation time.
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub max_restarts: Option<u32>,
148
149    /// Sliding window for `max_restarts`. Authored as a duration
150    /// string (`"60s"`, `"5m"`).
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub restart_window: Option<String>,
153
154    /// Static children of a supervisor. Required for OneForOne /
155    /// OneForAll / RestForOne; must be empty for SimpleOneForOne.
156    #[serde(default)]
157    pub children: Vec<crate::supervisor::ChildSpec>,
158
159    // ── M3 Aplicacao slots (theory/MESH-COMPOSITION.md) ─────────────────
160    //
161    // Required when :kind Aplicacao; ignored otherwise.
162    // Composed into a typed AplicacaoSpec via Caixa::aplicacao_view().
163    /// Member Servicos that make up this Aplicacao. Each is a
164    /// caixa-name + version-constraint pair. Required for Aplicacao.
165    #[serde(default)]
166    pub membros: Vec<crate::aplicacao::Membro>,
167
168    /// WIT-typed inter-Servico contracts. Each `:de` and `:para`
169    /// must reference a name in `:membros`.
170    #[serde(default)]
171    pub contratos: Vec<crate::aplicacao::WitContract>,
172
173    /// Mesh-level policies (timeout, retries, circuit-breaker, mTLS,
174    /// rate-limit). Apply to every contrato unless overridden per-edge
175    /// in M4.
176    #[serde(default, skip_serializing_if = "Option::is_none")]
177    pub politicas: Option<crate::aplicacao::MeshPolicy>,
178
179    /// Placement strategy across the cluster fleet
180    /// (single-node | replicated | sharded).
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub placement: Option<crate::aplicacao::Placement>,
183
184    /// External entry point — gateway / ingress shape. Optional;
185    /// only for public Aplicacaos.
186    #[serde(default, skip_serializing_if = "Option::is_none")]
187    pub entrada: Option<crate::aplicacao::Entrada>,
188
189    // ── Acao slot (CANTEIRO §7.1-C) ──────────────────────────────────────
190    //
191    // Required when :kind Acao; ignored otherwise (mirrors the M2/
192    // supervisor-tree/M3 slot triads above — a declared-but-foreign `:ci`
193    // is a `LayoutError::CiOnNonAcao` build error, not a silent drop).
194    /// Typed CI run — a repo's CI run as a set of typed nodes + their
195    /// dependency edges. Required for `:kind Acao`; validated (not
196    /// rendered) by the `caixa-actions` renderer via
197    /// `canteiro_types::decompose`. See `caixa-actions`' crate docs for
198    /// the M0 validate-only contract.
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub ci: Option<canteiro_types::CiRun>,
201}
202
203/// Why reading a manifest into a [`Caixa`] failed.
204///
205/// Split from [`ManifestError`] (which reports a *parsed* manifest that is
206/// semantically wrong) because the two answer different questions, and the
207/// distinction is the whole point of this type: `ManifestError` means "your
208/// caixa is wrong", `LeituraError::DialetoEstrangeiro` means "this file is not
209/// a caixa".
210#[derive(Debug, thiserror::Error)]
211pub enum LeituraError {
212    /// The source is not readable as a `(defcaixa …)` package manifest — bad
213    /// syntax, a wrong head symbol, an unknown or mistyped slot.
214    ///
215    /// `#[source]`, not `#[error(transparent)]`. Transparent delegates
216    /// `source()` past the inner error to ITS source, which drops the
217    /// `LispError` off the cause chain — and `feira`'s
218    /// `load_caixa_parse_error_preserves_underlying_lisp_error_on_chain`
219    /// pins that a caller can `downcast_ref::<tatara_lisp::LispError>()`
220    /// through an anyhow context to read the typed payload. That pin caught
221    /// this exact regression when the variant first landed transparent.
222    #[error("{0}")]
223    Leitura(
224        #[source]
225        #[from]
226        tatara_lisp::LispError,
227    ),
228
229    /// The source IS a well-formed `(defcaixa …)` form, but of a different
230    /// declaration than this crate's.
231    ///
232    /// The variant that did not exist before, and whose absence is the defect.
233    /// A `(defcaixa :name "x" :ecosystem :go …)` used to reach the derive's
234    /// `parse_kwargs_strict` and come back as an unknown-keyword rejection —
235    /// byte-identical in shape to a typo in a real manifest. Measured over the
236    /// org checkout on 2026-07-31, that shape is the MAJORITY of the corpus, so
237    /// the confusing error was also the common one.
238    ///
239    /// Carrying the dialect means a consumer can branch on "not mine" without
240    /// re-parsing, and a census can count it. Every user-facing byte-string
241    /// (canonical keyword, one-line description, consuming crate) is a
242    /// projection of [`crate::dialeto::CaixaDialeto`] — the variant stores the
243    /// typed dialect and the `#[error]` template calls
244    /// [`CaixaDialeto::palavra_canonica`] /
245    /// [`CaixaDialeto::descricao`] / [`CaixaDialeto::consumidor`] on it, so
246    /// the three axes cannot silently diverge from the classification. Prior
247    /// to this closure the variant carried each accessor's return value as a
248    /// stored `&'static str` snapshot alongside `dialeto`, and the sole
249    /// constructor at [`Caixa::from_lisp`] filled all four fields — a caller
250    /// could construct `DialetoEstrangeiro { dialeto: Molde,
251    /// palavra_canonica: "defcaixa", … }` and every downstream consumer
252    /// (Display, ad-hoc audit, future JSON serialization) would silently
253    /// disagree with `dialeto.palavra_canonica() == "defmolde"`. The typed
254    /// enum owns the projections; the variant only carries the axis.
255    #[error(
256        "this is a `{palavra}` declaration ({desc}), read by \
257         {cons} — not a caixa-core package manifest. `defcaixa` is the \
258         tatara-lisp package manifest (`:nome :versao :kind :deps …`); the two \
259         are different declarations that shared one keyword until 2026-07-31",
260        palavra = dialeto.palavra_canonica(),
261        desc = dialeto.descricao(),
262        cons = dialeto.consumidor()
263    )]
264    DialetoEstrangeiro {
265        /// Which declaration this actually is. Sole authoritative axis;
266        /// every user-facing projection routes through
267        /// [`crate::dialeto::CaixaDialeto`]'s typed accessors so the four
268        /// axes cannot silently disagree.
269        dialeto: crate::dialeto::CaixaDialeto,
270    },
271
272    /// Not a manifest declaration at all.
273    #[error(transparent)]
274    Dialeto(#[from] crate::dialeto::DialetoError),
275}
276
277impl Caixa {
278    /// Parse a `caixa.lisp` source string to a typed `Caixa`.
279    ///
280    /// Classifies the dialect **before** parsing. A `(defcaixa …)` of another
281    /// declaration is [`LeituraError::DialetoEstrangeiro`], naming what it is
282    /// and who reads it, instead of an unknown-keyword rejection that reads as
283    /// "your manifest is broken".
284    ///
285    /// The ordering is load-bearing. Handing a foreign dialect to the derive
286    /// first and interpreting the failure afterwards would mean guessing from
287    /// an error message, and the guess would be wrong for every file whose
288    /// first unknown slot happens to be one both schemas could plausibly carry.
289    pub fn from_lisp(src: &str) -> Result<Self, LeituraError> {
290        use tatara_lisp::domain::TataraDomain;
291        let forms = tatara_lisp::read(src).map_err(LeituraError::Leitura)?;
292        let first = forms.first().ok_or(crate::dialeto::DialetoError::Vazio)?;
293
294        match crate::dialeto::classify_form(first)? {
295            crate::dialeto::CaixaDialeto::Pacote => {}
296            // `Desconhecido` deliberately falls through to the derive rather
297            // than short-circuiting: a `(defcaixa …)` matching neither schema
298            // is most likely a genuine package manifest with a typo in
299            // `:nome`, and the derive's diagnostic — which names the offending
300            // keyword and suggests the nearest slot — is far better than
301            // anything this classifier could say.
302            crate::dialeto::CaixaDialeto::Desconhecido => {}
303            foreign => {
304                // Only the typed dialect flows into the error — the three
305                // user-facing projections (canonical keyword, description,
306                // consumer) are read at Display time through
307                // [`crate::dialeto::CaixaDialeto`]'s own accessors, so the
308                // variant cannot carry a snapshot that drifts from
309                // [`crate::dialeto::CaixaDialeto::palavra_canonica`] /
310                // `descricao` / `consumidor`.
311                return Err(LeituraError::DialetoEstrangeiro { dialeto: foreign });
312            }
313        }
314
315        Self::compile_from_sexp(first).map_err(LeituraError::Leitura)
316    }
317
318    /// Register `Caixa` with the global tatara-lisp domain registry so
319    /// `defcaixa` is dispatchable from any tatara-lisp binary that seeds
320    /// the registry (e.g. `tatara-check`).
321    ///
322    /// `pending-fallible-register`: upstream `tatara_lisp::domain::register`
323    /// became `-> Result<(), KeywordCollision>` on 2026-07-31, so a second type
324    /// claiming `defcaixa` in one process is refused and named instead of
325    /// silently displacing this one. This workspace pins
326    /// `tatara-lisp = "0.3.3"`, which predates that, so the result cannot be
327    /// checked here yet. Propagate it — `pub fn register() -> Result<(),
328    /// tatara_lisp::KeywordCollision>` — in the same commit that bumps the pin.
329    pub fn register() {
330        tatara_lisp::domain::register::<Self>();
331    }
332
333    /// Substrate-canonical per-`Caixa` `:licenca` SPDX-expression scalar
334    /// accessor every consumer of the top-level manifest's license axis
335    /// keys off — returns the author-declared `:licenca` byte-string
336    /// verbatim as an `Option<&str>`, borrowed from the typed slot's own
337    /// `Option<String>` storage. `None` when the slot is absent (the
338    /// canonical "omit to defer to the caixa-helm renderer's `MIT`
339    /// fallback" shape [`Self::validate_licenca`] documents at
340    /// caixa-core/src/manifest.rs:1560; the peer [`caixa-helm`]
341    /// `build_readme` fold at caixa-helm/src/lib.rs:962 reads this
342    /// predicate too, so an authored-but-unset `:licenca` round-trips to
343    /// a rendered `lareira-<nome>` chart's `README.md` `## License`
344    /// section structurally identical to one that omits the slot).
345    ///
346    /// The `:licenca` slot carries the universal-axis SPDX-expression
347    /// license identifier every kind of caixa emits under (CAIXA-SDLC
348    /// §I — the author-facing surface every `defcaixa` form supplies) —
349    /// the typed slot's `Option<String>` accept-set (empty-string
350    /// rejected through [`ManifestError::LicencaEmpty`], SPDX-alphabet-
351    /// invalid rejected through [`ManifestError::LicencaInvalid`]) maps
352    /// onto the `lareira-<nome>` Helm chart's `README.md` `## License`
353    /// section (caixa-helm/src/lib.rs:962) and (through future
354    /// tightening documented at [`Self::validate_licenca`]) the
355    /// Chart.yaml `annotations["artifacthub.io/license"]` axis every
356    /// registry-facing chart carries. Every downstream consumer that
357    /// reads the license byte-string keys off this scalar (the
358    /// [`Self::validate_licenca`] empty-arm + SPDX-shape gate that
359    /// routes through `self.licenca.as_deref()`, the caixa-helm
360    /// `build_readme` `unwrap_or_else(|| "MIT".into())` fold that keys
361    /// the fallback off the `Option::is_none()` arm, every future
362    /// per-`Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
363    /// acknowledges).
364    ///
365    /// Prior to this lift the `.licenca` field was accessed inline at
366    /// two production sites — [`Self::validate_licenca`]'s
367    /// `self.licenca.as_deref()` empty-and-shape gate binding and the
368    /// caixa-helm `build_readme` `caixa.licenca.clone().unwrap_or_else(||
369    /// "MIT".into())` `README.md` `## License` fold — two open-coded
370    /// field-accesses that expressed no compile-time link back to the
371    /// typed slot. A future extension of the `:licenca` axis to a
372    /// richer author surface — a per-`:licenca` structured SPDX
373    /// expression parser + license-id allowlist (the future tightening
374    /// [`Self::validate_licenca`]'s docstring acknowledges), a
375    /// per-cluster license-default overlay the M4 CR materializer
376    /// resolves per-CR (the "cluster policy pins `Apache-2.0` for every
377    /// unlisted caixa" arm), a promotion of the plain
378    /// `Option<String>` byte-string to a richer `SpdxExpression` enum
379    /// once the SPDX-expression parser lands — would have had to be
380    /// threaded through both open-coded copies in lockstep or the
381    /// validate gate and the caixa-helm emit path would silently
382    /// disagree on which license a given [`Caixa`] resolves to (an
383    /// author's `:licenca "MIT OR Apache-2.0"` would satisfy validate
384    /// while the emit path silently rendered a stale `MIT` fallback,
385    /// or vice versa). Lifting the resolution to a typed method on the
386    /// substrate primitive means every downstream consumer of the
387    /// caixa's per-`Caixa` license surface reaches for exactly one
388    /// typed dispatch — the resolver's accept-set migrates as a unit
389    /// on any future axis addition.
390    ///
391    /// First `Option<&str>`-return top-level [`Caixa`] scalar accessor —
392    /// opens the "outer [`Caixa`] `Option<&str>` scalar" projection
393    /// pattern the sibling per-`Caixa` `:descricao` / `:repositorio` /
394    /// `:edicao` future lifts fold on. Same "one typed dispatch on the
395    /// substrate primitive, thin projections at each consumer"
396    /// discipline the peer per-`:placement` [`crate::aplicacao::Placement::shard_key`]
397    /// (7cd2a28) / [`crate::aplicacao::Placement::affinity`] (74ec2d3)
398    /// / per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
399    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
400    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
401    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
402    /// typed-slot atom axes, extended here to the outer top-level
403    /// `Caixa` universal-axis surface. Named `licenca()` to match the
404    /// storage field's name; the accessor's identity maps onto the
405    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
406    /// carries.
407    #[must_use]
408    pub fn licenca(&self) -> Option<&str> {
409        self.licenca.as_deref()
410    }
411
412    /// Substrate-canonical per-`Caixa` `:repositorio` git-repo-URL scalar
413    /// accessor every consumer of the top-level manifest's homepage /
414    /// source-of-truth axis keys off — returns the author-declared
415    /// `:repositorio` byte-string verbatim as an `Option<&str>`, borrowed
416    /// from the typed slot's own `Option<String>` storage. `None` when
417    /// the slot is absent (the canonical "omit to defer to the renderer's
418    /// per-target placeholder" shape — [`caixa-helm`]'s `ChartYaml.home`
419    /// carries the `Option<String>` through verbatim so an author-omitted
420    /// `:repositorio` renders a `Chart.yaml` without a `home:` field
421    /// (`skip_serializing_if = "Option::is_none"`), while [`caixa-flux`]'s
422    /// `ClusterBundleOpts::for_caixa` folds the omitted slot through a
423    /// `format!("https://github.com/{DEFAULT_PLEME_GIT_ORG}/{nome}")`
424    /// fallback derived from `caixa.nome`).
425    ///
426    /// The `:repositorio` slot carries the universal-axis git-repo-URL
427    /// homepage identifier every kind of caixa emits under (CAIXA-SDLC
428    /// §I — the author-facing surface every `defcaixa` form supplies) —
429    /// the typed slot's `Option<String>` accept-set (empty-string
430    /// rejected through [`ManifestError::RepositorioEmpty`], git-repo-URL-
431    /// shape-invalid rejected through [`ManifestError::RepositorioInvalid`]
432    /// past the shared [`crate::render::is_git_repo_url`] predicate the
433    /// peer per-`:deps :fonte :repo` axis also routes through) maps onto
434    /// four load-bearing downstream consumers:
435    ///
436    ///   - [`Self::validate_repositorio`]'s empty-arm + shape-predicate
437    ///     gate binding at caixa-core/src/manifest.rs:1456 — the
438    ///     universal-axis identity gate wired at caixa-build time.
439    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.home` fold at
440    ///     caixa-helm/src/lib.rs:840 — the rendered `lareira-<nome>`
441    ///     Helm chart's `Chart.yaml` `home:` field, which every registry
442    ///     that ingests the chart (ArtifactHub, chartmuseum,
443    ///     `helm search repo`) surfaces as the chart's canonical source-
444    ///     of-truth link.
445    ///   - [`caixa-helm`]'s `build_readme` `## Source` fold at
446    ///     caixa-helm/src/lib.rs:957 — the rendered `lareira-<nome>`
447    ///     chart's `README.md` header link back to the source repo,
448    ///     which every author who inspects the rendered chart bundle
449    ///     lands at.
450    ///   - [`caixa-flux`]'s `ClusterBundleOpts::for_caixa`
451    ///     `GitRepository.spec.url` fold at caixa-flux/src/lib.rs:2006 —
452    ///     the rendered `GitRepository` CR's `spec.url` field, which
453    ///     FluxCD's `source-controller` polls to reconcile the caixa's
454    ///     manifest bundle from git.
455    ///
456    /// Prior to this lift the `.repositorio` field was accessed inline
457    /// at four production sites — [`Self::validate_repositorio`]'s
458    /// `self.repositorio.as_deref()` empty-and-shape gate binding, the
459    /// caixa-helm `build_chart_yaml` `caixa.repositorio.clone()`
460    /// `Chart.yaml` `home:` field fold, the caixa-helm `build_readme`
461    /// `caixa.repositorio.clone().unwrap_or_else(|| caixa.nome.clone())`
462    /// `README.md` `## Source` fold, and the caixa-flux
463    /// `ClusterBundleOpts::for_caixa`
464    /// `caixa.repositorio.clone().unwrap_or_else(|| format!(...))`
465    /// `GitRepository.spec.url` fold — four open-coded field-accesses
466    /// that expressed no compile-time link back to the typed slot. A
467    /// future extension of the `:repositorio` axis to a richer author
468    /// surface — a per-`:repositorio` structured
469    /// [`crate::render::GitRepoUrl`]-shaped scheme+host+path parse
470    /// (the future tightening [`Self::validate_repositorio`]'s
471    /// docstring anticipates alongside the peer per-`:deps :fonte
472    /// :repo` axis), a per-cluster repo-mirror overlay the M4 CR
473    /// materializer resolves per-CR (the "cluster policy rewrites
474    /// `github:pleme-io/...` to `git.internal/mirror/pleme-io/...`"
475    /// arm the private-registry story acknowledges), a promotion of
476    /// the plain `Option<String>` byte-string to a richer
477    /// `RepoUrl` enum discriminated on scheme — would have had to be
478    /// threaded through all four open-coded copies in lockstep or the
479    /// validate gate and the three emit paths would silently disagree
480    /// on which URL a given [`Caixa`] resolves to (an author's
481    /// `:repositorio "github:pleme-io/checkout"` would satisfy validate
482    /// while one of the emit paths silently rendered a stale URL, or
483    /// vice versa). Lifting the resolution to a typed method on the
484    /// substrate primitive means every downstream consumer of the
485    /// caixa's per-`Caixa` repo-URL surface reaches for exactly one
486    /// typed dispatch — the resolver's accept-set migrates as a unit on
487    /// any future axis addition.
488    ///
489    /// Second outer top-level [`Caixa`] `Option<&str>`-return scalar
490    /// accessor — sibling of [`Self::licenca`] (6d5bc28), the accessor
491    /// that opened the "outer [`Caixa`] `Option<&str>` scalar"
492    /// projection pattern this lift folds on. Same "one typed dispatch
493    /// on the substrate primitive, thin projections at each consumer"
494    /// discipline the peer per-`:placement`
495    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
496    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
497    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
498    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
499    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
500    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
501    /// typed-slot atom axes, extended here to the second outer top-level
502    /// `Caixa` universal-axis surface. Named `repositorio()` to match
503    /// the storage field's name; the accessor's identity maps onto the
504    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
505    /// carries.
506    #[must_use]
507    pub fn repositorio(&self) -> Option<&str> {
508        self.repositorio.as_deref()
509    }
510
511    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
512    /// chart-description scalar accessor every consumer of the top-level
513    /// manifest's Chart.yaml `description:` axis keys off — returns the
514    /// author-declared `:descricao` byte-string verbatim as an
515    /// `Option<&str>`, borrowed from the typed slot's own
516    /// `Option<String>` storage. `None` when the slot is absent (the
517    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
518    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
519    /// omitted slot through a `format!("Generated chart for caixa Servico
520    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
521    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
522    /// and [`caixa-feira`]'s `render_flake` folds it through a
523    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
524    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
525    ///
526    /// The `:descricao` slot carries the universal-axis free-form-prose
527    /// chart-description identifier every kind of caixa emits under
528    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
529    /// supplies) — the typed slot's `Option<String>` accept-set
530    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
531    /// chart-description-shape-invalid rejected through
532    /// [`ManifestError::DescricaoInvalid`] past the shared
533    /// [`crate::render::is_chart_description_shape`] predicate the peer
534    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
535    /// load-bearing downstream consumers:
536    ///
537    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
538    ///     gate binding — the universal-axis identity gate wired at
539    ///     caixa-build time.
540    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
541    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
542    ///     chart's `Chart.yaml` `description:` field, which
543    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
544    ///     `WARNING [chart.metadata.description]: description is required`
545    ///     when absent) and which every registry that ingests the chart
546    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
547    ///     chart's canonical one-line prose descriptor.
548    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
549    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
550    ///     header directly beneath the `# <chart-name>` title, which
551    ///     every author who inspects the rendered chart bundle lands at.
552    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
553    ///     top-level fold — the emitted `flake.nix`'s `description`
554    ///     field, which every Nix consumer (`nix flake show`,
555    ///     `nix flake metadata`, downstream flake-registry ingestors)
556    ///     surfaces as the flake's canonical descriptor.
557    ///
558    /// Prior to this lift the `.descricao` field was accessed inline at
559    /// four production sites — [`Self::validate_descricao`]'s
560    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
561    /// caixa-helm `build_chart_yaml`
562    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
563    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
564    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
565    /// `README.md` header fold, and the caixa-feira `render_flake`
566    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
567    /// `description = ""` fold — four open-coded field-accesses that
568    /// expressed no compile-time link back to the typed slot. A future
569    /// extension of the `:descricao` axis to a richer author surface —
570    /// a per-`:descricao` locale-tagged multi-language descriptor map
571    /// (the "one caixa, N language-tagged prose descriptions" arm
572    /// author-tooling internationalization anticipates), a
573    /// per-registry-target length-and-shape overlay the M4 CR
574    /// materializer resolves per-CR (the "ArtifactHub caps description
575    /// at 512 bytes but the internal registry caps at 256" arm), a
576    /// promotion of the plain `Option<String>` byte-string to a richer
577    /// `ChartDescription` newtype guaranteeing the
578    /// `is_chart_description_shape` predicate at the type level — would
579    /// have had to be threaded through all four open-coded copies in
580    /// lockstep or the validate gate and the three emit paths would
581    /// silently disagree on which prose string a given [`Caixa`]
582    /// resolves to (an author's
583    /// `:descricao "Checkout flow orchestration."` would satisfy
584    /// validate while one of the emit paths silently rendered a stale
585    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
586    /// resolution to a typed method on the substrate primitive means
587    /// every downstream consumer of the caixa's per-`Caixa`
588    /// chart-description surface reaches for exactly one typed dispatch
589    /// — the resolver's accept-set migrates as a unit on any future
590    /// axis addition.
591    ///
592    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
593    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
594    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
595    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
596    /// lift folds on. Same "one typed dispatch on the substrate
597    /// primitive, thin projections at each consumer" discipline the
598    /// peer per-`:placement`
599    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
600    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
601    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
602    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
603    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
604    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
605    /// typed-slot atom axes, extended here to the third outer top-level
606    /// `Caixa` universal-axis surface. Named `descricao()` to match the
607    /// storage field's name; the accessor's identity maps onto the
608    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
609    /// carries. The one remaining universal `Option<String>` slot
610    /// (`:edicao`) folds on this pattern next.
611    #[must_use]
612    pub fn descricao(&self) -> Option<&str> {
613        self.descricao.as_deref()
614    }
615
616    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
617    /// accessor every consumer of the top-level manifest's tatara-lisp
618    /// edition-selector axis keys off — returns the author-declared
619    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
620    /// the typed slot's own `Option<String>` storage. `None` when the
621    /// slot is absent (the canonical "omit the slot to defer to the
622    /// substrate's default edition" shape every existing
623    /// [`caixa-resolver`] integration test fixture carries via
624    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
625    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
626    /// arm by construction, so an author-omitted `:edicao` round-trips
627    /// to a build without triggering the year-shape predicate).
628    ///
629    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
630    /// decimal-year language-edition identifier every kind of caixa
631    /// emits under (CAIXA-SDLC §I — the author-facing surface every
632    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
633    /// accept-set (empty-string rejected through
634    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
635    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
636    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
637    /// onto one load-bearing downstream consumer today
638    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
639    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
640    /// future edition-aware substrate consumer the CAIXA-SDLC §I
641    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
642    /// selector every edition-aware build step keys off, the future
643    /// per-edition compatibility-flag overlay the M4 CR materializer
644    /// resolves per-CR, the peer [`Caixa::template`] canonical
645    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
646    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
647    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
648    /// carry `edicao: Some("2026".into())` by construction).
649    ///
650    /// Prior to this lift the `.edicao` field was accessed inline at
651    /// one production site — [`Self::validate_edicao`]'s
652    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
653    /// open-coded field-access that expressed no compile-time link
654    /// back to the typed slot. A future extension of the `:edicao`
655    /// axis to a richer author surface — a per-`:edicao` known-
656    /// edition allowlist (the future tightening
657    /// [`Self::validate_edicao`]'s docstring acknowledges past the
658    /// structural year-shape floor, rejecting year-shaped values that
659    /// don't name a tatara-lisp edition the substrate actually
660    /// understands — `"1999"` is year-shaped but no `1999` edition
661    /// exists), a per-edition compatibility-flag overlay the M4 CR
662    /// materializer resolves per-CR (the "edition `"2026"` enables
663    /// macro-surface features the sibling `"2018"` gates behind a
664    /// feature flag" arm the edition-selector story anticipates), a
665    /// promotion of the plain `Option<String>` byte-string to a
666    /// richer `CaixaEdition` enum discriminated on year once a sibling
667    /// edition to `"2026"` lands — would have had to be threaded
668    /// through the open-coded copy in lockstep with every future
669    /// edition-aware consumer, or the validate gate and the future
670    /// edition-aware consumer path would silently disagree on which
671    /// edition a given [`Caixa`] resolves to (an author's
672    /// `:edicao "2026"` would satisfy validate while a future
673    /// edition-aware consumer silently defaulted to a stale edition,
674    /// or vice versa). Lifting the resolution to a typed method on
675    /// the substrate primitive means every downstream consumer of the
676    /// caixa's per-`Caixa` edition surface reaches for exactly one
677    /// typed dispatch — the resolver's accept-set migrates as a unit
678    /// on any future axis addition.
679    ///
680    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
681    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
682    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
683    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
684    /// `Option<&str>` scalar" projection pattern this lift folds on.
685    /// Same "one typed dispatch on the substrate primitive, thin
686    /// projections at each consumer" discipline the peer per-`:placement`
687    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
688    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
689    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
690    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
691    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
692    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
693    /// typed-slot atom axes, extended here to close the outer top-level
694    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
695    /// slot. Named `edicao()` to match the storage field's name; the
696    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
697    /// vocabulary the slot's docstring already carries.
698    #[must_use]
699    pub fn edicao(&self) -> Option<&str> {
700        self.edicao.as_deref()
701    }
702
703    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
704    /// label caixa-identity scalar accessor every consumer of the top-
705    /// level manifest's identity axis keys off — returns the author-
706    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
707    /// the typed slot's own `String` storage. Non-optional (`:nome` is
708    /// a required-axis scalar every `defcaixa` form must supply; the
709    /// [`Self::from_lisp`] derive rejects an omitted / non-string
710    /// `:nome` at parse time, so a `Caixa` past parse definitionally
711    /// carries a non-`None` `:nome`).
712    ///
713    /// The `:nome` slot carries the universal-axis DNS-1123-label
714    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
715    /// the primary identity axis every `defcaixa` form supplies
716    /// alongside `:versao` / `:kind`; the substrate-wide identity every
717    /// other typed surface that names a caixa reaches through — `:deps`
718    /// entries, `:membros` entries, `:children` entries, the
719    /// `lareira-<nome>` Helm chart name every per-Servico renderer
720    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
721    /// renderer emits) — the typed slot's `String` accept-set (empty
722    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
723    /// invalid rejected through [`ManifestError::NomeInvalid`] past
724    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
725    /// the peer name axes each land on, joint-length-with-`lareira-`-
726    /// prefix rejected through
727    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
728    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
729    /// load-bearing downstream consumer the substrate carries — the
730    /// two universal-axis validate gates at caixa-build time
731    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
732    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
733    /// derivation every per-Servico renderer keys off, the caixa-helm
734    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
735    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
736    /// `HTTPRoute` per-Aplicacao name axes at
737    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
738    /// [`crate::pleme_program_selector`] /
739    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
740    /// derivations, and every future substrate renderer that emits an
741    /// artifact keyed by the caixa's identity.
742    ///
743    /// Prior to this lift the `.nome` field was accessed inline at a
744    /// dozen production sites across `caixa-core` (the two universal-
745    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
746    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
747    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
748    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
749    /// entry `name:` fold, the `flux_kustomization_source_subtree`
750    /// per-cluster subpath derivation), and `caixa-mesh` (the
751    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
752    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
753    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
754    /// insert) — a dozen open-coded field-accesses that expressed no
755    /// compile-time link back to the typed slot. A future extension of
756    /// the `:nome` axis to a richer author surface — a per-`:nome`
757    /// structured `CaixaIdentity` newtype that carries the joint-
758    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
759    /// enforces at the type level (rather than as a validate-time
760    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
761    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
762    /// `partner-org/checkout` collision" arm the multi-tenant-registry
763    /// story acknowledges), a promotion of the plain `String` byte-
764    /// string to a richer `CaixaNome` newtype discriminated on
765    /// namespace prefix — would have had to be threaded through every
766    /// open-coded copy in lockstep or the two validate gates and the
767    /// dozen emit paths would silently disagree on which identity a
768    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
769    /// would satisfy validate while one of the emit paths silently
770    /// rendered a drifted other identity, or vice versa). Lifting the
771    /// resolution to a typed method on the substrate primitive means
772    /// every downstream consumer of the caixa's per-`Caixa` identity
773    /// surface reaches for exactly one typed dispatch — the resolver's
774    /// accept-set migrates as a unit on any future axis addition.
775    ///
776    /// First outer top-level [`Caixa`] `&str`-return required-scalar
777    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
778    /// projection pattern the sibling per-`Caixa` `:versao` future lift
779    /// folds on. Sibling in shape to the peer per-`:membros`
780    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
781    /// [`crate::aplicacao::WitContract::source`] /
782    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
783    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
784    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
785    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
786    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
787    /// per-sub-struct required-axis accessors carry on the sibling M3
788    /// mesh-slot-atom scalar-value axes, extended here to open the
789    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
790    /// Named `nome()` to match the storage field's name; the accessor's
791    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
792    /// slot's docstring already carries.
793    #[must_use]
794    pub fn nome(&self) -> &str {
795        &self.nome
796    }
797
798    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
799    /// pinned-version scalar accessor every consumer of the top-level
800    /// manifest's version axis keys off — returns the author-declared
801    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
802    /// typed slot's own `String` storage. Non-optional (`:versao` is a
803    /// required-axis scalar every `defcaixa` form must supply alongside
804    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
805    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
806    /// parse definitionally carries a non-`None` `:versao`).
807    ///
808    /// The `:versao` slot carries the universal-axis SemVer-2
809    /// concrete-version body every kind of caixa emits under
810    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
811    /// supplies alongside `:nome` / `:kind`; the substrate-wide
812    /// pinned-version every downstream artifact-emitting consumer
813    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
814    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
815    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
816    /// prefix composes on top of, the programs.yaml entry's `versao:`
817    /// value the `lareira-fleet-programs` aggregator carries onto each
818    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
819    /// tags every substrate-side `skopeo push` writes, the lacre
820    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
821    /// prior-version references peers in the exact same SemVer-2 shape).
822    /// The typed slot's `String` accept-set (empty rejected through
823    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
824    /// through [`ManifestError::VersaoInvalid`] past
825    /// [`semver::Version::parse`]) maps onto every load-bearing
826    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
827    /// universal-axis validate gate at caixa-build time, the
828    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
829    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
830    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
831    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
832    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
833    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
834    /// tag derivation (`format!("{prefix}{versao}")`), and every future
835    /// substrate renderer that emits an artifact keyed by the caixa's
836    /// pinned version.
837    ///
838    /// Prior to this lift the `.versao` field was accessed inline at a
839    /// dozen production sites across `caixa-core` (the universal-axis
840    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
841    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
842    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
843    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
844    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
845    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
846    /// (the `feira publish` git-tag derivation + the `feira app graph` /
847    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
848    /// field-accesses that expressed no compile-time link back to the
849    /// typed slot. A future extension of the `:versao` axis to a richer
850    /// author surface — a per-`:versao` structured `CaixaVersion` at the
851    /// storage layer (the substrate already carries a `CaixaVersion`
852    /// newtype at [`crate::version::CaixaVersion`], deferred until the
853    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
854    /// a per-registry `:versao` immutability overlay the M4 CR
855    /// materializer enforces per-CR, a promotion of the plain `String`
856    /// byte-string to a richer `PinnedVersao` newtype discriminated on
857    /// SemVer-2 pre-release / build-metadata presence — would have had
858    /// to be threaded through every open-coded copy in lockstep or the
859    /// validate gate and the dozen emit paths would silently disagree
860    /// on which version a given [`Caixa`] resolves to (an author's
861    /// `:versao "0.1.0"` would satisfy validate while one of the emit
862    /// paths silently rendered a drifted other version, or vice versa).
863    /// Lifting the resolution to a typed method on the substrate
864    /// primitive means every downstream consumer of the caixa's
865    /// per-`Caixa` pinned-version surface reaches for exactly one typed
866    /// dispatch — the resolver's accept-set migrates as a unit on any
867    /// future axis addition.
868    ///
869    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
870    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
871    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
872    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
873    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
874    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
875    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
876    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
877    /// on the sibling per-typed-slot version-carrier axes, extended here
878    /// to close the second outer top-level [`Caixa`] required-`&str`-
879    /// carrying axis so the two universal-axis identity-carrying
880    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
881    /// share the same "one typed dispatch per axis" discipline. Named
882    /// `versao()` to match the storage field's name; the accessor's
883    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
884    /// slot's docstring already carries.
885    #[must_use]
886    pub fn versao(&self) -> &str {
887        &self.versao
888    }
889
890    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
891    /// closed-set-enum discriminant accessor every consumer of the top-
892    /// level manifest's kind axis keys off — returns the author-declared
893    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
894    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
895    /// (`:kind` is a required-axis discriminant every `defcaixa` form
896    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
897    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
898    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
899    /// variant).
900    ///
901    /// The `:kind` slot carries the universal-axis closed-set typed-
902    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
903    /// §I — the primary shape gate every renderer / verifier /
904    /// operator branches on; the five variants `Biblioteca` /
905    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
906    /// the caixa surface into disjoint runtime contracts) — the typed
907    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
908    /// values through the derive-macro's symbol-arm gate, exhaustively
909    /// matched at every downstream dispatch site) maps onto every
910    /// load-bearing downstream consumer the substrate carries:
911    ///
912    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
913    ///     predicate — the canonical two-line
914    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
915    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
916    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
917    ///     ComputeUnit` CR materializer) runs at its entry-point,
918    ///     alongside the [`crate::render::KindMismatch`] error carrier's
919    ///     `actual:` field the diagnostic surfaces to name the offending
920    ///     caixa's variant.
921    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
922    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
923    ///     `_view` composers that fold the flat mesh-slot / supervisor-
924    ///     slot columns into their typed sub-spec only when the kind
925    ///     matches (returns `None` otherwise); the future per-Servico
926    ///     M2-view composer (`servico_view`) will follow the same shape.
927    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
928    ///     coherence gate — the `!self.kind.requires_exe()` /
929    ///     `!self.kind.requires_servicos()` predicates that fence
930    ///     each code-surface slot from the wrong owning kind.
931    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
932    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
933    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
934    ///     coherence error carriers (`SupervisorOwnsCode` /
935    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
936    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
937    ///     / `ForeignCodeSlot`) which each name the offending caixa's
938    ///     variant in their `kind:` field.
939    ///
940    /// Prior to this lift the `.kind` field was accessed inline at
941    /// twenty-plus production sites across `caixa-core` (the
942    /// [`crate::render::require_kind`] entry-gate predicate + the
943    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
944    /// composers, the `declared_foreign_code_slots` per-slot kind-
945    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
946    /// kind ↔ code-surface predicates + four error carriers) — a score
947    /// of open-coded field-accesses that expressed no compile-time link
948    /// back to the typed slot. A future extension of the `:kind` axis
949    /// to a richer author surface — a per-`:kind` sub-variant discriminant
950    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
951    /// variant across the wasm-component / legacy-container / native-
952    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
953    /// kind-overlay the M4 CR materializer resolves per-CR (the
954    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
955    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
956    /// enum to a richer `KindWithRuntime` discriminated on the
957    /// component-model world axis — would have had to be threaded
958    /// through every open-coded copy in lockstep or the entry gate,
959    /// the view composers, and the layout invariants would silently
960    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
961    /// the resolution to a typed method on the substrate primitive
962    /// means every downstream consumer of the caixa's per-`Caixa`
963    /// kind surface reaches for exactly one typed dispatch — the
964    /// resolver's accept-set migrates as a unit on any future axis
965    /// addition.
966    ///
967    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
968    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
969    /// required-discriminant" projection pattern. Sibling in shape to
970    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
971    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
972    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
973    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
974    /// on the sibling nested-spec typed-slot discriminator axes,
975    /// extended here to the outer top-level [`Caixa`] universal-axis
976    /// surface. Named `kind()` to match the storage field's name;
977    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
978    /// vocabulary the slot's docstring already carries.
979    #[must_use]
980    pub fn kind(&self) -> CaixaKind {
981        self.kind
982    }
983
984    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
985    /// maintainer-name-list slice-accessor every consumer of the top-
986    /// level manifest's maintainer axis keys off — returns the author-
987    /// declared `:autores` list verbatim as a `&[String]` slice-view over
988    /// the same backing buffer the raw `self.autores.as_slice()` field
989    /// access borrows from. Empty-list-carrying (`:autores` is a default-
990    /// empty axis every `defcaixa` form supplies with an empty `()` when
991    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
992    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
993    /// parse definitionally carries a `Vec<String>` slot — possibly
994    /// empty — and the returned `&[String]` degenerates to an empty
995    /// slice on that arm without any silent `None` collapse).
996    ///
997    /// The `:autores` slot carries the universal-axis maintainer-name
998    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
999    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1000    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1001    /// every downstream registry-facing artifact emits under) — the
1002    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1003    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1004    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1005    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1006    /// onto every load-bearing downstream consumer the substrate carries
1007    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1008    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1009    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1010    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1011    /// name, email: None }` record, every future per-`Caixa` registry-
1012    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1013    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1014    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1015    /// the future per-cluster author-notification overlay the M4 CR
1016    /// materializer resolves per-CR).
1017    ///
1018    /// Prior to this lift the `.autores` field was accessed inline at
1019    /// two production sites — [`Self::validate_autores`]'s `for autor
1020    /// in &self.autores` walk that gates every entry through
1021    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1022    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1023    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1024    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1025    /// two open-coded field-accesses that expressed no compile-time link
1026    /// back to the typed slot. A future extension of the `:autores` axis
1027    /// to a richer author surface — a per-`:autores` structured
1028    /// `Maintainer { name, email, url }` at the storage layer once the
1029    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1030    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1031    /// enforces per-CR (the "cluster policy demands every author declare
1032    /// an on-file `mailto:` contact" arm), a promotion of the plain
1033    /// `Vec<String>` byte-string list to a richer
1034    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1035    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1036    /// predicate already resolves through — would have had to be
1037    /// threaded through both open-coded copies in lockstep or the
1038    /// validate gate and the caixa-helm emit path would silently
1039    /// disagree on which authors a given [`Caixa`] resolves to (an
1040    /// author's `:autores ("alice" "bob")` would satisfy validate while
1041    /// the caixa-helm emit path silently rendered a drifted other
1042    /// maintainer list, or vice versa). Lifting the resolution to a
1043    /// typed method on the substrate primitive means every downstream
1044    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1045    /// for exactly one typed dispatch — the resolver's accept-set
1046    /// migrates as a unit on any future axis addition.
1047    ///
1048    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1049    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1050    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1051    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1052    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1053    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1054    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1055    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1056    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1057    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1058    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1059    /// per-M3 typed-slot list axes, extended here to the outer top-level
1060    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1061    /// `&Vec<String>`) because every downstream consumer of the author
1062    /// list treats it as a read-only sequence — the slice-view is the
1063    /// narrowest borrow that supports every present + roadmapped consumer
1064    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1065    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1066    /// reaches for (the storage-side `Vec` remains reachable through the
1067    /// `pub autores` field for the mutation-carrying serde round-trip and
1068    /// per-test fixture-mutation paths). Named `autores()` to match the
1069    /// storage field's name; the accessor's identity maps onto the
1070    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1071    /// carries.
1072    #[must_use]
1073    pub fn autores(&self) -> &[String] {
1074        self.autores.as_slice()
1075    }
1076
1077    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1078    /// registry-search-tag-list slice-accessor every consumer of the
1079    /// top-level manifest's topical-tag axis keys off — returns the
1080    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1081    /// slice-view over the same backing buffer the raw
1082    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1083    /// list-carrying (`:etiquetas` is a default-empty axis every
1084    /// `defcaixa` form supplies with an empty `()` when unset; the
1085    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1086    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1087    /// definitionally carries a `Vec<String>` slot — possibly empty —
1088    /// and the returned `&[String]` degenerates to an empty slice on
1089    /// that arm without any silent `None` collapse).
1090    ///
1091    /// The `:etiquetas` slot carries the universal-axis topical-tag
1092    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1093    /// author-facing surface every `defcaixa` form supplies alongside
1094    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1095    /// search-facing axis every downstream registry-facing artifact
1096    /// emits under) — the typed slot's `Vec<String>` accept-set
1097    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1098    /// non-chart-keyword-shape rejected through
1099    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1100    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1101    /// every load-bearing downstream consumer the substrate carries —
1102    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1103    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1104    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1105    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1106    /// `Chart.yaml` `keywords:` array (chained with the
1107    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1108    /// dedup'd through a `BTreeSet` at emit time), every future per-
1109    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1110    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1111    /// annotation, the future per-cluster tag-notification overlay the
1112    /// M4 CR materializer resolves per-CR).
1113    ///
1114    /// Prior to this lift the `.etiquetas` field was accessed inline at
1115    /// two production sites — [`Self::validate_etiquetas`]'s `for
1116    /// etiqueta in &self.etiquetas` walk that gates every entry through
1117    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1118    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1119    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1120    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1121    /// two open-coded field-accesses that expressed no compile-time
1122    /// link back to the typed slot. A future extension of the
1123    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1124    /// structured `ChartKeyword { name, uri, category }` at the storage
1125    /// layer once the substrate absorbs `artifacthub.io/keywords`
1126    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1127    /// CR materializer enforces per-CR (the "cluster policy demands
1128    /// every tag come from a substrate-approved taxonomy" arm), a
1129    /// promotion of the plain `Vec<String>` byte-string list to a
1130    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1131    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1132    /// already resolves through — would have had to be threaded through
1133    /// both open-coded copies in lockstep or the validate gate and the
1134    /// caixa-helm emit path would silently disagree on which tags a
1135    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1136    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1137    /// path silently rendered a drifted other keyword list, or vice
1138    /// versa). Lifting the resolution to a typed method on the
1139    /// substrate primitive means every downstream consumer of the
1140    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1141    /// typed dispatch — the resolver's accept-set migrates as a unit
1142    /// on any future axis addition.
1143    ///
1144    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1145    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1146    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1147    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1148    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1149    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1150    /// fold onto the same pattern in future lifts. Sibling in shape to
1151    /// the peer per-`:supervisor`
1152    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1153    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1154    /// (a6e18d7), per-`:membros`
1155    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1156    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1157    /// (0dcc926), and per-`:upgrade-from :instructions`
1158    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1159    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1160    /// typed-slot list axes, extended here to the outer top-level
1161    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1162    /// `&Vec<String>`) because every downstream consumer of the tag
1163    /// list treats it as a read-only sequence — the slice-view is the
1164    /// narrowest borrow that supports every present + roadmapped
1165    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1166    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1167    /// the typed view reaches for (the storage-side `Vec` remains
1168    /// reachable through the `pub etiquetas` field for the mutation-
1169    /// carrying serde round-trip and per-test fixture-mutation paths).
1170    /// Named `etiquetas()` to match the storage field's name; the
1171    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1172    /// vocabulary the slot's docstring already carries.
1173    #[must_use]
1174    pub fn etiquetas(&self) -> &[String] {
1175        self.etiquetas.as_slice()
1176    }
1177
1178    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1179    /// library-source-path-list slice-accessor every consumer of the
1180    /// top-level manifest's Biblioteca-source axis keys off — returns
1181    /// the author-declared `:bibliotecas` list verbatim as a
1182    /// `&[String]` slice-view over the same backing buffer the raw
1183    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1184    /// list-carrying (`:bibliotecas` is a default-empty axis every
1185    /// `defcaixa` form supplies with an empty `()` when unset; the
1186    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1187    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1188    /// parse definitionally carries a `Vec<String>` slot — possibly
1189    /// empty — and the returned `&[String]` degenerates to an empty
1190    /// slice on that arm without any silent `None` collapse).
1191    ///
1192    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1193    /// entry-path list every `:kind Biblioteca` caixa emits under
1194    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1195    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1196    /// substrate-wide library-carrier axis every downstream
1197    /// authoring-facing consumer keys off) — the typed slot's
1198    /// `Vec<String>` accept-set (empty-per-entry rejected through
1199    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1200    /// non-sandboxed-relative-shape rejected through
1201    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1202    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1203    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1204    /// maps onto every load-bearing downstream consumer the substrate
1205    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1206    /// empty-check + per-entry file-exists loop at
1207    /// caixa-core/src/layout.rs that gates each entry through
1208    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1209    /// [`Self::validate_code_paths`] per-slot shape gate at
1210    /// caixa-core/src/manifest.rs that walks each entry through the
1211    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1212    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1213    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1214    /// declared library file for lexical / structural errors before
1215    /// downstream `importar` resolution, every future per-`Caixa`
1216    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1217    /// (the future `tatara-lispc` compilation entry the docstring at
1218    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1219    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1220    /// the future `caixa-lsp` per-library semantic-token stream the
1221    /// caixa-lsp docstring roadmaps).
1222    ///
1223    /// Prior to this lift the `.bibliotecas` field was accessed inline
1224    /// at three production sites — [`crate::LayoutInvariants`]'s
1225    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1226    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1227    /// declared library path through the on-disk-existence check,
1228    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1229    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1230    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1231    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1232    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1233    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1234    /// coded field-accesses that expressed no compile-time link back
1235    /// to the typed slot. A future extension of the `:bibliotecas`
1236    /// axis to a richer library surface — a per-`:bibliotecas`
1237    /// structured `BibliotecaEntry { path, edition, exports }` at the
1238    /// storage layer once the substrate absorbs the per-library
1239    /// language-edition + explicit-exports tuple the tatara-lisp
1240    /// module-system roadmap acknowledges, a per-registry
1241    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1242    /// per-CR (the "cluster policy demands every biblioteca declare
1243    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1244    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1245    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1246    /// [`crate::render::is_sandboxed_relative_path`] +
1247    /// [`crate::render::is_lisp_extension`] predicates already resolve
1248    /// through — would have had to be threaded through all three
1249    /// open-coded copies in lockstep or the layout gate, the shape
1250    /// validator, and the `feira build` phase-1 parse walk would
1251    /// silently disagree on which library paths a given [`Caixa`]
1252    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1253    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1254    /// silently parsed a drifted other list, or vice versa). Lifting
1255    /// the resolution to a typed method on the substrate primitive
1256    /// means every downstream consumer of the caixa's per-`Caixa`
1257    /// library-source surface reaches for exactly one typed dispatch
1258    /// — the resolver's accept-set migrates as a unit on any future
1259    /// axis addition.
1260    ///
1261    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1262    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1263    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1264    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1265    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1266    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1267    /// `:children` / `:membros` / `:contratos`) fold onto the same
1268    /// pattern in future lifts. Sibling in shape to the peer
1269    /// per-`:supervisor`
1270    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1271    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1272    /// (a6e18d7), per-`:membros`
1273    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1274    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1275    /// (0dcc926), and per-`:upgrade-from :instructions`
1276    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1277    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1278    /// typed-slot list axes, extended here to the outer top-level
1279    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1280    /// `&Vec<String>`) because every downstream consumer of the
1281    /// library-source list treats it as a read-only sequence — the
1282    /// slice-view is the narrowest borrow that supports every
1283    /// present + roadmapped consumer (`.iter()`, `.len()`,
1284    /// `.is_empty()`) without leaking the backing `Vec`'s
1285    /// grow/push/reserve surface no consumer of the typed view
1286    /// reaches for (the storage-side `Vec` remains reachable through
1287    /// the `pub bibliotecas` field for the mutation-carrying serde
1288    /// round-trip and per-test fixture-mutation paths). Named
1289    /// `bibliotecas()` to match the storage field's name; the
1290    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1291    /// vocabulary the slot's docstring already carries.
1292    #[must_use]
1293    pub fn bibliotecas(&self) -> &[String] {
1294        self.bibliotecas.as_slice()
1295    }
1296
1297    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1298    /// nix-built-executable-entry-path-list slice-accessor every consumer
1299    /// of the top-level manifest's Binario-executable axis keys off —
1300    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1301    /// slice-view over the same backing buffer the raw
1302    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1303    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1304    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1305    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1306    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1307    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1308    /// degenerates to an empty slice on that arm without any silent
1309    /// `None` collapse).
1310    ///
1311    /// The `:exe` slot carries the universal-axis nix-built executable
1312    /// entry-path list every `:kind Binario` caixa emits under
1313    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1314    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1315    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1316    /// downstream flake-build-facing consumer keys off) — the typed
1317    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1318    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1319    /// non-sandboxed-relative-shape rejected through
1320    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1321    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1322    /// directory paths rejected past the layout's
1323    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1324    /// onto every load-bearing downstream consumer the substrate carries
1325    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1326    /// per-entry file-exists + `exe/`-directory-fence loop at
1327    /// caixa-core/src/layout.rs that gates each entry through
1328    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1329    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1330    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1331    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1332    /// that fences code-surface slots off from the two no-code kinds,
1333    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1334    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1335    /// fences the `:exe` code surface off from every non-Binario code-
1336    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1337    /// that walks each entry through the sandbox-relative / cross-entry
1338    /// duplicate gates, every future per-`Caixa` executable-facing
1339    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1340    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1341    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1342    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1343    /// future `feira nix` per-executable Binario-target emit path).
1344    ///
1345    /// Prior to this lift the `.exe` field was accessed inline at three
1346    /// production sites — the compound-code-path `has_code =
1347    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1348    /// !caixa.servicos.is_empty()` OR-fold on the
1349    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1350    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1351    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1352    /// gate, the per-entry `for p in &caixa.exe`
1353    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1354    /// [`Self::declared_foreign_code_slots`]'s
1355    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1356    /// open-coded field-accesses that expressed no compile-time link
1357    /// back to the typed slot. A future extension of the `:exe` axis
1358    /// to a richer executable surface — a per-`:exe` structured
1359    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1360    /// layer once the substrate absorbs the per-executable
1361    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1362    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1363    /// the M4 CR materializer enforces per-CR (the "cluster policy
1364    /// demands every Binario declare an explicit `:wrapper`" arm), a
1365    /// promotion of the plain `Vec<String>` byte-string list to a
1366    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1367    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1368    /// fence already resolves through — would have had to be threaded
1369    /// through all four open-coded copies in lockstep or the layout
1370    /// gate, the shape validator, and the `feira nix` emit path would
1371    /// silently disagree on which executable paths a given [`Caixa`]
1372    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1373    /// satisfy layout while `feira nix` silently packaged a drifted
1374    /// other list, or vice versa). Lifting the resolution to a typed
1375    /// method on the substrate primitive means every downstream
1376    /// consumer of the caixa's per-`Caixa` executable-source surface
1377    /// reaches for exactly one typed dispatch — the resolver's accept-
1378    /// set migrates as a unit on any future axis addition.
1379    ///
1380    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1381    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1382    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1383    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1384    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1385    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1386    /// future lift closes onto (per the trio of code-surface list slots
1387    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1388    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1389    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1390    /// last unlifted code-surface slot). Sibling in shape to the peer
1391    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1392    /// (bc92bce), per-`:placement`
1393    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1394    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1395    /// (6c77e36), per-`:contratos`
1396    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1397    /// per-`:upgrade-from :instructions`
1398    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1399    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1400    /// typed-slot list axes, extended here to the outer top-level
1401    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1402    /// `&Vec<String>`) because every downstream consumer of the
1403    /// executable-source list treats it as a read-only sequence — the
1404    /// slice-view is the narrowest borrow that supports every
1405    /// present + roadmapped consumer (`.iter()`, `.len()`,
1406    /// `.is_empty()`) without leaking the backing `Vec`'s
1407    /// grow/push/reserve surface no consumer of the typed view
1408    /// reaches for (the storage-side `Vec` remains reachable through
1409    /// the `pub exe` field for the mutation-carrying serde
1410    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1411    /// to match the storage field's name; the accessor's identity
1412    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1413    /// docstring already carries.
1414    #[must_use]
1415    pub fn exe(&self) -> &[String] {
1416        self.exe.as_slice()
1417    }
1418
1419    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1420    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1421    /// of the top-level manifest's Servico-component axis keys off —
1422    /// returns the author-declared `:servicos` list verbatim as a
1423    /// `&[String]` slice-view over the same backing buffer the raw
1424    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1425    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1426    /// form supplies with an empty `()` when unset; the
1427    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1428    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1429    /// definitionally carries a `Vec<String>` slot — possibly empty —
1430    /// and the returned `&[String]` degenerates to an empty slice on
1431    /// that arm without any silent `None` collapse).
1432    ///
1433    /// The `:servicos` slot carries the universal-axis
1434    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1435    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1436    /// author-facing surface every `defcaixa` form supplies alongside
1437    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1438    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1439    /// Servico-facing renderer keys off) — the typed slot's
1440    /// `Vec<String>` accept-set (empty-per-entry rejected through
1441    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1442    /// non-sandboxed-relative-shape rejected through
1443    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1444    /// extension rejected through
1445    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1446    /// entry duplicate rejected through
1447    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1448    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1449    /// renderer entry-points, out-of-`servicos/`-directory paths
1450    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1451    /// `starts_with` fence) maps onto every load-bearing downstream
1452    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1453    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1454    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1455    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1456    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1457    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1458    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1459    /// that fences code-surface slots off from the two no-code kinds,
1460    /// [`Self::declared_foreign_code_slots`]'s
1461    /// `!self.servicos.is_empty()` arm on the
1462    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1463    /// `:servicos` code surface off from every non-Servico code-running
1464    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1465    /// walks each entry through the sandbox-relative / `.computeunit.
1466    /// yaml`-extension / cross-entry duplicate gates, the
1467    /// [`crate::require_single_servico`] V0 singularity gate every
1468    /// per-Servico renderer entry-point runs through
1469    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1470    /// `feira deploy` per-verb `first_servico_path` walk at
1471    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1472    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1473    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1474    /// per-Servico OCI packager, the future M4
1475    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1476    /// per-Servico OTel collector-config emit).
1477    ///
1478    /// Prior to this lift the `.servicos` field was accessed inline at
1479    /// five production sites — the compound-code-path `has_code =
1480    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1481    /// !caixa.servicos.is_empty()` OR-fold on the
1482    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1483    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1484    /// `caixa.servicos.is_empty()`
1485    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1486    /// per-entry `for p in &caixa.servicos`
1487    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1488    /// [`Self::declared_foreign_code_slots`]'s
1489    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1490    /// and the [`crate::require_single_servico`] V0 count gate's
1491    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1492    /// projection (both the accept-arm predicate and the
1493    /// diagnostic-carrying `ServicoCountMismatch { count }`
1494    /// projection) — five open-coded field-accesses across three
1495    /// crates that expressed no compile-time link back to the typed
1496    /// slot. A future extension of the `:servicos` axis to a richer
1497    /// component surface — a per-`:servicos` structured
1498    /// `ServicoEntry { path, world, capabilities }` at the storage
1499    /// layer once the substrate absorbs the per-component WIT-world +
1500    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1501    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1502    /// materializer enforces per-CR (the "cluster policy demands every
1503    /// Servico declare an explicit `:world`" arm), a promotion of the
1504    /// plain `Vec<String>` byte-string list to a richer
1505    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1506    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1507    /// `starts_with(servicos_dir)` fence and the
1508    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1509    /// already resolve through, a promotion of the V0 singleton
1510    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1511    /// component-model multi-world boundary — would have had to be
1512    /// threaded through all five open-coded copies in lockstep or the
1513    /// layout gate, the shape validator, the V0 count gate, and the
1514    /// `feira chart` / `feira deploy` entry-point walks would silently
1515    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1516    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1517    /// yaml")` would satisfy layout while `feira chart` silently
1518    /// packaged a drifted other list, or vice versa). Lifting the
1519    /// resolution to a typed method on the substrate primitive means
1520    /// every downstream consumer of the caixa's per-`Caixa`
1521    /// ComputeUnit-CR-source surface reaches for exactly one typed
1522    /// dispatch — the resolver's accept-set migrates as a unit on any
1523    /// future axis addition.
1524    ///
1525    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1526    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1527    /// projection pattern [`Self::autores`] (b5d813f) opened,
1528    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1529    /// (8a36c23) closed the universal-axis text-tag family of, and
1530    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1531    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1532    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1533    /// a substrate-canonical slice accessor, the trio of code-surface
1534    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1535    /// tuple carries is complete on the typed dispatch surface (the
1536    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1537    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1538    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1539    /// per-element accessor swap in isolation — a future companion lift
1540    /// promotes the tuple's element type to `&[String]` and threads the
1541    /// triple of typed dispatches through as a unit). Sibling in shape
1542    /// to the peer per-`:supervisor`
1543    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1544    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1545    /// (a6e18d7), per-`:membros`
1546    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1547    /// per-`:contratos`
1548    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1549    /// per-`:upgrade-from :instructions`
1550    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1551    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1552    /// typed-slot list axes, extended here to the outer top-level
1553    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1554    /// `&Vec<String>`) because every downstream consumer of the
1555    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1556    /// the slice-view is the narrowest borrow that supports every
1557    /// present + roadmapped consumer (`.iter()`, `.len()`,
1558    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1559    /// grow/push/reserve surface no consumer of the typed view reaches
1560    /// for (the storage-side `Vec` remains reachable through the
1561    /// `pub servicos` field for the mutation-carrying serde round-trip
1562    /// and per-test fixture-mutation paths, and for the
1563    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1564    /// homogeneous-element-type shape carries the raw field access
1565    /// until the trio-closure lift promotes the tuple as a unit).
1566    /// Named `servicos()` to match the storage field's name; the
1567    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1568    /// vocabulary the slot's docstring already carries.
1569    #[must_use]
1570    pub fn servicos(&self) -> &[String] {
1571        self.servicos.as_slice()
1572    }
1573
1574    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
1575    /// runtime-dependency-declaration-list slice-accessor every consumer
1576    /// of the top-level manifest's runtime-dep-graph axis keys off —
1577    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
1578    /// slice-view over the same backing buffer the raw
1579    /// `self.deps.as_slice()` field access borrows from. Empty-list-
1580    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
1581    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1582    /// derive folds an omitted `:deps` through `#[serde(default)]` to
1583    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1584    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
1585    /// degenerates to an empty slice on that arm without any silent
1586    /// `None` collapse).
1587    ///
1588    /// The `:deps` slot carries the universal-axis runtime dependency
1589    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1590    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1591    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
1592    /// every downstream resolver-facing artifact emits under) — the
1593    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
1594    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
1595    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
1596    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
1597    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
1598    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
1599    /// maps onto every load-bearing downstream consumer the substrate
1600    /// carries — the [`Self::validate_deps`] per-entry
1601    /// [`Dep::validate`] + within-list dedup walk at
1602    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
1603    /// cross-list self-reference gate at caixa-core/src/layout.rs that
1604    /// checks each entry against the caixa's own `:nome`, the
1605    /// caixa-resolver `for dep in &root.deps` closure walk at
1606    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
1607    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
1608    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
1609    /// caixa-crd/src/conversion.rs that materializes each entry into the
1610    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
1611    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1612    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
1613    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
1614    /// closure emit walk the caixa-resolver docstring roadmaps).
1615    ///
1616    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1617    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1618    /// sibling `:deps-dev` future lift closes on. Peer of the closed
1619    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
1620    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
1621    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
1622    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
1623    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
1624    /// pattern onto a novel element-type axis (`Dep` composite vs the
1625    /// prior sibling family's `String` scalar). Sibling in shape to the
1626    /// peer per-`:supervisor`
1627    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1628    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1629    /// (a6e18d7), per-`:membros`
1630    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1631    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1632    /// (0dcc926), and per-`:upgrade-from :instructions`
1633    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1634    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1635    /// typed-slot list axes, extended here to the outer top-level
1636    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
1637    /// (not `&Vec<Dep>`) because every downstream consumer of the
1638    /// runtime-dep list treats it as a read-only sequence — the slice-
1639    /// view is the narrowest borrow that supports every present +
1640    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1641    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1642    /// of the typed view reaches for (the storage-side `Vec` remains
1643    /// reachable through the `pub deps` field for the mutation-carrying
1644    /// serde round-trip and per-test fixture-mutation paths). Named
1645    /// `deps()` to match the storage field's name; the accessor's
1646    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1647    /// slot's docstring already carries.
1648    #[must_use]
1649    pub fn deps(&self) -> &[Dep] {
1650        self.deps.as_slice()
1651    }
1652
1653    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
1654    /// development-only-dependency-declaration-list slice-accessor every
1655    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
1656    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
1657    /// slice-view over the same backing buffer the raw
1658    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
1659    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
1660    /// form supplies with an empty `()` when unset; the
1661    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
1662    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1663    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
1664    /// the returned `&[Dep]` degenerates to an empty slice on that arm
1665    /// without any silent `None` collapse).
1666    ///
1667    /// The `:deps-dev` slot carries the universal-axis dev-only
1668    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
1669    /// the author-facing sibling of `:deps` that every `defcaixa` form
1670    /// supplies to declare tests / lint / bench closures the runtime
1671    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
1672    /// axis every downstream test-facing artifact emits under, matching
1673    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
1674    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
1675    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
1676    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
1677    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
1678    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
1679    /// within-list duplicate `:nome` rejected through
1680    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
1681    /// load-bearing downstream consumer the substrate carries — the
1682    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
1683    /// dedup walk at caixa-core/src/manifest.rs, the
1684    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
1685    /// gate at caixa-core/src/layout.rs that checks each entry against
1686    /// the caixa's own `:nome`, the caixa-resolver
1687    /// `for dep in &root.deps_dev` closure walk at
1688    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
1689    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
1690    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
1691    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
1692    /// overlay the M4 CR materializer resolves per-CR, the future
1693    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
1694    /// roadmaps).
1695    ///
1696    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1697    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1698    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
1699    /// jointly close the two-list dep-graph surface every downstream
1700    /// resolver-facing consumer keys off (runtime `:deps` +
1701    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
1702    /// pair the [`Self::validate_deps`] gate already walks in canonical
1703    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
1704    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
1705    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
1706    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
1707    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
1708    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
1709    /// dev-dep composite-element axis (`Dep` composite, matching the
1710    /// [`Self::deps`] element type). Sibling in shape to the peer
1711    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1712    /// (bc92bce), per-`:placement`
1713    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1714    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1715    /// (6c77e36), per-`:contratos`
1716    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1717    /// per-`:upgrade-from :instructions`
1718    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1719    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1720    /// typed-slot list axes, folded here to the outer top-level
1721    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
1722    /// (not `&Vec<Dep>`) because every downstream consumer of the
1723    /// dev-dep list treats it as a read-only sequence — the slice-view
1724    /// is the narrowest borrow that supports every present +
1725    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1726    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1727    /// of the typed view reaches for (the storage-side `Vec` remains
1728    /// reachable through the `pub deps_dev` field for the mutation-
1729    /// carrying serde round-trip and per-test fixture-mutation paths).
1730    /// Named `deps_dev()` to match the storage field's `snake_case` name;
1731    /// the kebab-case author-surface tag `:deps-dev` is the same axis
1732    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
1733    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1734    /// docstring already carries.
1735    #[must_use]
1736    pub fn deps_dev(&self) -> &[Dep] {
1737        self.deps_dev.as_slice()
1738    }
1739
1740    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
1741    /// every consumer that walks one of the two dep-list axes keyed on a
1742    /// [`crate::dep::DepList`] discriminant reaches for — routes the
1743    /// `(list: DepList) -> &[Dep]` projection through one typed method on
1744    /// the substrate primitive rather than the prior open-coded
1745    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
1746    /// inline dispatch every per-axis walker would otherwise carry.
1747    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
1748    /// `&[Dep]` slice-view over the same backing buffer the sibling
1749    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
1750    /// accessors borrow from, preserving the empty-list-carrying invariant
1751    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
1752    /// are default-empty axes every `defcaixa` form supplies with an empty
1753    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
1754    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
1755    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
1756    /// returned `&[Dep]` degenerates to an empty slice on either arm
1757    /// without any silent `None` collapse).
1758    ///
1759    /// The [`crate::dep::DepList`] closed-set typed enum is the
1760    /// substrate's canonical discriminator for the "runtime-closure
1761    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
1762    /// consumer dispatches on — the compiler-checked exhaustiveness on
1763    /// the enum's `match` arms is the build-time guarantee that no future
1764    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
1765    /// that a future third dep-list axis (a `:deps-build` build-only
1766    /// closure once the substrate grows cross-artifact heterogeneous
1767    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
1768    /// consumer. Prior to this the read side carried two per-slot
1769    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
1770    /// typed dispatch that a per-axis walker could parametrise on, so
1771    /// every per-list walker (the [`Self::validate_deps`] per-list
1772    /// [`crate::render::insert_first_seen`] dedup walk, a future
1773    /// `feira app graph` per-list dep summary, a future M4 per-cluster
1774    /// dev-closure-audit overlay the CR materializer resolves per-CR)
1775    /// open-coded the same two-block "run over `:deps`, then run over
1776    /// `:deps-dev`" pattern — a silent duplication that a future third
1777    /// dep-list axis would have had to grow a third block at every site.
1778    ///
1779    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
1780    /// (359fba5) — closes the two-side dispatch symmetry on the outer
1781    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
1782    /// side, `deps_of` on the read side, both keyed on the same
1783    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
1784    /// the substrate primitive, thin projections at each consumer"
1785    /// discipline the sibling per-slot read accessors ([`Self::nome`]
1786    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
1787    /// the outer-[`Caixa`] typed-dispatch read surface.
1788    #[must_use]
1789    pub fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
1790        match list {
1791            crate::dep::DepList::Prod => self.deps(),
1792            crate::dep::DepList::Dev => self.deps_dev(),
1793        }
1794    }
1795
1796    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
1797    /// consumer that appends to one of the two dep-list axes keys off
1798    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
1799    /// method on the substrate primitive rather than the prior
1800    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
1801    /// else { &mut caixa.deps }` inline dispatch + open-coded
1802    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
1803    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
1804    /// a within-list name collision — the same `list: &'static str`
1805    /// diagnostic shape [`Self::validate_deps`]'s per-list
1806    /// [`crate::render::insert_first_seen`] walk raises on the peer
1807    /// parse-time within-list dedup axis, so a future author reading a
1808    /// `feira add` refusal and a `feira build` refusal reaches for the
1809    /// same corrective surface without switching diagnostic idioms.
1810    ///
1811    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
1812    /// closed-set typed carrier for the "runtime-closure `:deps` vs
1813    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
1814    /// dispatches on — the compiler-checked exhaustiveness on the
1815    /// enum's `match` arms is the build-time guarantee that no future
1816    /// per-list mutation-site regresses to a bare-`bool`-flag
1817    /// (`is_dev: bool`) inline dispatch that a future third
1818    /// dep-list axis (a `:deps-build` build-only closure once the
1819    /// substrate grows cross-artifact heterogeneous dep-graphs, per
1820    /// CAIXA-SDLC §I) would silently split at every consumer.
1821    ///
1822    /// Same "one typed dispatch on the substrate primitive, thin
1823    /// projections at each consumer" discipline the sibling per-slot
1824    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
1825    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
1826    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
1827    /// the substrate's first typed-mutation dispatch on the top-level
1828    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
1829    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
1830    /// diagnostic path routed no through-line back to the typed slot,
1831    /// so a future extension of either dep-list axis to a richer author
1832    /// surface (a per-cluster override the operator pins through a
1833    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
1834    /// roadmap acknowledges, an M4
1835    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
1836    /// admission-webhook that normalized the list at admission time)
1837    /// would have had to be threaded through the `feira add` mutation
1838    /// site in lockstep with every read consumer or one path would
1839    /// silently disagree with the other on which list a given dep lands
1840    /// in. Lifting the resolution rule to a typed method on the
1841    /// substrate primitive means every downstream dep-list-mutating
1842    /// consumer of the top-level manifest reaches for exactly one typed
1843    /// dispatch — the resolver's accept-set migrates as a unit on any
1844    /// future axis addition.
1845    ///
1846    /// # Errors
1847    ///
1848    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
1849    /// when another entry in the same list already carries the same
1850    /// `:nome` — the mutation is refused and the caller can surface the
1851    /// typed diagnostic to the author (the `feira add` verb routes the
1852    /// error through `anyhow::Error::from`, which preserves the
1853    /// canonical `#[error(...)]`-templated diagnostic body).
1854    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
1855        let target = match list {
1856            crate::dep::DepList::Prod => &mut self.deps,
1857            crate::dep::DepList::Dev => &mut self.deps_dev,
1858        };
1859        if target.iter().any(|d| d.nome() == dep.nome()) {
1860            return Err(DepError::DuplicateNome {
1861                nome: dep.nome().to_string(),
1862                list: list.as_str(),
1863            });
1864        }
1865        target.push(dep);
1866        Ok(())
1867    }
1868
1869    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
1870    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
1871    /// composite-reference accessor every consumer of the top-level
1872    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
1873    /// off — returns the author-declared `:limits` typed composite
1874    /// verbatim as an `Option<&LimitsSpec>` reference over the same
1875    /// backing storage the raw `self.limits.as_ref()` field access
1876    /// borrows from, with `None` naming the "no `:limits` block
1877    /// authored — every per-axis Lunatic-sandbox cap defers to the
1878    /// wasm-engine-default arm named on the per-axis
1879    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
1880    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
1881    /// docstrings" partition every downstream Servico-M2-overlay
1882    /// emitter treats as "emit nothing" and the sibling
1883    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
1884    /// treats as "skip the per-axis
1885    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
1886    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
1887    ///
1888    /// The outer `:limits` slot carries the M2 Servico-runtime typed
1889    /// composite — the load-bearing container of every Lunatic-shaped
1890    /// per-process wasm32-sandbox cap axis every long-running wasm
1891    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
1892    /// Lunatic per-process linear-memory / fuel / wall-clock /
1893    /// millicore cap primitives translated onto pleme-io's typed
1894    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
1895    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
1896    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
1897    /// chart both fan on). Every per-`:limits` axis threads through a
1898    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
1899    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
1900    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
1901    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
1902    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
1903    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
1904    /// consumer that reaches for a limits axis first passes through
1905    /// this outer accessor onto the composite and then dispatches
1906    /// onto the per-axis accessor — the two-level dispatch means
1907    /// every per-`:limits` reader now routes through a typed dispatch
1908    /// on the substrate primitive at both altitudes.
1909    ///
1910    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
1911    /// was accessed inline at three production sites — the
1912    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
1913    /// `if let Some(l) = &caixa.limits { … }` traversal head
1914    /// (caixa-core/src/layout.rs:882, which drives the per-axis
1915    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
1916    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
1917    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
1918    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
1919    /// [`LimitsSpec::validate`] fans onto), the
1920    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
1921    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
1922    /// head (caixa-core/src/render.rs:18504, which drives the
1923    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
1924    /// projection every `caixa-helm` / `caixa-flux` Servico values-
1925    /// block emitter fans on), and the
1926    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
1927    /// set enumerator's `self.limits.is_some()` presence probe
1928    /// (caixa-core/src/manifest.rs:1788, which drives the
1929    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
1930    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
1931    /// gate reads) — three open-coded outer-field accesses that
1932    /// expressed no compile-time link back to the typed slot at the
1933    /// [`Caixa`] altitude. A future extension of the `:limits` outer
1934    /// axis to a richer author surface (a multi-`:limits` list the M4
1935    /// CR materializer resolves per-CR at admission time so a Servico
1936    /// can expose a compute-heavy + IO-heavy limits pair, a per-
1937    /// cluster `:limits-overrides` slot the operator pins so a
1938    /// cluster-specific policy can tighten a caixa-declared cap
1939    /// without re-authoring the `caixa.lisp`, a promotion of the
1940    /// plain `Option<LimitsSpec>` to a richer
1941    /// `{static, dynamic}` partition once the wasm-engine's runtime-
1942    /// resolved dynamic-cap surface lands) would have had to be
1943    /// threaded through all three open-coded copies in lockstep or
1944    /// one consumer would silently disagree with the peers on which
1945    /// limits composite a given Caixa resolves to — the layout gate's
1946    /// per-axis bracket-dispatch seed reading the raw slot while the
1947    /// peer `servico_m2_overlay` emitter read an operator-resolved
1948    /// slot would silently split the build-time sandbox-shape gate
1949    /// from the runtime `ComputeUnit` CR emission gate, a three-
1950    /// consumer split at the layout gate, the M2 overlay emitter, and
1951    /// the declared-slot enumerator far from the source `caixa.lisp`
1952    /// with no field naming the limits-drift root cause. Lifting the
1953    /// resolution rule to a typed method on the substrate primitive
1954    /// means every downstream consumer of the caixa's per-`Caixa`
1955    /// Lunatic-sandboxing outer-composite surface reaches for exactly
1956    /// one typed dispatch — the resolver's accept-set migrates as a
1957    /// unit on any future axis addition.
1958    ///
1959    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
1960    /// composite-reference accessor — opens the outer-`Caixa`
1961    /// `Option<&Composite>` composite-reference projection pattern the
1962    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
1963    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
1964    /// [`crate::aplicacao::Placement`] / `:entrada`
1965    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
1966    /// fold on. Peer of the M3 mesh-slot outer-composite family the
1967    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
1968    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
1969    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
1970    /// accessors already close on the outer [`crate::AplicacaoSpec`]
1971    /// altitude — extends that "one typed dispatch on the substrate
1972    /// primitive, thin projections at each consumer" discipline onto
1973    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
1974    /// runtime slot family's outer-composite axis. Returns
1975    /// `Option<&LimitsSpec>` (not the owning composite by copy or
1976    /// clone) because every downstream consumer of the limits
1977    /// composite treats it as a read-only per-axis dispatch source —
1978    /// the reference-view is the narrowest borrow that supports every
1979    /// present + roadmapped consumer (per-axis accessor dispatch,
1980    /// `.is_empty()`-gated overlay projection, presence-probe early
1981    /// return on the "author-omitted `:limits` ⇒ engine-default
1982    /// applies" partition) without cloning the composite through
1983    /// every consumer's fast path. The `Option` half of the return-
1984    /// type preserves the load-bearing "author-omitted `:limits` ⇒
1985    /// engine-default applies" partition (not a default composite the
1986    /// downstream must reject on emptiness) — the accessor projects
1987    /// the raw `Option<LimitsSpec>` slot's presence bit through the
1988    /// reference-return unchanged. Named `limits()` to match the
1989    /// storage field's name verbatim and the tatara-lisp author-
1990    /// surface term (`:limits`) the field's own docstring already
1991    /// carries.
1992    #[must_use]
1993    pub fn limits(&self) -> Option<&LimitsSpec> {
1994        self.limits.as_ref()
1995    }
1996
1997    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
1998    /// composite OTP-`gen_server`-shaped callback-table optional-
1999    /// composite-reference accessor every consumer of the top-level
2000    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2001    /// keys off — returns the author-declared `:behavior` typed
2002    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2003    /// the same backing storage the raw `self.behavior.as_ref()` field
2004    /// access borrows from, with `None` naming the "no `:behavior`
2005    /// block authored — every per-callback OTP-shaped hook defers to
2006    /// the wasm-engine's runtime default arm named on the per-axis
2007    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2008    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2009    /// [`BehaviorSpec::on_state_change`] /
2010    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2011    /// partition every downstream Servico-M2-overlay emitter treats as
2012    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2013    /// per-`:behavior` shape gate treats as "skip the per-arm
2014    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2015    /// per-callback on-disk `MissingEntry` existence check".
2016    ///
2017    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2018    /// composite — the load-bearing container of every OTP-shaped
2019    /// per-Servico lifecycle-callback path axis every long-running wasm
2020    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2021    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2022    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2023    /// translated onto pleme-io's typed `:behavior :on-init` /
2024    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2025    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2026    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2027    /// chart both fan on). Every per-`:behavior` axis threads through a
2028    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2029    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2030    /// Every downstream consumer that reaches for a behavior axis
2031    /// first passes through this outer accessor onto the composite
2032    /// and then dispatches onto the per-callback accessor — the
2033    /// two-level dispatch means every per-`:behavior` reader now
2034    /// routes through a typed dispatch on the substrate primitive at
2035    /// both altitudes.
2036    ///
2037    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2038    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2039    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2040    /// keys the "per-version `:state-change` instruction must have a
2041    /// `:on-state-change` callback" precondition off this accessor's
2042    /// composite (the callback-side counterpart to the
2043    /// `:upgrade-from :instructions :state-change :script` refusal at
2044    /// the appup-side). Threading that gate's traversal input through
2045    /// this accessor closes the cross-slot invariant on the substrate
2046    /// primitive, not on the raw field.
2047    ///
2048    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2049    /// composite was accessed inline at four production sites — the
2050    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2051    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2052    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2053    /// `BehaviorError` refusal cascade + the per-callback on-disk
2054    /// [`crate::LayoutError::MissingEntry`] existence check under
2055    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2056    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2057    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2058    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2059    /// drives the `:state-change` ↔ `:on-state-change` precondition
2060    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2061    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2062    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2063    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2064    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2065    /// Servico values-block emitter fans on), and the
2066    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2067    /// set enumerator's `self.behavior.is_some()` presence probe
2068    /// (caixa-core/src/manifest.rs:1919, which drives the
2069    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2070    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2071    /// gate reads) — four open-coded outer-field accesses that
2072    /// expressed no compile-time link back to the typed slot at the
2073    /// [`Caixa`] altitude. A future extension of the `:behavior`
2074    /// outer axis to a richer author surface (a per-callback overlay
2075    /// resolver the operator materializes at admission time so a
2076    /// cluster-specific policy can inject a per-callback tracing
2077    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2078    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2079    /// dynamic}` partition once a runtime-resolved behavior-swap
2080    /// surface lands, the M4 per-callback middleware chain the
2081    /// caixa-operator's per-Servico admission webhook keys off) would
2082    /// have had to be threaded through all four open-coded copies in
2083    /// lockstep or one consumer would silently disagree with the
2084    /// peers on which behavior composite a given Caixa resolves to —
2085    /// the layout gate's per-callback existence-check seed reading
2086    /// the raw slot while the peer `servico_m2_overlay` emitter read
2087    /// an operator-resolved slot would silently split the build-time
2088    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2089    /// gate from the cross-slot `:state-change` composition gate from
2090    /// the M2 declared-slot enumerator, a four-consumer split far
2091    /// from the source `caixa.lisp` with no field naming the
2092    /// behavior-drift root cause. Lifting the resolution rule to a
2093    /// typed method on the substrate primitive means every downstream
2094    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2095    /// composite surface reaches for exactly one typed dispatch — the
2096    /// resolver's accept-set migrates as a unit on any future axis
2097    /// addition.
2098    ///
2099    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2100    /// composite-reference accessor — sibling to the opening
2101    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2102    /// `Option<&Composite>` composite-reference sub-family, extends
2103    /// the "one typed dispatch on the substrate primitive, thin
2104    /// projections at each consumer" discipline onto the second of
2105    /// the three M2 Servico-runtime slots. The remaining
2106    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2107    /// altitude — the M3 mesh-slot family (`:politicas`,
2108    /// `:placement`, `:entrada` — already closed on the inner
2109    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2110    /// d32111c) — remain the future sibling lifts on the outer
2111    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2112    /// the owning composite by copy or clone) because every
2113    /// downstream consumer of the behavior composite treats it as a
2114    /// read-only per-callback dispatch source — the reference-view is
2115    /// the narrowest borrow that supports every present + roadmapped
2116    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2117    /// overlay projection, presence-probe early return on the
2118    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2119    /// partition, cross-slot `:state-change` composition input)
2120    /// without cloning the composite through every consumer's fast
2121    /// path. The `Option` half of the return-type preserves the
2122    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2123    /// applies" partition (not a default composite the downstream
2124    /// must reject on emptiness) — the accessor projects the raw
2125    /// `Option<BehaviorSpec>` slot's presence bit through the
2126    /// reference-return unchanged. Named `behavior()` to match the
2127    /// storage field's name verbatim and the tatara-lisp author-
2128    /// surface term (`:behavior`) the field's own docstring already
2129    /// carries.
2130    #[must_use]
2131    pub fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2132        self.behavior.as_ref()
2133    }
2134
2135    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2136    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2137    /// reference accessor every consumer of the top-level manifest's
2138    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2139    /// reader keys off — returns the author-declared `:politicas` typed
2140    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2141    /// same backing storage the raw `self.politicas.as_ref()` field
2142    /// access borrows from, with `None` naming the "no `:politicas`
2143    /// block authored — every per-axis mesh-policy scalar defers to the
2144    /// cluster-default arm named on the per-axis
2145    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2146    /// [`crate::aplicacao::MeshPolicy::retries`] /
2147    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2148    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2149    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2150    /// docstrings" partition every downstream caixa-mesh /
2151    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2152    /// "emit no per-`:politicas` overlay" and the sibling
2153    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2154    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2155    /// arm.
2156    ///
2157    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2158    /// Aplicacao typed composite — the load-bearing container of every
2159    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2160    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2161    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2162    /// composite; §V — the "no infinite blocking" per-call deadline +
2163    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2164    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2165    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2166    /// threads through a lifted per-slot accessor on the
2167    /// [`crate::aplicacao::MeshPolicy`] type: the
2168    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2169    /// mTLS-enforcement toggle, the
2170    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2171    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2172    /// (7073d0f) Gateway-API per-call deadline, the
2173    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2174    /// Envoy-outlier-detection composite. Every downstream consumer
2175    /// that reaches for a mesh-policy axis first passes through this
2176    /// outer accessor onto the composite and then dispatches onto the
2177    /// per-axis accessor — the two-level dispatch means every per-
2178    /// `:politicas` reader now routes through a typed dispatch on the
2179    /// substrate primitive at both altitudes.
2180    ///
2181    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2182    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2183    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2184    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2185    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2186    /// composite whether or not the author declared the outer slot.
2187    /// The outer accessor preserves the "author-omitted vs authored-
2188    /// empty" partition the inner accessor's `is_empty()`-gated
2189    /// renderer overlay collapses — routing the presence bit through
2190    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2191    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2192    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2193    ///
2194    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2195    /// composite was accessed inline at two production sites — the
2196    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2197    /// `self.politicas.clone().unwrap_or_default()` traversal head
2198    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2199    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2200    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2201    /// then observes), and the [`Self::declared_mesh_slots`] M3
2202    /// declared-slot-set enumerator's `self.politicas.is_some()`
2203    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2204    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2205    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2206    /// coherence gate reads) — two open-coded outer-field accesses
2207    /// that expressed no compile-time link back to the typed slot at
2208    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2209    /// outer axis to a richer author surface (a per-cluster
2210    /// `:politicas-overrides` slot the operator materializes at
2211    /// admission time so a cluster-specific policy can tighten the
2212    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2213    /// promotion of the plain `Option<MeshPolicy>` to a richer
2214    /// `{static, dynamic}` partition once the M4 per-edge
2215    /// contrato-scoped policy-override surface lands, the M5 traffic-
2216    /// shaping composition the caixa-operator's per-Aplicacao mesh
2217    /// admission webhook keys off) would have had to be threaded
2218    /// through both open-coded copies in lockstep or the Aplicacao-
2219    /// composition seed's default-fold arm would silently disagree
2220    /// with the M3 declared-slot enumerator on which policy composite
2221    /// a given Caixa resolves to — the seed reading an operator-
2222    /// resolved slot while the enumerator's presence probe read the
2223    /// raw slot would silently split the build-time mesh-artifact
2224    /// emission gate from the M3 declared-slot enumerator's kind-
2225    /// coherence gate, a two-consumer split far from the source
2226    /// `caixa.lisp` with no field naming the policy-drift root cause.
2227    /// Lifting the resolution rule to a typed method on the substrate
2228    /// primitive means every downstream consumer of the caixa's per-
2229    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2230    /// reaches for exactly one typed dispatch — the resolver's
2231    /// accept-set migrates as a unit on any future axis addition.
2232    ///
2233    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2234    /// composite-reference accessor — sibling to the opening
2235    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2236    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2237    /// reference sub-family, extends the "one typed dispatch on the
2238    /// substrate primitive, thin projections at each consumer"
2239    /// discipline onto the first of the three M3 mesh-slot axes.
2240    /// Peer of the closed inner mesh-slot outer-composite family the
2241    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2242    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2243    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2244    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2245    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2246    /// mesh-slot arm of the composite-reference family the remaining
2247    /// two axes (`:placement`, `:entrada`) fold onto in future
2248    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2249    /// composite by copy or clone) because every downstream consumer
2250    /// of the mesh-policy composite treats it as a read-only per-axis
2251    /// dispatch source — the reference-view is the narrowest borrow
2252    /// that supports every present + roadmapped consumer (per-axis
2253    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2254    /// presence-probe early return on the "author-omitted `:politicas`
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 `:politicas` ⇒
2259    /// cluster-default applies" partition (not a default composite
2260    /// the downstream must reject on emptiness) — the accessor
2261    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2262    /// through the reference-return unchanged. Named `politicas()` to
2263    /// match the storage field's name verbatim and the tatara-lisp
2264    /// author-surface term (`:politicas`) the field's own docstring
2265    /// already carries.
2266    #[must_use]
2267    pub fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2268        self.politicas.as_ref()
2269    }
2270
2271    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2272    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2273    /// reference accessor every consumer of the top-level manifest's
2274    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2275    /// reader keys off — returns the author-declared `:placement` typed
2276    /// composite verbatim as an `Option<&Placement>` reference over the
2277    /// same backing storage the raw `self.placement.as_ref()` field
2278    /// access borrows from, with `None` naming the "no `:placement`
2279    /// block authored — every per-axis placement scalar defers to the
2280    /// cluster-default arm named on the per-axis
2281    /// [`crate::aplicacao::Placement::estrategia`] /
2282    /// [`crate::aplicacao::Placement::clusters`] /
2283    /// [`crate::aplicacao::Placement::affinity`] /
2284    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2285    /// docstrings" partition every downstream caixa-mesh /
2286    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2287    /// "emit no per-`:placement` overlay" and the sibling
2288    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2289    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2290    ///
2291    /// The outer `:placement` slot carries the M3 mesh-slot per-
2292    /// Aplicacao typed distribution composite — the load-bearing
2293    /// container of every where-does-this-Aplicacao-run axis every
2294    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2295    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2296    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2297    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2298    /// Aplicacao's typed distribution composite; §V CSE invariants —
2299    /// "distribution is a first-class typed composite, not a runtime
2300    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2301    /// typed inter-Servico contrato-edge overlay the per-cluster
2302    /// mesh renderer keys off). Every per-`:placement` axis threads
2303    /// through a lifted per-slot accessor on the
2304    /// [`crate::aplicacao::Placement`] type: the
2305    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2306    /// MESH-COMPOSITION distribution-strategy scalar, the
2307    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2308    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2309    /// M3-Adaptive-compression-hint optional-scalar, and the
2310    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2311    /// sharding extractor-expression optional-scalar. Every downstream
2312    /// consumer that reaches for a placement axis first passes through
2313    /// this outer accessor onto the composite and then dispatches onto
2314    /// the per-axis accessor — the two-level dispatch means every per-
2315    /// `:placement` reader now routes through a typed dispatch on the
2316    /// substrate primitive at both altitudes.
2317    ///
2318    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2319    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2320    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2321    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2322    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2323    /// whether or not the author declared the outer slot. The outer
2324    /// accessor preserves the "author-omitted vs authored-empty" partition
2325    /// the inner accessor collapses at the cluster-default fold —
2326    /// routing the presence bit through this accessor keeps the
2327    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2328    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2329    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2330    /// dispatch.
2331    ///
2332    /// Prior to this lift the `.placement` `Option<Placement>`
2333    /// composite was accessed inline at two production sites — the
2334    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2335    /// `self.placement.clone().unwrap_or_default()` traversal head
2336    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2337    /// the [`crate::aplicacao::Placement::default`] cluster-default
2338    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2339    /// then observes), and the [`Self::declared_mesh_slots`] M3
2340    /// declared-slot-set enumerator's `self.placement.is_some()`
2341    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2342    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2343    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2344    /// coherence gate reads) — two open-coded outer-field accesses
2345    /// that expressed no compile-time link back to the typed slot at
2346    /// the [`Caixa`] altitude. A future extension of the `:placement`
2347    /// outer axis to a richer author surface (a per-cluster
2348    /// `:placement-overrides` slot the operator materializes at
2349    /// admission time so a cluster-specific placement can tighten the
2350    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2351    /// per-tenant placement-alias table the M4
2352    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2353    /// per-CR at admission time, a promotion of the plain
2354    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2355    /// once Orleans-style virtual-actor dynamic placement comes into
2356    /// typed scope) would have had to be threaded through both open-
2357    /// coded copies in lockstep or the Aplicacao-composition seed's
2358    /// default-fold arm would silently disagree with the M3 declared-
2359    /// slot enumerator on which distribution composite a given Caixa
2360    /// resolves to — the seed reading an operator-resolved slot while
2361    /// the enumerator's presence probe read the raw slot would
2362    /// silently split the build-time distribution-artifact emission
2363    /// gate from the M3 declared-slot enumerator's kind-coherence
2364    /// gate, a two-consumer split far from the source `caixa.lisp`
2365    /// with no field naming the distribution-drift root cause.
2366    /// Lifting the resolution rule to a typed method on the substrate
2367    /// primitive means every downstream consumer of the caixa's per-
2368    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2369    /// reaches for exactly one typed dispatch — the resolver's
2370    /// accept-set migrates as a unit on any future axis addition.
2371    ///
2372    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2373    /// composite-reference accessor — sibling to the opening
2374    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2375    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2376    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2377    /// composite-reference sub-family, folds on the "one typed
2378    /// dispatch on the substrate primitive, thin projections at each
2379    /// consumer" discipline extended onto the second of the three M3
2380    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2381    /// composite family the sibling
2382    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2383    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2384    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2385    /// accessor pins already close on the inner
2386    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2387    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2388    /// [`Self::politicas`] opened, extending the discipline onto the
2389    /// second of the three M3 mesh-slot axes. The remaining M3
2390    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2391    /// discipline in the final sibling lift, closing the outer top-
2392    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2393    /// Returns `Option<&Placement>` (not the owning composite by copy
2394    /// or clone) because every downstream consumer of the placement
2395    /// composite treats it as a read-only per-axis dispatch source —
2396    /// the reference-view is the narrowest borrow that supports every
2397    /// present + roadmapped consumer (per-axis accessor dispatch,
2398    /// serde composite-serialization on the programs.yaml overlay,
2399    /// presence-probe early return on the "author-omitted `:placement`
2400    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2401    /// seed's default-fold arm) without cloning the composite through
2402    /// every consumer's fast path. The `Option` half of the return-
2403    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2404    /// cluster-default applies" partition (not a default composite
2405    /// the downstream must reject on emptiness) — the accessor
2406    /// projects the raw `Option<Placement>` slot's presence bit
2407    /// through the reference-return unchanged. Named `placement()` to
2408    /// match the storage field's name verbatim and the tatara-lisp
2409    /// author-surface term (`:placement`) the field's own docstring
2410    /// already carries.
2411    #[must_use]
2412    pub fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2413        self.placement.as_ref()
2414    }
2415
2416    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2417    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2418    /// composite-reference accessor every consumer of the top-level
2419    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2420    /// composite reader keys off — returns the author-declared
2421    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2422    /// reference over the same backing storage the raw
2423    /// `self.entrada.as_ref()` field access borrows from, with `None`
2424    /// naming the "no `:entrada` block authored — this Aplicacao is
2425    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2426    /// partition every downstream caixa-mesh Gateway-API artifact
2427    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2428    /// backend for this Aplicacao" and the sibling
2429    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2430    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2431    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2432    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2433    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2434    /// the same `Option<&Entrada>` presence bit unchanged).
2435    ///
2436    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2437    /// Aplicacao typed external-gateway composite — the load-bearing
2438    /// container of every how-does-the-outside-world-reach-this-
2439    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2440    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2441    /// external-entry composite; §V CSE invariants — "the external
2442    /// gateway is a first-class typed composite, not a per-Servico
2443    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2444    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2445    /// API renderer keys off). Every per-`:entrada` axis threads
2446    /// through a lifted per-slot accessor on the
2447    /// [`crate::aplicacao::Entrada`] type: the
2448    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2449    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2450    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2451    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2452    /// backend `trigger.service.port` scalar, and the
2453    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2454    /// resolver every HTTPRoute-aware renderer consumes. Every
2455    /// downstream consumer that reaches for an entry axis first passes
2456    /// through this outer accessor onto the composite and then
2457    /// dispatches onto the per-axis accessor — the two-level dispatch
2458    /// means every per-`:entrada` reader now routes through a typed
2459    /// dispatch on the substrate primitive at both altitudes.
2460    ///
2461    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2462    /// seed: the Aplicacao-view builder forwards the outer `Option`
2463    /// arm verbatim (no default fold — `:entrada` is inherently
2464    /// optional; a cluster-internal Aplicacao has no external gateway
2465    /// at all, not "an external gateway that defaults to nothing"), so
2466    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2467    /// `Option<&Entrada>`-return accessor observes the same presence
2468    /// bit whether or not the author declared the outer slot. Routing
2469    /// the presence bit through this accessor keeps the
2470    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2471    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2472    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2473    /// hostname/backend/path emission dispatch.
2474    ///
2475    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2476    /// was accessed inline at two production sites — the
2477    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2478    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2479    /// which drives the forward onto the peer inner
2480    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2481    /// Gateway-API fan-out then observes), and the
2482    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2483    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2484    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2485    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2486    /// kind-coherence gate reads) — two open-coded outer-field
2487    /// accesses that expressed no compile-time link back to the typed
2488    /// slot at the [`Caixa`] altitude. A future extension of the
2489    /// `:entrada` outer axis to a richer author surface (a per-cluster
2490    /// `:entrada-overrides` slot the operator materializes at admission
2491    /// time so a cluster-specific hostname can pin the caixa-declared
2492    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2493    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2494    /// CR materializer resolves per-CR at admission time, a promotion
2495    /// of the plain `Option<Entrada>` to a richer
2496    /// `{public, private, internal}` partition once Cilium-identity-
2497    /// scoped internal gateways come into typed scope) would have had
2498    /// to be threaded through both open-coded copies in lockstep or the
2499    /// Aplicacao-composition seed's forward arm would silently
2500    /// disagree with the M3 declared-slot enumerator on which external-
2501    /// gateway composite a given Caixa resolves to — the seed reading
2502    /// an operator-resolved slot while the enumerator's presence probe
2503    /// read the raw slot would silently split the build-time gateway-
2504    /// artifact emission gate from the M3 declared-slot enumerator's
2505    /// kind-coherence gate, a two-consumer split far from the source
2506    /// `caixa.lisp` with no field naming the entry-drift root cause.
2507    /// Lifting the resolution rule to a typed method on the substrate
2508    /// primitive means every downstream consumer of the caixa's per-
2509    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2510    /// surface reaches for exactly one typed dispatch — the resolver's
2511    /// accept-set migrates as a unit on any future axis addition.
2512    ///
2513    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2514    /// return composite-reference accessor — closes the outer-`Caixa`
2515    /// `Option<&Composite>` composite-reference sub-family opened by
2516    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2517    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2518    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2519    /// folds on the "one typed dispatch on the substrate primitive,
2520    /// thin projections at each consumer" discipline extended onto the
2521    /// third and final M3 mesh-slot axis. Peer of the closed inner
2522    /// mesh-slot outer-composite family the sibling
2523    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2524    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2525    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2526    /// accessor pins already close on the inner
2527    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2528    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2529    /// altitudes of the outer-composite reference-return discipline
2530    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2531    /// slot presence) now carry the full five-arm accept-set behind a
2532    /// typed dispatch on the substrate primitive. Returns
2533    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2534    /// because every downstream consumer of the entrada composite
2535    /// treats it as a read-only per-axis dispatch source — the
2536    /// reference-view is the narrowest borrow that supports every
2537    /// present + roadmapped consumer (per-axis accessor dispatch,
2538    /// serde composite-serialization on the programs.yaml overlay,
2539    /// presence-probe early return on the "author-omitted `:entrada`
2540    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2541    /// seed's forward arm) without cloning the composite through every
2542    /// consumer's fast path. The `Option` half of the return-type
2543    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2544    /// cluster-internal Aplicacao" partition (not a default composite
2545    /// the downstream must reject on emptiness — a cluster-internal
2546    /// Aplicacao has no external gateway at all, not "a default gateway
2547    /// that emits nothing"); the accessor projects the raw
2548    /// `Option<Entrada>` slot's presence bit through the reference-
2549    /// return unchanged. Named `entrada()` to match the storage field's
2550    /// name verbatim and the tatara-lisp author-surface term
2551    /// (`:entrada`) the field's own docstring already carries.
2552    #[must_use]
2553    pub fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
2554        self.entrada.as_ref()
2555    }
2556
2557    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
2558    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
2559    /// an `Option<&CiRun>`, borrowed from the typed slot's own
2560    /// `Option<CiRun>` storage. `None` when the slot is absent (every
2561    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
2562    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
2563    /// not silently accepted).
2564    ///
2565    /// Named `ci()` to match the storage field's name and the
2566    /// tatara-lisp author surface (`:ci`); mirrors the sibling
2567    /// `Option<&Composite>` accessors on this same `Caixa` altitude
2568    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
2569    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
2570    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
2571    /// at every consumer.
2572    #[must_use]
2573    pub fn ci(&self) -> Option<&canteiro_types::CiRun> {
2574        self.ci.as_ref()
2575    }
2576
2577    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
2578    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
2579    /// accessor every consumer of the top-level manifest's per-Supervisor
2580    /// restart-strategy axis keys off — returns the author-declared
2581    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
2582    /// `Copy`-projected from the typed slot's own
2583    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
2584    /// (`:estrategia` is a flat-spread supervisor-only slot every
2585    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
2586    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2587    /// still omit to defer to [`RestartStrategy::default`] —
2588    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
2589    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
2590    /// [`SupervisorSpec::default`]-inherited strategy without any silent
2591    /// promotion to a fresh explicit variant at the accessor boundary).
2592    ///
2593    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
2594    /// restart-strategy discriminant every substrate-side per-Supervisor
2595    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
2596    /// closed-set `one_for_one | one_for_all | rest_for_one |
2597    /// simple_one_for_one` algebra translated onto pleme-io's typed
2598    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
2599    /// slot algebra the operator's hierarchical reconciliation scheduler
2600    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
2601    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
2602    /// supervisor slots are flat on Caixa (vs nested under a
2603    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
2604    /// level of nesting"), so the accessor's altitude is the outer
2605    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
2606    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
2607    /// (eafb619) accessor keys off. The two typed axes — the outer
2608    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
2609    /// (author-omitted arm carried as `None`) and the inner post-
2610    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
2611    /// (`Option` collapsed through the [`Self::supervisor_view`]
2612    /// `unwrap_or_default()` fold) — now share one accessor discipline for
2613    /// the shared substrate concept "the author-declared OTP-shaped
2614    /// sibling-restart-strategy variant that partitions the downstream
2615    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
2616    /// `None` arm is the pre-composition presence bit every declared-slot
2617    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
2618    /// inner-altitude non-`Option` `RestartStrategy` is the post-
2619    /// composition partition-dispatch input every strategy-arm consumer
2620    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
2621    /// Supervisor sibling-restart branch, the future M4
2622    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
2623    /// webhook) fans on.
2624    ///
2625    /// Prior to this lift the `.estrategia` field was accessed inline at
2626    /// two production sites in `caixa-core/src/manifest.rs` — the
2627    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
2628    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
2629    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2630    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
2631    /// `SupervisorSpec` construction site at `estrategia:
2632    /// self.estrategia.unwrap_or_default()` (which composes the flat-
2633    /// spread outer author-surface `Option<RestartStrategy>` onto the
2634    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
2635    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
2636    /// coded field-accesses that expressed no compile-time link back to
2637    /// the typed slot. A future extension of the outer `:estrategia` axis
2638    /// to a richer author surface (a per-cluster strategy override the
2639    /// operator pins through a future `:estrategia-overrides` overlay the
2640    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
2641    /// a per-tenant strategy-alias table the M4 CR materializer resolves
2642    /// per-CR, a per-Supervisor dynamic strategy derivation the future
2643    /// adaptive-supervision engine computes from child-failure-history
2644    /// topology, a per-child-cohort strategy split the future
2645    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
2646    /// absorption roadmap acknowledges, a promotion of the plain
2647    /// `Option<RestartStrategy>` to a richer
2648    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
2649    /// operator-resolved overlay lands) would have had to be threaded
2650    /// through both open-coded copies in lockstep or the enumerator's
2651    /// presence probe and the composition site's `unwrap_or_default()`
2652    /// fold would silently disagree on which strategy a given [`Caixa`]
2653    /// resolves to (an author's `:estrategia OneForAll` would satisfy
2654    /// the enumerator's presence probe while the composition site
2655    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
2656    /// the resolution rule to a typed method on the substrate primitive
2657    /// means every downstream consumer of the caixa's per-`Caixa` outer-
2658    /// altitude sibling-restart-strategy surface reaches for exactly one
2659    /// typed dispatch — the resolver's accept-set migrates as a unit on
2660    /// any future axis addition.
2661    ///
2662    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2663    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2664    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
2665    /// projection pattern the sibling per-`Caixa` `:max-restarts`
2666    /// `Option<u32>` and (through the future duration-newtype landing)
2667    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
2668    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
2669    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
2670    /// the post-composition [`SupervisorSpec`] altitude — same "one
2671    /// typed dispatch on the substrate primitive, thin projections at
2672    /// each consumer" discipline extended onto the pre-composition outer
2673    /// author-surface [`Caixa`] altitude for the same OTP-shaped
2674    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
2675    /// `Option<&Composite>` composite-reference family the sibling
2676    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
2677    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
2678    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
2679    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
2680    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
2681    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
2682    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2683    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
2684    /// pins on the inner-altitude per-`:placement` composite. Named
2685    /// `estrategia()` to match the storage field's name and the
2686    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
2687    /// / per-[`crate::aplicacao::Placement`] peer
2688    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
2689    /// verbatim; the accessor's identity name maps onto the canonical
2690    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
2691    /// docstring already carries.
2692    #[must_use]
2693    pub fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
2694        self.estrategia
2695    }
2696
2697    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
2698    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
2699    /// scalar accessor every consumer of the top-level manifest's per-
2700    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
2701    /// returns the author-declared `:max-restarts` typed `Option<u32>`
2702    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
2703    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
2704    /// accessor returns by value; no borrow of `&self` past the call).
2705    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
2706    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
2707    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2708    /// still omit to defer to the [`Self::supervisor_view`]
2709    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
2710    ///
2711    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
2712    /// `MaxIntensity` restart-budget count that pairs with the sibling
2713    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
2714    /// restart-intensity ratio the supervisor trips its own escalation on
2715    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
2716    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
2717    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
2718    /// reconciliation scheduler fans on). The slot is *flat-spread* on
2719    /// the outer top-level `Caixa` (per the field-shape docstring at
2720    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
2721    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
2722    /// accessor's altitude is the outer [`Caixa`] surface rather than the
2723    /// composed [`SupervisorSpec`] altitude the sibling
2724    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
2725    /// off. The two typed axes — the outer author-surface `Option<u32>`
2726    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
2727    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
2728    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
2729    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
2730    /// shared substrate concept "the author-declared OTP-shaped
2731    /// restart-budget count every downstream per-Supervisor consumer's
2732    /// restart-intensity budget-vs-count comparator fans on".
2733    ///
2734    /// Prior to this lift the `.max_restarts` field was accessed inline
2735    /// at two production sites in `caixa-core/src/manifest.rs` — the
2736    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
2737    /// presence-probe arm at `if self.max_restarts.is_some()` (which
2738    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
2739    /// kind-coherence gate's per-slot label push) and the
2740    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
2741    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
2742    /// flat-spread outer author-surface `Option<u32>` onto the inner
2743    /// post-composition [`SupervisorSpec`] `u32` field the
2744    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
2745    /// coded field-accesses that expressed no compile-time link back to
2746    /// the typed slot. A future extension of the outer `:max-restarts`
2747    /// axis to a richer author surface (a per-cluster restart-budget
2748    /// override the operator pins through a future `:max-restarts-overrides`
2749    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
2750    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
2751    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
2752    /// budget derivation the future adaptive-supervision engine computes
2753    /// from child-failure-history topology, a promotion of the plain
2754    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
2755    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
2756    /// per-child-cohort roadmap lands) would have had to be threaded
2757    /// through both open-coded copies in lockstep or the enumerator's
2758    /// presence probe and the composition site's `unwrap_or(5)` fold
2759    /// would silently disagree on which restart-budget a given [`Caixa`]
2760    /// resolves to (an author's `:max-restarts 10` would satisfy the
2761    /// enumerator's presence probe while the composition site silently
2762    /// composed the OTP-canonical `5`, or vice versa). Lifting the
2763    /// resolution rule to a typed method on the substrate primitive means
2764    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
2765    /// restart-budget-count surface reaches for exactly one typed dispatch
2766    /// — the resolver's accept-set migrates as a unit on any future axis
2767    /// addition.
2768    ///
2769    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2770    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2771    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
2772    /// projection pattern the sibling per-`Caixa`
2773    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
2774    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
2775    /// Peer of the inner-altitude
2776    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
2777    /// on the post-composition [`SupervisorSpec`] altitude — same "one
2778    /// typed dispatch on the substrate primitive, thin projections at
2779    /// each consumer" discipline extended onto the pre-composition outer
2780    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
2781    /// shaped restart-budget-count axis. Named `max_restarts()` to match
2782    /// the storage field's name and the per-[`SupervisorSpec`] peer
2783    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
2784    /// discipline verbatim; the accessor's identity maps onto the
2785    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
2786    /// field's docstring already carries.
2787    #[must_use]
2788    pub const fn max_restarts(&self) -> Option<u32> {
2789        self.max_restarts
2790    }
2791
2792    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
2793    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
2794    /// denominator raw-duration-string scalar accessor every consumer of
2795    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
2796    /// window axis keys off — returns the author-declared `:restart-window`
2797    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
2798    /// from the typed slot's own `Option<String>` storage. `None` when
2799    /// the slot is absent (the canonical "never reset — every restart
2800    /// across the supervisor's lifetime counts against the sibling
2801    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
2802    /// `defcaixa` carries by `#[serde(default)]` and every
2803    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
2804    /// [`Self::supervisor_view`] `restart_window: None` composition
2805    /// through the [`crate::supervisor::duration_codec::parse`] soft-
2806    /// swallow `.and_then(|s| … .ok())` fold).
2807    ///
2808    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
2809    /// shaped `Period` sliding-observation-interval duration string that
2810    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
2811    /// budget count to form the `MaxIntensity / Period` restart-intensity
2812    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
2813    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
2814    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
2815    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
2816    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
2817    /// holds an `Option<Duration>` routed through the shared
2818    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
2819    /// — so the outer altitude's accessor returns `Option<&str>` (raw
2820    /// authoring surface) while the inner altitude's
2821    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
2822    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
2823    /// is closed by the sibling [`Self::validate_restart_window`] gate
2824    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
2825    /// the offending value; the view-construction path
2826    /// [`Self::supervisor_view`] soft-swallows the same parse error to
2827    /// `None` to keep the view best-effort.
2828    ///
2829    /// Prior to this lift the `.restart_window` field was accessed inline
2830    /// at three production sites in `caixa-core/src/manifest.rs` — the
2831    /// [`Self::declared_supervisor_slots`]
2832    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
2833    /// `if self.restart_window.is_some()` (which drives the
2834    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2835    /// coherence gate's per-slot label push), the
2836    /// [`Self::validate_restart_window`] `let Some(s) =
2837    /// self.restart_window.as_deref()` empty-and-shape gate binding
2838    /// (which folds the raw string through the shared
2839    /// [`crate::supervisor::duration_codec::parse`] to surface
2840    /// [`ManifestError::RestartWindowMalformed`] naming the offending
2841    /// value), and the [`Self::supervisor_view`] `self.restart_window
2842    /// .as_deref().and_then(…)` view-construction fold (which composes
2843    /// the flat-spread outer author-surface `Option<String>` onto the
2844    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
2845    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
2846    /// three open-coded field-accesses that expressed no compile-time
2847    /// link back to the typed slot. A future extension of the outer
2848    /// `:restart-window` axis to a richer author surface (a per-cluster
2849    /// window override, a per-tenant window-alias table, a per-Supervisor
2850    /// dynamic window derivation the future adaptive-supervision engine
2851    /// computes from child-failure-history topology, a promotion of the
2852    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
2853    /// once the future author-surface parser lands at the [`Caixa`]
2854    /// altitude and the raw-string form is retired) would have had to be
2855    /// threaded through every open-coded copy in lockstep or the three
2856    /// consumers would silently disagree on which raw string a given
2857    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
2858    /// method on the substrate primitive means every downstream consumer
2859    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
2860    /// string surface reaches for exactly one typed dispatch — the
2861    /// resolver's accept-set migrates as a unit on any future axis
2862    /// addition.
2863    ///
2864    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
2865    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
2866    /// spread projection pattern the sibling per-`Caixa`
2867    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
2868    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
2869    /// the sub-family onto the sibling `Option<&str>` raw-duration-
2870    /// string arm (the outer altitude's raw-string form; the inner
2871    /// altitude's parsed [`Duration`] form is the peer
2872    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
2873    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
2874    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
2875    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
2876    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
2877    /// sub-family already carries — same "one typed dispatch on the
2878    /// substrate primitive, thin projections at each consumer"
2879    /// discipline extended onto the M2 supervisor-tree flat-spread
2880    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
2881    /// to match the storage field's name and the per-[`SupervisorSpec`]
2882    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
2883    /// method-name discipline verbatim; the accessor's identity maps
2884    /// onto the canonical OTP-shape supervision vocabulary the
2885    /// `:restart-window` field's docstring already carries.
2886    #[must_use]
2887    pub fn restart_window(&self) -> Option<&str> {
2888        self.restart_window.as_deref()
2889    }
2890
2891    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
2892    /// outer-composite OTP-appup-shaped per-prior-version migration-
2893    /// entry-list slice accessor every consumer of the top-level
2894    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
2895    /// slice-view keys off — returns the author-declared `:upgrade-from`
2896    /// typed `Vec<UpgradeFromEntry>` verbatim as a
2897    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
2898    /// the raw `self.upgrade_from.as_slice()` field access borrows
2899    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
2900    /// arm every `defcaixa` without an `:upgrade-from` block carries;
2901    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
2902    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
2903    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
2904    /// possibly empty — and the returned `&[UpgradeFromEntry]`
2905    /// degenerates to an empty slice on that arm without any silent
2906    /// `None` collapse).
2907    ///
2908    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
2909    /// migration block — the load-bearing container of every per-
2910    /// prior-`:versao` migration-instruction list the wasm-operator
2911    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
2912    /// `.appup` per-prior-version `LoadModule | StateChange |
2913    /// SoftPurge | Purge | Restart` instruction algebra translated
2914    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
2915    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
2916    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
2917    /// threads through a lifted per-entry accessor on the
2918    /// [`UpgradeFromEntry`] type: the
2919    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
2920    /// version scalar accessor and the
2921    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
2922    /// return per-entry instruction-list accessor (0137e5a). Every
2923    /// downstream consumer of the hot-upgrade path first passes
2924    /// through this outer accessor onto the slice and then dispatches
2925    /// per-entry through the inner accessors — the two-level dispatch
2926    /// means every per-`:upgrade-from` reader now routes through a
2927    /// typed dispatch on the substrate primitive at both altitudes.
2928    ///
2929    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
2930    /// slot was accessed inline at production sites across three
2931    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
2932    /// enumerator's `self.upgrade_from.is_empty()` presence probe
2933    /// (caixa-core/src/manifest.rs, which drives the
2934    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
2935    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2936    /// gate reads), the [`crate::StandardLayout::verify`] per-
2937    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
2938    /// layout.rs, which fans onto the
2939    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
2940    /// cross-entry duplicate gate, the
2941    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
2942    /// SemVer-precedence cross-slot gate, the
2943    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2944    /// `:state-change` ↔ `:on-state-change` cross-slot composition
2945    /// gate, and the per-instruction script-path existence-probe walk
2946    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
2947    /// resolve every declared migration script against the layout
2948    /// root), and the [`crate::render::servico_m2_overlay`] per-
2949    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
2950    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
2951    /// projection (caixa-core/src/render.rs, which drives the
2952    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
2953    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
2954    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
2955    /// A future extension of the outer `:upgrade-from` axis (a per-
2956    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
2957    /// resolves at admission time so a cluster-specific migration
2958    /// policy can tighten a caixa-declared step without re-authoring
2959    /// the `caixa.lisp`, promotion of the plain
2960    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
2961    /// partition once runtime-resolved hot-upgrade instructions land,
2962    /// per-entry priority annotation once multi-strategy fan-out
2963    /// lands) would have had to be threaded through all six open-
2964    /// coded copies in lockstep or one consumer would silently
2965    /// disagree with the peers on which upgrade slice a given Caixa
2966    /// resolves to — a six-consumer split at the enumerator, the
2967    /// three-stage validate pass, the script-path probe walk, and the
2968    /// M2 overlay emitter, far from the source `caixa.lisp` with no
2969    /// field naming the upgrade-drift root cause. Lifting the
2970    /// resolution rule to a typed method on the substrate primitive
2971    /// means every downstream consumer of the caixa's per-`Caixa`
2972    /// OTP-appup outer-slice surface reaches for exactly one typed
2973    /// dispatch — the resolver's accept-set migrates as a unit on any
2974    /// future axis addition.
2975    ///
2976    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
2977    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
2978    /// outer-`Caixa` `&[Composite]` composite-slice projection
2979    /// pattern the sibling `:children`
2980    /// [`crate::supervisor::ChildSpec`] / `:membros`
2981    /// [`crate::aplicacao::Membro`] / `:contratos`
2982    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
2983    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
2984    /// `Option<&Composite>` composite-reference family the sibling
2985    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
2986    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
2987    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
2988    /// `Option<&Composite>` altitude, extended here to the outer-
2989    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
2990    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
2991    /// (0137e5a) — same "one typed dispatch on the substrate
2992    /// primitive, thin projections at each consumer" discipline
2993    /// folded onto the outer top-level [`Caixa`] altitude, opening the
2994    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
2995    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
2996    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
2997    /// `&[String]`-return [`Self::autores`] (b5d813f) /
2998    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
2999    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3000    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3001    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3002    /// slice" projection pattern onto the sibling M2 typed-composite-
3003    /// element axis (`UpgradeFromEntry` composite, matching the
3004    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3005    /// different altitude).
3006    ///
3007    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3008    /// because every downstream consumer of the hot-upgrade list
3009    /// treats it as a read-only sequence — the slice-view is the
3010    /// narrowest borrow that supports every present + roadmapped
3011    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3012    /// serialization through
3013    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3014    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3015    /// the typed view reaches for (the storage-side `Vec` remains
3016    /// reachable through the `pub upgrade_from` field for the
3017    /// mutation-carrying serde round-trip and per-test fixture-
3018    /// mutation paths). Named `upgrade_from()` to match the storage
3019    /// field's `snake_case` name; the kebab-case author-surface tag
3020    /// `:upgrade-from` is the same axis after tatara-lisp's
3021    /// kebab↔snake fold and the accessor's identity maps onto the
3022    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3023    /// already carries.
3024    #[must_use]
3025    pub fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3026        self.upgrade_from.as_slice()
3027    }
3028
3029    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3030    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3031    /// slice accessor every consumer of the top-level manifest's per-
3032    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3033    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3034    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3035    /// the same backing buffer the raw `self.children.as_slice()` field
3036    /// access borrows from. Empty-slice-carrying (the "no static children
3037    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3038    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3039    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3040    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3041    /// on those arms without any silent `None` collapse).
3042    ///
3043    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3044    /// static-child list — the load-bearing container of every per-
3045    /// child `{caixa, versao, restart}` triple the wasm-operator's
3046    /// hierarchical reconciler dispatches on at supervisor-tree
3047    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3048    /// static-child list translated onto pleme-io's typed
3049    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3050    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3051    /// dispatch fans on). Every per-child axis threads through a lifted
3052    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3053    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3054    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3055    /// version-requirement scalar accessor, and the
3056    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3057    /// per-child post-exit restart-decision-policy discriminant
3058    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3059    /// tree path first passes through this outer accessor onto the
3060    /// slice and then dispatches per-child through the inner accessors
3061    /// — the two-level dispatch means every per-`:children` reader now
3062    /// routes through a typed dispatch on the substrate primitive at
3063    /// both altitudes.
3064    ///
3065    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3066    /// accessed inline at three production sites across two files —
3067    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3068    /// declared-slot enumerator's `!self.children.is_empty()` presence
3069    /// probe (caixa-core/src/manifest.rs, which drives the
3070    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3071    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3072    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3073    /// per-supervisor typed-view composer's `self.children.clone()`
3074    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3075    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3076    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3077    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3078    /// `:children :caixa` self-parent refusal probe's
3079    /// `&caixa.children`-borrowed
3080    /// [`crate::supervisor::validate_no_self_supervision`] input
3081    /// (caixa-core/src/layout.rs, which pins the "no child names the
3082    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3083    /// extension of the outer `:children` axis (a per-cluster
3084    /// `:children-overrides` overlay the wasm-engine operator resolves
3085    /// at admission time so a cluster-specific child-set can tighten
3086    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3087    /// promotion of the plain `Vec<ChildSpec>` to a richer
3088    /// `{static, dynamic}` partition once Erlang/OTP's
3089    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3090    /// axis, per-child priority annotation once multi-strategy fan-out
3091    /// lands) would have had to be threaded through all three open-
3092    /// coded copies in lockstep or one consumer would silently
3093    /// disagree with the peers on which child slice a given Caixa
3094    /// resolves to — the enumerator's presence probe reading the raw
3095    /// slot while the peer view-composer's fold-in path read an
3096    /// operator-resolved slot would silently split the paired
3097    /// declared-slot enumerator and typed-view composition, and the
3098    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3099    /// refusal probe reading a third borrow would silently drift the
3100    /// cross-slot coherence gate's traversal input from the two peers,
3101    /// a three-consumer split at the enumerator, the view composer,
3102    /// and the self-parent gate far from the source `caixa.lisp` with
3103    /// no field naming the child-set-drift root cause. Lifting the
3104    /// resolution rule to a typed method on the substrate primitive
3105    /// means every downstream consumer of the caixa's per-`Caixa`
3106    /// OTP-supervisor outer-slice surface reaches for exactly one
3107    /// typed dispatch — the resolver's accept-set migrates as a unit
3108    /// on any future axis addition.
3109    ///
3110    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3111    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3112    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3113    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3114    /// at the outer altitude of the closed inner-`SupervisorSpec`
3115    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3116    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3117    /// borrow-shared" outer-accessor discipline extended onto the
3118    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3119    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3120    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3121    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3122    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3123    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3124    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3125    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3126    /// M2 typed-composite-element axis
3127    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3128    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3129    /// different altitude).
3130    ///
3131    /// Returns `&[crate::supervisor::ChildSpec]` (not
3132    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3133    /// child list treats it as a read-only sequence — the slice-view
3134    /// is the narrowest borrow that supports every present +
3135    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3136    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3137    /// input, `serde` slice-serialization) without leaking the backing
3138    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3139    /// reaches for (the storage-side `Vec` remains reachable through
3140    /// the `pub children` field for the mutation-carrying serde round-
3141    /// trip and per-test fixture-mutation paths, including the
3142    /// [`Self::supervisor_view`] fold-in path that clones the slot
3143    /// into the typed view). Named `children()` to match the storage
3144    /// field's name verbatim and the tatara-lisp author-surface term
3145    /// (`:children`) the field's own docstring already carries; the
3146    /// accessor's identity maps onto the canonical OTP supervision
3147    /// vocabulary the [`Caixa::children`] field's docstring already
3148    /// reaches for ("Static children of a supervisor").
3149    #[must_use]
3150    pub fn children(&self) -> &[crate::supervisor::ChildSpec] {
3151        self.children.as_slice()
3152    }
3153
3154    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3155    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3156    /// accessor every consumer of the top-level manifest's per-Aplicacao
3157    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3158    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3159    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3160    /// same backing buffer the raw `self.membros.as_slice()` field access
3161    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3162    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3163    /// and every partially-authored Aplicacao carries before the
3164    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3165    /// `&[Membro]` degenerates to an empty slice on those arms without any
3166    /// silent `None` collapse).
3167    ///
3168    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3169    /// per-Aplicacao member list — the load-bearing container of every
3170    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3171    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3172    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3173    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3174    /// the `:entrada :para` external-gateway destination validates
3175    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3176    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3177    /// threads through a lifted per-entry accessor on the
3178    /// [`crate::aplicacao::Membro`] type: the
3179    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3180    /// identity scalar accessor (4a32abf) and the peer
3181    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3182    /// version-requirement scalar accessor (a40b0e3). Every downstream
3183    /// consumer of the mesh-graph path first passes through this outer
3184    /// accessor onto the slice and then dispatches per-member through
3185    /// the inner accessors — the two-level dispatch means every per-
3186    /// `:membros` reader now routes through a typed dispatch on the
3187    /// substrate primitive at both altitudes.
3188    ///
3189    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3190    /// inline at three production sites across two files — the
3191    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3192    /// enumerator's `!self.membros.is_empty()` presence probe
3193    /// (caixa-core/src/manifest.rs, which drives the
3194    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3195    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3196    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3197    /// composer's `self.membros.clone()` per-member fold-in path
3198    /// (caixa-core/src/manifest.rs, which materializes the typed
3199    /// [`crate::aplicacao::AplicacaoSpec`] view every
3200    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3201    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3202    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3203    /// [`crate::aplicacao::validate_no_self_membership`] input
3204    /// (caixa-core/src/layout.rs, which pins the "no member names the
3205    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3206    /// extension of the outer `:membros` axis (a per-cluster
3207    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3208    /// admission time so a cluster-specific member-set can tighten a
3209    /// caixa-declared list without re-authoring the `caixa.lisp`,
3210    /// promotion of the plain `Vec<Membro>` to a richer
3211    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3212    /// members land as a typed axis, per-member priority annotation once
3213    /// multi-strategy fan-out lands) would have had to be threaded
3214    /// through all three open-coded copies in lockstep or one consumer
3215    /// would silently disagree with the peers on which member slice a
3216    /// given Caixa resolves to — the enumerator's presence probe reading
3217    /// the raw slot while the peer view-composer's fold-in path read an
3218    /// operator-resolved slot would silently split the paired
3219    /// declared-slot enumerator and typed-view composition, and the
3220    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3221    /// refusal probe reading a third borrow would silently drift the
3222    /// cross-slot coherence gate's traversal input from the two peers, a
3223    /// three-consumer split at the enumerator, the view composer, and
3224    /// the self-membership gate far from the source `caixa.lisp` with no
3225    /// field naming the member-set-drift root cause. Lifting the
3226    /// resolution rule to a typed method on the substrate primitive
3227    /// means every downstream consumer of the caixa's per-`Caixa`
3228    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3229    /// typed dispatch — the resolver's accept-set migrates as a unit on
3230    /// any future axis addition.
3231    ///
3232    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3233    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3234    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3235    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3236    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3237    /// altitude. Peer at the outer altitude of the closed inner-
3238    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3239    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3240    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3241    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3242    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3243    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3244    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3245    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3246    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3247    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3248    /// pattern onto the sibling M3 typed-composite-element axis
3249    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3250    /// [`crate::AplicacaoSpec::membros`] element type at a different
3251    /// altitude).
3252    ///
3253    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3254    /// because every downstream consumer of the member list treats it
3255    /// as a read-only sequence — the slice-view is the narrowest borrow
3256    /// that supports every present + roadmapped consumer (`.iter()`,
3257    /// `.len()`, `.is_empty()`, the
3258    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3259    /// input, `serde` slice-serialization) without leaking the backing
3260    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3261    /// reaches for (the storage-side `Vec` remains reachable through the
3262    /// `pub membros` field for the mutation-carrying serde round-trip
3263    /// and per-test fixture-mutation paths, including the
3264    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3265    /// the typed view). Named `membros()` to match the storage field's
3266    /// name verbatim and the tatara-lisp author-surface term
3267    /// (`:membros`) the field's own docstring already carries; the
3268    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3269    /// vocabulary the [`Caixa::membros`] field's docstring already
3270    /// reaches for ("Member Servicos that make up this Aplicacao").
3271    #[must_use]
3272    pub fn membros(&self) -> &[crate::aplicacao::Membro] {
3273        self.membros.as_slice()
3274    }
3275
3276    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3277    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3278    /// inter-Servico contract-list slice accessor every consumer of the
3279    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3280    /// slice-view keys off — returns the author-declared `:contratos`
3281    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3282    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3283    /// backing buffer the raw `self.contratos.as_slice()` field access
3284    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3285    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3286    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3287    /// single member with no inter-Servico edge carries; the returned
3288    /// `&[WitContract]` degenerates to an empty slice on those arms
3289    /// without any silent `None` collapse).
3290    ///
3291    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3292    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3293    /// container of every per-edge `{de, para, wit, endpoint | subject |
3294    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3295    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3296    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3297    /// adjacency-list seed dispatch on at mesh-artifact materialization
3298    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3299    /// `:membros` vertex set resolves against, closed by the
3300    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3301    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3302    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3303    /// per-edge axis threads through a lifted per-entry accessor on the
3304    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3305    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3306    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3307    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3308    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3309    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3310    /// and the WIT-world discriminant. Every downstream consumer of the
3311    /// mesh-graph edge path first passes through this outer accessor
3312    /// onto the slice and then dispatches per-contract through the
3313    /// inner accessors — the two-level dispatch means every
3314    /// per-`:contratos` reader now routes through a typed dispatch on
3315    /// the substrate primitive at both altitudes.
3316    ///
3317    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3318    /// accessed inline at two production sites in
3319    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3320    /// mesh-slot declared-slot enumerator's
3321    /// `!self.contratos.is_empty()` presence probe (which drives the
3322    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3323    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3324    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3325    /// typed-view composer's `self.contratos.clone()` per-contract
3326    /// fold-in path (which materializes the typed
3327    /// [`crate::aplicacao::AplicacaoSpec`] view every
3328    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3329    /// downstream `caixa-mesh` renderer dispatches on). A future
3330    /// extension of the outer `:contratos` axis (a per-cluster
3331    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3332    /// at admission time so a cluster-specific edge-set can tighten a
3333    /// caixa-declared list without re-authoring the `caixa.lisp`,
3334    /// promotion of the plain `Vec<WitContract>` to a richer
3335    /// `{static, dynamic}` partition once runtime-resolved contract
3336    /// edges land, per-edge policy annotation once the M4 per-edge
3337    /// policy overlay axis lands) would have had to be threaded through
3338    /// both open-coded copies in lockstep or one consumer would
3339    /// silently disagree with the peer on which edge slice a given
3340    /// Caixa resolves to — the enumerator's presence probe reading the
3341    /// raw slot while the peer view-composer's fold-in path read an
3342    /// operator-resolved slot would silently split the paired
3343    /// declared-slot enumerator and typed-view composition, a
3344    /// two-consumer split at the enumerator and the view composer far
3345    /// from the source `caixa.lisp` with no field naming the edge-set-
3346    /// drift root cause. Lifting the resolution rule to a typed method
3347    /// on the substrate primitive means every downstream consumer of
3348    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3349    /// reaches for exactly one typed dispatch — the resolver's
3350    /// accept-set migrates as a unit on any future axis addition.
3351    ///
3352    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3353    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3354    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3355    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3356    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3357    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3358    /// mesh-slot arm of the composite-slice sub-family the sibling
3359    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3360    /// Peer at the outer altitude of the closed inner-
3361    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3362    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3363    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3364    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3365    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3366    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3367    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3368    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3369    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3370    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3371    /// pattern onto the sibling M3 typed-composite-element axis
3372    /// ([`crate::aplicacao::WitContract`] composite, matching the
3373    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3374    /// different altitude).
3375    ///
3376    /// Returns `&[crate::aplicacao::WitContract]` (not
3377    /// `&Vec<WitContract>`) because every downstream consumer of the
3378    /// contract list treats it as a read-only sequence — the slice-view
3379    /// is the narrowest borrow that supports every present + roadmapped
3380    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3381    /// discriminant dispatch, `serde` slice-serialization) without
3382    /// leaking the backing `Vec`'s grow/push/reserve surface no
3383    /// consumer of the typed view reaches for (the storage-side `Vec`
3384    /// remains reachable through the `pub contratos` field for the
3385    /// mutation-carrying serde round-trip and per-test fixture-mutation
3386    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3387    /// clones the slot into the typed view). Named `contratos()` to
3388    /// match the storage field's name verbatim and the tatara-lisp
3389    /// author-surface term (`:contratos`) the field's own docstring
3390    /// already carries; the accessor's identity maps onto the canonical
3391    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3392    /// docstring already reaches for ("WIT-typed inter-Servico
3393    /// contracts").
3394    #[must_use]
3395    pub fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3396        self.contratos.as_slice()
3397    }
3398
3399    /// Compose the Aplicacao-related flat slots into a single typed
3400    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3401    /// downstream renderer consumption. Returns `None` when the
3402    /// caixa isn't a `:kind Aplicacao`.
3403    #[must_use]
3404    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3405        if !self.kind().is_aplicacao() {
3406            return None;
3407        }
3408        Some(crate::aplicacao::AplicacaoSpec {
3409            membros: self.membros().to_vec(),
3410            contratos: self.contratos().to_vec(),
3411            politicas: self.politicas().cloned().unwrap_or_default(),
3412            placement: self.placement().cloned().unwrap_or_default(),
3413            entrada: self.entrada().cloned(),
3414        })
3415    }
3416
3417    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3418    /// *declares* a value on, in canonical declaration order
3419    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3420    /// `:entrada`). A slot counts as declared when its backing field
3421    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3422    ///
3423    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3424    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3425    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3426    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3427    /// caixa-flux / caixa-helm renderers only emit them for an
3428    /// Aplicacao. On any *other* kind a declared mesh slot is the
3429    /// manifest field's documented "ignored otherwise" (see the
3430    /// `:membros` … `:entrada` field docs): it silently passes
3431    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3432    /// rendered — far from the source caixa.lisp.
3433    /// [`crate::StandardLayout::verify`] consults this to reject that
3434    /// silent-drop at caixa-build time
3435    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3436    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3437    /// a slot foreign to the kind is a build error, not a silent drop.
3438    ///
3439    /// Lifted as a typed method (rather than an inline disjunction at
3440    /// the verify call site) so the mesh-slot set lives in one place —
3441    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3442    /// overlay, distributed-app takeover config) is one push here, and
3443    /// every consumer reaching for "which mesh slots are set" (the
3444    /// verify gate, a future `feira lint` kind-coherence advisory)
3445    /// inherits the canonical order without rolling its own.
3446    ///
3447    /// Each per-arm kebab-case label is routed through the peer
3448    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3449    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3450    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3451    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3452    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3453    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3454    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3455    /// kebab-case label + renderer-side artifact key) route through one
3456    /// canonical declaration per arm — same discipline the peer
3457    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3458    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3459    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3460    /// axis, extended here to close the M3 mesh-slot author-facing-label
3461    /// axis so both altitudes of the typed-slot algebra
3462    /// (per-Servico M2 + per-Aplicacao M3) share the same
3463    /// "one canonical byte-string per arm, next to the axis" discipline.
3464    #[must_use]
3465    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3466        let mut slots = Vec::new();
3467        if !self.membros().is_empty() {
3468            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3469        }
3470        if !self.contratos().is_empty() {
3471            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3472        }
3473        if self.politicas().is_some() {
3474            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3475        }
3476        if self.placement().is_some() {
3477            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3478        }
3479        if self.entrada().is_some() {
3480            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3481        }
3482        slots
3483    }
3484
3485    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3486    /// caixa *declares* a value on, in canonical declaration order
3487    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3488    /// `:children`). A slot counts as declared when its backing field
3489    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3490    ///
3491    /// The supervisor-tree slots compose the typed OTP supervisor of a
3492    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3493    /// `:children` field docs above). [`Self::supervisor_view`] only
3494    /// folds them into a validatable [`SupervisorSpec`] when the kind
3495    /// matches (returns `None` otherwise), and the wasm-operator's
3496    /// hierarchical reconciler only consumes them for a Supervisor. On
3497    /// any *other* kind a declared supervisor slot is the manifest
3498    /// field's documented "ignored otherwise" (see the `:estrategia` …
3499    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3500    /// and then vanishes — never validated, never reconciled — far from
3501    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3502    /// this to reject that silent-drop at caixa-build time
3503    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3504    /// exact mirror of the [`Self::declared_mesh_slots`] /
3505    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3506    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3507    /// error, not a silent drop.
3508    #[must_use]
3509    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3510        let mut slots = Vec::new();
3511        if self.estrategia().is_some() {
3512            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3513        }
3514        if self.max_restarts().is_some() {
3515            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3516        }
3517        if self.restart_window().is_some() {
3518            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3519        }
3520        if !self.children().is_empty() {
3521            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3522        }
3523        slots
3524    }
3525
3526    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3527    /// caixa *declares* a value on, in canonical declaration order
3528    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3529    /// declared when its backing field carries a value — a `Some(...)`,
3530    /// or a non-empty `Vec`.
3531    ///
3532    /// The M2 slots configure the runtime of a long-running wasm
3533    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3534    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3535    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3536    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3537    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3538    /// emit these slots for a Servico; on any *other* kind a declared M2
3539    /// slot is the manifest field's documented "ignored otherwise": its
3540    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3541    /// but the value is never rendered into a chart / programs.yaml entry
3542    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
3543    /// vanishes, far from the source caixa.lisp.
3544    /// [`crate::StandardLayout::verify`] consults this to reject that
3545    /// silent-drop at caixa-build time
3546    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
3547    /// mirror of the [`Self::declared_mesh_slots`] /
3548    /// [`Self::declared_supervisor_slots`] gates on the peer
3549    /// kind-exclusive slot sets: a slot foreign to the kind is a build
3550    /// error, not a silent drop.
3551    ///
3552    /// Each per-arm kebab-case label is routed through the peer
3553    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
3554    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
3555    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
3556    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
3557    /// both halves of the M2 top-level slot's dual axis (author-facing
3558    /// kebab-case label + renderer-side camelCase overlay-container wire
3559    /// key) route through one canonical declaration per arm — same
3560    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
3561    /// author-label consts (889dc18) establish on the sibling
3562    /// per-callback axis inside the `:behavior` overlay block.
3563    #[must_use]
3564    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
3565        let mut slots = Vec::new();
3566        if self.limits().is_some() {
3567            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
3568        }
3569        if self.behavior().is_some() {
3570            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
3571        }
3572        if !self.upgrade_from().is_empty() {
3573            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
3574        }
3575        slots
3576    }
3577
3578    /// The kebab-case `:slot` tags of every code-surface slot this caixa
3579    /// declares a value on that its [`CaixaKind`] doesn't natively own,
3580    /// in canonical declaration order (`:exe` → `:servicos`). A
3581    /// code-surface slot is owned by exactly one kind: `:exe` by
3582    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
3583    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
3584    /// `ComputeUnit` daemon surface).
3585    ///
3586    /// Each is silently ignored when declared on the wrong kind: the
3587    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
3588    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
3589    /// code-running kind a declared `:exe` / `:servicos` is the manifest
3590    /// field's documented "ignored otherwise" — its path is checked for
3591    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
3592    /// (which run after [`Caixa::from_lisp`]), but the value is never
3593    /// rendered into a build target or programs.yaml entry. It silently
3594    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
3595    /// caixa.lisp, with no field naming which slot is foreign.
3596    ///
3597    /// [`crate::StandardLayout::verify`] consults this to reject that
3598    /// silent-drop at caixa-build time
3599    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
3600    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
3601    /// gates ([`Self::declared_servico_slots`] /
3602    /// [`Self::declared_supervisor_slots`] /
3603    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
3604    /// axis to be closed on the typed surface. The Supervisor /
3605    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
3606    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
3607    /// diagnostics — they fire ahead of this gate on the same `verify`
3608    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
3609    /// and this method is moot. For Biblioteca / Binario / Servico, this
3610    /// gate fires when a code-running kind declares another code-running
3611    /// kind's exclusive code surface.
3612    ///
3613    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
3614    /// may legitimately ship a `lib/` helper that the underlying
3615    /// substrate (the nix flake for Binario, the wasm component build
3616    /// for Servico) bundles into its build, so the slot's
3617    /// declared-on-wrong-kind cardinality isn't a structural error on
3618    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
3619    /// is the native case (the slot's owning kind). Supervisor /
3620    /// Aplicacao declaring `:bibliotecas` is gated upstream by
3621    /// [`crate::LayoutError::SupervisorOwnsCode`] /
3622    /// [`crate::LayoutError::AplicacaoOwnsCode`].
3623    ///
3624    /// Lifted as a typed method (rather than an inline disjunction at
3625    /// the verify call site) so the foreign-code-slot set lives in one
3626    /// place — a future kind that gains its own code-surface slot is
3627    /// one push here, and every consumer reaching for "which code
3628    /// surfaces are foreign to this kind" (the verify gate, a future
3629    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
3630    /// per-caixa build-target classifier) inherits the canonical order
3631    /// without rolling its own.
3632    #[must_use]
3633    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
3634        let mut slots = Vec::new();
3635        if !self.exe().is_empty() && !self.kind().requires_exe() {
3636            slots.push(":exe");
3637        }
3638        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
3639            slots.push(":servicos");
3640        }
3641        slots
3642    }
3643
3644    /// Validate every entry of `:deps` and `:deps-dev` through
3645    /// [`Dep::validate`] — closing the parity loop with the per-axis
3646    /// `:versao` gates already wired into the typed-graph
3647    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
3648    /// 9888b13) and typed supervisor tree
3649    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
3650    ///
3651    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
3652    /// were the only `:versao` axes still untyped past
3653    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
3654    /// as a String without parsing it, so a malformed-but-non-empty
3655    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
3656    /// silently passed parse and the `semver::Error` surfaced at
3657    /// lacre-resolve time, far from the source caixa.lisp, with no
3658    /// field naming which `:deps` entry carried the typo. Lifting the
3659    /// gate here makes the four `:versao` typed surfaces (`:deps`,
3660    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
3661    /// every requirement string past `validate_deps` is round-trippable
3662    /// through [`crate::parse_requirement`] without re-checking at the
3663    /// resolver layer.
3664    ///
3665    /// Both lists run through the same per-entry validator so a typo
3666    /// in `:deps-dev` surfaces with the same diagnostic as one in
3667    /// `:deps` — neither axis is a second-class citizen of the typed
3668    /// surface.
3669    ///
3670    /// Within each list, [`DepError::DuplicateNome`] closes the
3671    /// set-not-multiset discipline on the `:nome` axis: two entries
3672    /// naming the same caixa carry two `:versao` / `:fonte` / feature
3673    /// triples that the caixa-resolver's lacre pipeline collapses to one
3674    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
3675    /// silently overwrites the first at `concrete_versao`-resolve time
3676    /// (the same "second wins / one silently overwrites the other"
3677    /// shape the peer typed-graph duplicate gates already close on every
3678    /// other Vec-shaped authoring surface that keys by name). The
3679    /// duplicate check fires per-list and runs *after* each per-entry
3680    /// [`Dep::validate`] call so a malformed-and-duplicated entry
3681    /// surfaces its narrower per-entry diagnostic
3682    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
3683    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
3684    /// diagnostic — the canonical "per-entry shape before cross-entry
3685    /// uniqueness" precedence the peer `:children :caixa`
3686    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
3687    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
3688    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
3689    /// ([`crate::AplicacaoSpec::validate_placement`]),
3690    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
3691    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
3692    /// and the within-`:upgrade-from`-entry per-instruction-class
3693    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
3694    /// [`crate::UpgradeError::DuplicateStateChange`],
3695    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
3696    ///
3697    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
3698    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
3699    /// same name in both tables (the dev table's pin overrides the
3700    /// runtime table's pin in test/dev contexts), and caixa's surface
3701    /// mirrors that convention until a deliberate choice retires the
3702    /// override pattern. Only within-list duplicates are structurally
3703    /// incoherent — those are what this gate closes.
3704    pub fn validate_deps(&self) -> Result<(), DepError> {
3705        for &list in crate::dep::DepList::ALL {
3706            let mut seen = std::collections::HashSet::new();
3707            for dep in self.deps_of(list) {
3708                dep.validate()?;
3709                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
3710                    DepError::DuplicateNome {
3711                        nome: dep.nome().to_string(),
3712                        list: list.as_str(),
3713                    }
3714                })?;
3715            }
3716        }
3717        Ok(())
3718    }
3719
3720    /// Reject `:nome` values the K8s apiserver would refuse at admission
3721    /// time. The top-level Caixa identity flows directly into every
3722    /// substrate-side artifact's `metadata.name` axis: the
3723    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
3724    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
3725    /// aggregator keys ComputeUnit derivation off
3726    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
3727    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
3728    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
3729    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
3730    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
3731    /// ([`caixa-mesh::lib::cilium_network_policies`],
3732    /// [`caixa-mesh::lib::gateway_routes`]), and the default
3733    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
3734    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
3735    /// schema enforces the DNS-1123 label rule on admission; a
3736    /// structurally invalid `:nome` (`"MyApp"` — the canonical
3737    /// "I copied the display name verbatim" footgun, `"my_app"` — the
3738    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
3739    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
3740    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
3741    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
3742    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
3743    /// failure surfaced at `kubectl apply` time as a `metadata.name:
3744    /// Invalid value` rejection on whichever derived artifact admitted
3745    /// first, far from the source `caixa.lisp` and without any field
3746    /// naming the offending `:nome`.
3747    ///
3748    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
3749    /// substrate-side predicate the per-axis name gates already share:
3750    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
3751    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
3752    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
3753    /// diagnostic is self-locating (the offending `:nome` is named
3754    /// verbatim) and the author can grep their `caixa.lisp` for
3755    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
3756    /// every per-axis sibling gate already exposes
3757    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
3758    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
3759    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
3760    ///
3761    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
3762    /// derive macro stores the raw String) is gated by the narrower
3763    /// [`ManifestError::NomeEmpty`] arm before the predicate is
3764    /// consulted, mirroring the empty-first cascade every per-axis
3765    /// name gate already uses (e.g. `MembroCaixaEmpty` before
3766    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
3767    pub fn validate_nome(&self) -> Result<(), ManifestError> {
3768        // Routes through the shared
3769        // [`crate::render::require_valid_dns_1123_label`] gate the peer
3770        // name axes each land on so drift between the eight axes'
3771        // accepted DNS-1123-label sets is structurally impossible.
3772        let nome = self.nome();
3773        crate::render::require_valid_dns_1123_label(
3774            nome,
3775            || ManifestError::NomeEmpty,
3776            |reason| ManifestError::NomeInvalid {
3777                nome: nome.to_string(),
3778                reason,
3779            },
3780        )
3781    }
3782
3783    /// Reject `:nome` values whose joint length with the canonical
3784    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
3785    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
3786    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
3787    /// substrate carries materializes the caixa's `:nome` through the
3788    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
3789    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
3790    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
3791    /// `ChartDir.name` + `Chart.yaml::name`
3792    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
3793    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
3794    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
3795    /// `oci://<registry>/lareira-<nome>` chart ref
3796    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
3797    /// admission rule strict-parses against DNS-1123-label, the Helm
3798    /// operator's tracking-secret name is derived from `release_name`
3799    /// and is itself DNS-1123-label-bounded, and the rendered chart's
3800    /// K8s object `metadata.name` axes embed the chart name as a
3801    /// prefix — every one fails admission on a > 63-byte chart name.
3802    ///
3803    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
3804    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
3805    /// `:nome` of 56–63 bytes silently passed validate (the inner
3806    /// DNS-1123 check accepts the bare `:nome`) but produced a
3807    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
3808    /// rejected at admission — far from the source `caixa.lisp`, with
3809    /// no field naming the overflow root cause. The
3810    /// [`lareira_chart_name`] helper's own doc comment
3811    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
3812    /// "the M4 admission webhook will pin the joint-length invariant
3813    /// when it lands". This gate lands the invariant at the
3814    /// manifest-validate layer rather than waiting for the apiserver
3815    /// — the same fail-at-the-source posture every peer per-axis
3816    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
3817    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
3818    /// `:edicao`, etc.) takes.
3819    ///
3820    /// Thin wrapper around
3821    /// [`crate::render::is_lareira_chart_name_shape`] (the
3822    /// substrate-side predicate that composes [`lareira_chart_name`] +
3823    /// [`is_dns_1123_label`] via the lifted
3824    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
3825    /// shared parser-shaped reason into the
3826    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
3827    /// diagnostic is self-locating (the offending `:nome` is named
3828    /// verbatim alongside the rendered chart name and the budget) and
3829    /// the author can shorten in one edit. The gate runs across every
3830    /// `:kind` — `:nome` is the substrate-wide identity axis any
3831    /// future renderer the substrate adds can derive a
3832    /// `lareira-<nome>` artifact from, and uniform enforcement closes
3833    /// the drift footgun where a future kind grows a chart-emitting
3834    /// render path while the validate cascade doesn't catch it.
3835    ///
3836    /// Runs *after* [`Self::validate_nome`] so the narrower
3837    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
3838    /// structurally-malformed `:nome` (empty, uppercase, underscore,
3839    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
3840    /// specific shape error rather than the chart-name-budget error,
3841    /// preserving the legitimate "well-shaped `:nome` that happens to
3842    /// overflow the joint cap" arm for this gate.
3843    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
3844        let nome = self.nome();
3845        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
3846            ManifestError::NomeChartNameBudgetExceeded {
3847                nome: nome.to_string(),
3848                reason,
3849            }
3850        })
3851    }
3852
3853    /// Reject `:versao` values that don't parse as [`semver::Version`].
3854    /// The top-level Caixa version flows directly into every
3855    /// substrate-side artifact that carries a "this is which version of
3856    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
3857    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
3858    /// SemVer-2-strict at `helm template` / `helm install` time per
3859    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
3860    /// `feira publish` Zig-style `v<versao>` git tag
3861    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
3862    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
3863    /// `versao:` value the `lareira-fleet-programs` aggregator carries
3864    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
3865    /// `:latest` tags the substrate's `wasi-service-flake` builds with
3866    /// `skopeo push`, the lacre closure's pinned versions
3867    /// ([`caixa-resolver`] keys `concrete_versao`), and the
3868    /// `:upgrade-from :from` references peers in this exact `versao`
3869    /// shape (`semver::Version`, not `VersionReq`). Each consumer
3870    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
3871    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
3872    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
3873    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
3874    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
3875    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
3876    /// into the version field a peer `:deps :versao` accepts;
3877    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
3878    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
3879    /// derive macro stores the raw String) and the failure surfaced at
3880    /// the *first* downstream consumer that strict-parses it: at
3881    /// `helm install` time as a chart-version rejection, at
3882    /// `feira publish` time as a malformed git tag, at lacre-resolve
3883    /// time as a `semver::Error` not naming the offending caixa, at
3884    /// `feira upgrade --to <versao>` time as an unresolvable
3885    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
3886    /// and without any field naming the offending `:versao`.
3887    ///
3888    /// Thin wrapper around [`semver::Version::parse`] — the same parser
3889    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
3890    /// and [`crate::UpgradeFromEntry::validate`] (the peer
3891    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
3892    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
3893    /// variant, carrying the offending `:versao` verbatim + a
3894    /// parser-shaped reason naming the specific violation, so the
3895    /// diagnostic is self-locating (the author can grep their
3896    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
3897    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
3898    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
3899    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
3900    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
3901    /// now structurally equivalent (every value past validate is
3902    /// round-trippable through [`semver::Version::parse`] without
3903    /// re-checking at the renderer, resolver, or operator hot-upgrade
3904    /// layer), peer with the four `:versao` requirement axes (`:deps`,
3905    /// `:deps-dev`, `:membros`, `:children`) the prior commits
3906    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
3907    ///
3908    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
3909    /// the derive macro stores the raw String) is gated by the
3910    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
3911    /// consulted, mirroring the empty-first cascade every per-axis
3912    /// version gate already uses (e.g. `MembroVersaoEmpty` before
3913    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
3914    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
3915    pub fn validate_versao(&self) -> Result<(), ManifestError> {
3916        let versao = self.versao();
3917        if versao.is_empty() {
3918            return Err(ManifestError::VersaoEmpty);
3919        }
3920        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
3921            versao: versao.to_string(),
3922            reason: e.to_string(),
3923        })?;
3924        Ok(())
3925    }
3926
3927    /// Reject `:restart-window` values the shared
3928    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
3929    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
3930    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
3931    /// `Option<Duration>` routed through the shared codec via `with =
3932    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
3933    /// view-construction path ([`Self::supervisor_view`]) folds the
3934    /// raw string through the same shared codec and soft-swallows the
3935    /// parse error as `None` to keep the view best-effort. Without
3936    /// this gate a malformed `:restart-window` (`"1.5s"` — the
3937    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
3938    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
3939    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
3940    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
3941    /// edge case) silently produced a `SupervisorSpec` with
3942    /// `restart_window: None`, indistinguishable from the canonical
3943    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
3944    /// `MaxIntensity / Period` invariant turns into a never-reset
3945    /// supervisor far from the source `caixa.lisp`, with no field
3946    /// naming the offending `:restart-window`. Lifting the gate to a
3947    /// Caixa-level validator mirrors the trajectory of the peer
3948    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
3949    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
3950    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
3951    /// (line 196: "reject invalid `:restart-window` (non-duration)").
3952    ///
3953    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
3954    /// (the shared codec backing `:supervisor :restart-window` as
3955    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
3956    /// `:politicas :circuit-breaker :window` — all three covered by
3957    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
3958    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
3959    /// variant, carrying the offending raw string + a parser-shaped
3960    /// reason naming the canonical authoring form, so the diagnostic
3961    /// is self-locating (the author can grep their `caixa.lisp` for
3962    /// `:restart-window "<value>"` and fix it in one edit) and
3963    /// uniform with every other manifest-level validate diagnostic.
3964    /// With this gate the four `:restart-window`-shaped surfaces (the
3965    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
3966    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
3967    /// now structurally equivalent — every value past the codec is in
3968    /// one accepted set, by construction.
3969    ///
3970    /// `None` (the canonical "omit the slot to express no reset"
3971    /// shape) is accepted trivially — the gate is a no-op when the
3972    /// author didn't author a window. The empty string is rejected by
3973    /// the shared codec (its digit-only gate refuses an empty
3974    /// magnitude), surfacing the same `RestartWindowMalformed`
3975    /// diagnostic as every other rejected non-canonical shape.
3976    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
3977        let Some(s) = self.restart_window() else {
3978            return Ok(());
3979        };
3980        crate::supervisor::duration_codec::parse(s)
3981            .map(|_| ())
3982            .map_err(|reason| ManifestError::RestartWindowMalformed {
3983                restart_window: s.to_string(),
3984                reason,
3985            })
3986    }
3987
3988    /// Reject per-entry values on the three Caixa-level code-surface
3989    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
3990    /// layout checker's `root.join(p)` sandbox would silently subvert.
3991    /// Same three structural footguns the peer
3992    /// [`BehaviorSpec::validate`] (b0c8389) and
3993    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
3994    /// (26da2c7) already close on the M2 `:behavior :on-*` and
3995    /// `:upgrade-from :state-change :script` axes, here lifted onto
3996    /// the three top-level code-path axes through the shared
3997    /// [`is_sandboxed_relative_path`] predicate:
3998    ///
3999    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
4000    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
4001    ///     [`Path::join`] as the base itself — `root.join("")` ==
4002    ///     `root`, so the existence check (`self.exists(&root)`)
4003    ///     trivially passes (the project root exists), and the layout
4004    ///     silently treats the project root as a biblioteca / exe /
4005    ///     servico entry. The `:bibliotecas` loop then hands the root
4006    ///     to `tatara_lisp::read` at `feira build` time as if the root
4007    ///     directory itself were a Lisp source file — a parse error
4008    ///     far from the source `caixa.lisp` with no field naming the
4009    ///     offending entry.
4010    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
4011    ///     [`Path::join`] *replaces* the base when the right-hand side
4012    ///     is absolute, so `root.join("/etc/passwd")` resolves to
4013    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
4014    ///     The existence check then silently consults whatever the
4015    ///     escaped path resolves to — for `:bibliotecas`, the layout
4016    ///     has no `starts_with`-fence (only `:exe` is fenced under
4017    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
4018    ///     `:bibliotecas` entry that happens to resolve on disk
4019    ///     silently passes. For `:exe` / `:servicos` the fence catches
4020    ///     the absolute case downstream as `ExeOutsideDir` /
4021    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
4022    ///     doesn't exist), but with a downstream-shaped diagnostic
4023    ///     that names the resolved escape path rather than the
4024    ///     authoring footgun at the source.
4025    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
4026    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
4027    ///     [`std::path::Component::ParentDir`] anywhere round-trips
4028    ///     through [`Path::join`] as a traversal above the caixa root.
4029    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
4030    ///     *component-aware* (not canonical-path-aware), so
4031    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
4032    ///     is **true** even though the canonical resolution
4033    ///     `{parent of root}/escape.lisp` lives outside the caixa root
4034    ///     — the fence silently lets the parent-escape through, and
4035    ///     the existence check passes if that escape-target happens
4036    ///     to exist. Caught regardless of where the `..` sits
4037    ///     (leading, mid-path, trailing) so the gate matches the peer
4038    ///     predicate's full coverage.
4039    ///
4040    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
4041    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
4042    /// same per-slot diagnostic shape every peer per-axis path-gate
4043    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
4044    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
4045    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
4046    /// order [`Caixa::declared_foreign_code_slots`] uses for its
4047    /// canonical foreign-code-slot diagnostic, so a manifest with
4048    /// multiple malformed slots surfaces the lexicographically-earliest
4049    /// slot's diagnostic deterministically.
4050    ///
4051    /// Lifted to the typed surface as a Caixa-level validator (peer
4052    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
4053    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
4054    /// and wired into [`crate::StandardLayout::verify`] before the
4055    /// existence-check loops so the diagnostic names the offending
4056    /// slot at the source caixa.lisp rather than reporting a
4057    /// downstream `MissingEntry` / `ExeOutsideDir` /
4058    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
4059    /// The fourth typed code-path surface — every author-supplied
4060    /// path on the manifest — is now structurally accept-shaped
4061    /// past validate, peer with `:behavior :on-*` and
4062    /// `:upgrade-from :state-change :script`.
4063    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
4064        /// Per-slot file-type contract for the three Caixa-level
4065        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
4066        /// Each variant names the predicate the per-entry file-type
4067        /// gate consults; [`Self::None`] opts the slot out of any
4068        /// file-type contract. Lifted as a typed local enum so the
4069        /// per-slot dispatch is exhaustive at the `match` — adding a
4070        /// future axis to the typed-substrate `:` slot set (the
4071        /// future `:assets` resource axis the M5 roadmap names, the
4072        /// future `:nix-flake` derivation axis the caixa-flake
4073        /// emitter consults) lands as one variant + one `match` arm,
4074        /// not a coordinated rewrite of every per-slot bool flag.
4075        ///
4076        /// Peer of the typed-substrate per-slot variant disciplines
4077        /// already established on this surface
4078        /// ([`crate::supervisor::RestartStrategy`] +
4079        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
4080        /// supervision-tree axis,
4081        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
4082        /// placement axis, [`crate::aplicacao::WitTarget`] on the
4083        /// `:contratos` payload-target axis): the typed `enum` is
4084        /// the substrate's single source of truth for the per-axis
4085        /// dispatch, and every consumer (the per-arm body here, the
4086        /// future feira-lint per-slot diagnostic renderer, the M4
4087        /// per-axis admission webhook) reaches for the same typed
4088        /// surface rather than re-deriving the partition from inline
4089        /// flag combinations.
4090        enum CodePathFileType {
4091            /// `:exe` — nix-build derivation output, no terminating-
4092            /// extension contract (the canonical `"exe/<name>"`
4093            /// fixtures the layout's `ExeOutsideDir` error message
4094            /// documents carry no extension by convention).
4095            None,
4096            /// `:bibliotecas` — tatara-lisp source files the
4097            /// `feira build` loop reads through `tatara_lisp::read`
4098            /// at parse time. Routes to [`is_lisp_extension`].
4099            LispSource,
4100            /// `:servicos` — ComputeUnit-CR YAML files the
4101            /// caixa-helm / caixa-flux renderers consume through
4102            /// `serde_yaml::from_str`. Routes to
4103            /// [`is_computeunit_yaml_extension`].
4104            ComputeUnitYaml,
4105        }
4106
4107        // The per-slot [`CodePathFileType`] selects which axes carry the
4108        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
4109        // source axis (the `feira build` loop at
4110        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
4111        // `tatara_lisp::read` at parse time) — the lifted
4112        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
4113        // `:exe` is the nix-built executable surface (per the canonical
4114        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
4115        // error message documents and every in-tree
4116        // `caixa_with_code_paths` positive control uses) — its file-type
4117        // contract is "nix-build derivation output", not a typed source
4118        // file, so [`CodePathFileType::None`] opts the slot out of any
4119        // file-type gate. `:servicos` is the `.computeunit.yaml`
4120        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
4121        // renderers consume each entry through `serde_yaml::from_str` as
4122        // a typed `ComputeUnit` CR) — the lifted
4123        // [`is_computeunit_yaml_extension`] predicate gates the compound
4124        // `.computeunit.yaml` suffix. All three axes are surfaced through
4125        // the same iteration so the sandbox-shape + duplicate gates
4126        // apply uniformly; the typed file-type dispatch fires per-slot
4127        // exactly where the downstream consumer's accepted set demands
4128        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
4129        // compounding lift on the peer 64772a9 `:bibliotecas`
4130        // `.lisp`-gate trajectory — the second of the three code-path
4131        // axes to land on a typed compound-suffix gate, with the same
4132        // self-locating per-slot diagnostic shape every peer per-axis
4133        // file-type lift uses (`*NonLispExtension { slot, path }` /
4134        // `*NonComputeUnitYamlExtension { slot, path }`).
4135        for (slot, list, file_type) in [
4136            (
4137                ":bibliotecas",
4138                &self.bibliotecas,
4139                CodePathFileType::LispSource,
4140            ),
4141            (":exe", &self.exe, CodePathFileType::None),
4142            (
4143                ":servicos",
4144                &self.servicos,
4145                CodePathFileType::ComputeUnitYaml,
4146            ),
4147        ] {
4148            // Per-slot set-not-multiset gate on the typed code-path axis.
4149            // Every peer Vec-shaped author-supplied list past validate is
4150            // a set, not a multiset: `:membros :caixa`
4151            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
4152            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
4153            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
4154            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
4155            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
4156            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
4157            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
4158            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
4159            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
4160            // the three code-path lists are the last Vec-shaped author-
4161            // supplied slots on the typed Caixa surface still admitting a
4162            // duplicate entry silently. Scope is per-list (`:bibliotecas`
4163            // duplicates are flagged within `:bibliotecas`, not across
4164            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
4165            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
4166            // legitimate dev-vs-runtime shape on the dep axis, fenced
4167            // separately by [`crate::dep::validate_no_self_dep`]). On the
4168            // code-path axis a cross-slot collision is structurally
4169            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
4170            // fence — `:exe` and `:servicos` entries are confined to their
4171            // own directory trees, so the only way a string could appear
4172            // on two code-path lists is the (rare, structurally invalid)
4173            // case where `:bibliotecas` carries an `"exe/<x>"` or
4174            // `"servicos/<x>.yaml"`-shaped path.
4175            //
4176            // Without the gate three authoring footguns silently passed:
4177            //
4178            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
4179            //     canonical copy-paste-the-wrong-file footgun. `feira
4180            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
4181            //     list and re-parses the same file twice, wasting work
4182            //     and silently masking the author's intent to declare a
4183            //     *second* biblioteca.
4184            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
4185            //     Binario surface. The future `caixa-flake` `nix flake`
4186            //     emitter that materializes each `:exe` entry as a flake
4187            //     `packages.<exe-name>` derivation would collide on the
4188            //     duplicate package name and surface a flake-eval error
4189            //     far from the source `caixa.lisp`.
4190            //   - `:servicos ("servicos/x.computeunit.yaml"
4191            //     "servicos/x.computeunit.yaml")` — the same footgun on
4192            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
4193            //     renderers already refuse `:servicos.len() != 1` with
4194            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
4195            //     that diagnostic surfaces "too many servicos" without
4196            //     naming "duplicate entry" — the typed self-locating
4197            //     "which entry is the duplicate" framing only lands at
4198            //     this gate.
4199            //
4200            // Same `seen.insert(entry.as_str())` shape every peer per-list
4201            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
4202            // 86c769b, `:deps` 359fba5) and the same "structural shape
4203            // checks fire before the duplicate check on the same entry"
4204            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
4205            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
4206            // empty entry first, not the duplicate on the later pair).
4207            let mut seen = std::collections::HashSet::new();
4208            for entry in list {
4209                let path = Path::new(entry);
4210                match is_sandboxed_relative_path(path) {
4211                    Ok(()) => {}
4212                    Err(PathShapeViolation::Empty) => {
4213                        return Err(ManifestError::CodePathEmpty { slot });
4214                    }
4215                    Err(PathShapeViolation::Absolute) => {
4216                        return Err(ManifestError::CodePathAbsolute {
4217                            slot,
4218                            path: path.to_path_buf(),
4219                        });
4220                    }
4221                    Err(PathShapeViolation::ParentEscape) => {
4222                        return Err(ManifestError::CodePathParentEscape {
4223                            slot,
4224                            path: path.to_path_buf(),
4225                        });
4226                    }
4227                }
4228                // The per-slot file-type gate dispatched through the
4229                // typed [`CodePathFileType`] selector above. Each variant
4230                // routes to the lifted predicate the downstream consumer
4231                // demands:
4232                //
4233                //   - [`LispSource`] → [`is_lisp_extension`] for
4234                //     `:bibliotecas` (the `feira build` loop's
4235                //     `tatara_lisp::read` consumer);
4236                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
4237                //     for `:servicos` (the caixa-helm / caixa-flux
4238                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
4239                //     accepted set);
4240                //   - [`None`] for `:exe` — the nix-build derivation-
4241                //     output axis has no terminating-extension contract.
4242                //
4243                // Fires after the sandbox-shape arms so a path that is
4244                // *both* sandbox-escaping and wrong-extension surfaces
4245                // the more fundamental sandbox-shape diagnostic first
4246                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
4247                // `ParentEscape` → `NonLispExtension` arm-ordering on
4248                // `:behavior :on-*` c97815a, and `EmptyScript` →
4249                // `AbsoluteScript` → `ParentEscapeScript` →
4250                // `NonLispExtensionScript` on
4251                // `:upgrade-from :state-change :script` 33cc830), and
4252                // before the duplicate gate so the narrower per-entry
4253                // file-type shape dominates the cross-entry uniqueness
4254                // diagnostic (a
4255                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
4256                // `:servicos` surfaces
4257                // `CodePathNonComputeUnitYamlExtension` on the first
4258                // entry rather than `CodePathDuplicate` on the pair —
4259                // peer with the 64772a9 `:bibliotecas`
4260                // `("lib/x.txt" "lib/x.txt")` ordering).
4261                match file_type {
4262                    CodePathFileType::None => {}
4263                    CodePathFileType::LispSource => {
4264                        if !is_lisp_extension(path) {
4265                            return Err(ManifestError::CodePathNonLispExtension {
4266                                slot,
4267                                path: path.to_path_buf(),
4268                            });
4269                        }
4270                    }
4271                    CodePathFileType::ComputeUnitYaml => {
4272                        if !is_computeunit_yaml_extension(path) {
4273                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
4274                                slot,
4275                                path: path.to_path_buf(),
4276                            });
4277                        }
4278                    }
4279                }
4280                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
4281                    ManifestError::CodePathDuplicate {
4282                        slot,
4283                        path: path.to_path_buf(),
4284                    }
4285                })?;
4286            }
4287        }
4288        Ok(())
4289    }
4290
4291    /// Reject `:etiquetas` lists with an empty entry or with two entries
4292    /// agreeing on the same string. `:etiquetas` is the universal
4293    /// registry-search-tag axis on [`Caixa`] (every kind carries the
4294    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4295    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
4296    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
4297    /// a [`std::collections::BTreeSet`] alongside the four substrate-
4298    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
4299    /// Two authoring footguns silently passed validate without this gate:
4300    ///
4301    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
4302    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
4303    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
4304    ///     `chart.metadata.keywords` admits the value without a strict
4305    ///     parser-side gate, but the empty keyword has no operational
4306    ///     meaning — it indexes nothing in the future caixa-registry
4307    ///     search axis and clutters the rendered chart with a no-op tag.
4308    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
4309    ///     copy-paste-the-wrong-tag footgun) silently passed validate
4310    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
4311    ///     at chart render — a "second wins / one silently disappears"
4312    ///     shape divergent from every peer typed-graph set gate
4313    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
4314    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
4315    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4316    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4317    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4318    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
4319    ///     on `:upgrade-from`, the per-instruction-class singularity
4320    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
4321    ///     [`crate::UpgradeError::DuplicateStateChange`] /
4322    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
4323    ///     discipline is uniform: every Vec-shaped author-supplied list
4324    ///     past validate is set-not-multiset, by construction.
4325    ///
4326    /// Past the empty arm the gate enforces the chart-keyword shape
4327    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
4328    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
4329    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
4330    /// continuation. Closes the canonical paste-from-doc footguns the
4331    /// bare empty + duplicate arms left open: paste-from-aligned-doc
4332    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
4333    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
4334    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
4335    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
4336    /// — the author meant three separate list entries), path-separator
4337    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
4338    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
4339    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
4340    /// control bytes that would silently land as malformed search tags
4341    /// in the rendered Chart.yaml `keywords:` array and break the
4342    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
4343    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
4344    /// established on the sibling universal-axis `Vec<String>` surface
4345    /// — the second universal-axis Vec<String> surface to land the
4346    /// empty-first-then-shape-then-duplicate per-entry cascade.
4347    ///
4348    /// Same empty-first cascade discipline every peer per-axis gate
4349    /// uses: the per-entry empty arm fires before the per-entry shape
4350    /// arm fires before the cross-entry duplicate arm, so an
4351    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
4352    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
4353    /// has no value" defect) before either the shape or the duplicate
4354    /// diagnostic. Walks the list in declaration order so the
4355    /// first-collision diagnostic surfaces the lexicographically-
4356    /// earliest offending position, peer with every other duplicate
4357    /// gate on this surface.
4358    ///
4359    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
4360    /// caixa-build gate alongside the peer universal gates
4361    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4362    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
4363    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
4364    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4365    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4366    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4367    /// slot sets. The future caixa-registry search axis can reach for
4368    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
4369    /// chart-keyword-shaped string without re-deriving the precondition.
4370    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
4371        let mut seen = std::collections::HashSet::new();
4372        for etiqueta in self.etiquetas() {
4373            if etiqueta.is_empty() {
4374                return Err(ManifestError::EtiquetaEmpty);
4375            }
4376            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
4377                ManifestError::EtiquetaInvalid {
4378                    etiqueta: etiqueta.clone(),
4379                    reason,
4380                }
4381            })?;
4382            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
4383                ManifestError::EtiquetaDuplicate {
4384                    etiqueta: etiqueta.clone(),
4385                }
4386            })?;
4387        }
4388        Ok(())
4389    }
4390
4391    /// Reject `:autores` lists with an empty entry or with two entries
4392    /// agreeing on the same string. `:autores` is the universal
4393    /// maintainer-axis on [`Caixa`] (every kind carries the
4394    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4395    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
4396    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
4397    /// to a `Maintainer { name, email: None }` without dedup). Two
4398    /// authoring footguns silently passed validate without this gate:
4399    ///
4400    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
4401    ///     blank-doc footgun) rendered as
4402    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
4403    ///     empty maintainer name has no operational meaning — it
4404    ///     identifies no one in the substrate's authorship index and
4405    ///     clutters the rendered chart with a no-op maintainer.
4406    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
4407    ///     the copy-paste-the-wrong-author footgun) silently passed
4408    ///     validate and rendered as two identical maintainer entries.
4409    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
4410    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
4411    ///     rendered `keywords:` array at chart-render time), the
4412    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
4413    ///     entries stack verbatim in the chart, divergent from every
4414    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
4415    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
4416    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4417    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4418    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4419    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
4420    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
4421    ///     `:etiquetas`).
4422    ///
4423    /// Past the empty arm the gate enforces the chart-maintainer-name
4424    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
4425    /// the structural single-line printable-UTF-8 floor every realistic
4426    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
4427    /// or trailing whitespace, no ASCII control characters anywhere,
4428    /// Unicode bytes accepted. Closes the canonical paste-from-doc
4429    /// footguns the bare empty + duplicate arms left open:
4430    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
4431    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
4432    /// pasted a multi-line block of author records into one `:autores`
4433    /// entry instead of splitting into one entry per author),
4434    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
4435    /// and the paste-from-binary-blob control bytes that would silently
4436    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
4437    /// `maintainers:` array. Mirrors the shape-predicate cascade
4438    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4439    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
4440    /// establish past their own empty arms on the sibling universal-axis
4441    /// `Option<String>` surfaces — the first universal-axis Vec<String>
4442    /// surface to land the empty-first-then-shape-then-duplicate per-entry
4443    /// cascade.
4444    ///
4445    /// Same empty-first cascade discipline every peer per-axis gate
4446    /// uses: the per-entry empty arm fires before the per-entry shape
4447    /// arm before the cross-entry duplicate arm. Walks the list in
4448    /// declaration order so the first-collision diagnostic surfaces the
4449    /// lexicographically-earliest offending position, peer with every
4450    /// other duplicate gate on this surface.
4451    ///
4452    /// Universal-axis (every kind carries `:autores`), so wired at the
4453    /// caixa-build gate alongside the peer universal gates
4454    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4455    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4456    /// [`Self::validate_code_paths`] — before the kind-coherence gates
4457    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4458    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4459    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4460    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4461    /// slot sets.
4462    pub fn validate_autores(&self) -> Result<(), ManifestError> {
4463        let mut seen = std::collections::HashSet::new();
4464        for autor in self.autores() {
4465            if autor.is_empty() {
4466                return Err(ManifestError::AutorEmpty);
4467            }
4468            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
4469                ManifestError::AutorInvalid {
4470                    autor: autor.clone(),
4471                    reason,
4472                }
4473            })?;
4474            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
4475                ManifestError::AutorDuplicate {
4476                    autor: autor.clone(),
4477                }
4478            })?;
4479        }
4480        Ok(())
4481    }
4482
4483    /// Reject `:repositorio` values whose shape the shared
4484    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
4485    /// `repositorio: Option<String>` slot on [`Caixa`] is the
4486    /// universal git-shaped homepage axis every kind carries — the
4487    /// substrate routes the same string through two load-bearing
4488    /// consumers:
4489    ///
4490    ///   - [`caixa-helm`] folds it verbatim into the rendered
4491    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
4492    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
4493    ///     the chart `README.md` `repo = …` interpolation
4494    ///     (`caixa-helm/src/lib.rs:359`).
4495    ///   - [`caixa-flux`] folds it verbatim into the standalone
4496    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
4497    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
4498    ///     `GitRepository.spec.url` the cluster's source-controller
4499    ///     polls — the load-bearing deploy-time axis.
4500    ///
4501    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4502    /// substitute a placeholder when the slot is absent (`None` → the
4503    /// fallback fires); a `Some("")` *skips the fallback* and silently
4504    /// passes the empty string through to `Chart.yaml home: ""` /
4505    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
4506    /// controller both reject the empty URL far from the source
4507    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
4508    /// Similarly a malformed `:repositorio` (whitespace, control char,
4509    /// missing `:` separator, leading `-`) silently lands in the
4510    /// rendered artifacts and breaks at `git clone` / `helm template`
4511    /// / `flux reconcile` time.
4512    ///
4513    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
4514    /// same shared predicate the peer [`crate::DepSource::validate`]
4515    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
4516    /// gate the two `git URL`-shaped surfaces on the typed Caixa
4517    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
4518    /// structurally equivalent: every value past validate is
4519    /// guaranteed-acceptable by the predicate's union of constraints
4520    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
4521    /// control chars, ASCII only, no leading `:`, contains a `:`
4522    /// separator). The predicate accepts every documented authoring
4523    /// shape — `github:org/repo` shorthand, `https://host/path`,
4524    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
4525    /// scp-style SSH, `file:///path` — and refuses the canonical
4526    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
4527    /// injection footguns at validate time. Maps the predicate's
4528    /// `String` reason verbatim into the
4529    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
4530    /// offending value + parser-shaped reason so the diagnostic is
4531    /// self-locating (the author can grep their `caixa.lisp` for
4532    /// `:repositorio "<value>"` and fix it in one edit).
4533    ///
4534    /// `None` (the canonical "omit the slot to express no published
4535    /// homepage" shape) is accepted trivially — the gate is a no-op
4536    /// when the author didn't declare a value. `Some("")` is gated by
4537    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
4538    /// shape predicate is consulted, mirroring the empty-first cascade
4539    /// every peer per-axis identity gate uses
4540    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
4541    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
4542    /// [`crate::DepError::FonteRepoEmpty`] →
4543    /// [`crate::DepError::FonteRepoInvalid`]).
4544    ///
4545    /// Universal-axis (every kind carries `:repositorio`), so wired at
4546    /// the caixa-build gate alongside the peer universal gates
4547    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4548    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4549    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
4550    /// before the kind-coherence gates
4551    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4552    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4553    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4554    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4555    /// specific slot sets.
4556    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
4557        let Some(s) = self.repositorio() else {
4558            return Ok(());
4559        };
4560        if s.is_empty() {
4561            return Err(ManifestError::RepositorioEmpty);
4562        }
4563        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
4564            repositorio: s.to_string(),
4565            reason,
4566        })
4567    }
4568
4569    /// Reject `:descricao` values that are the empty string. The flat
4570    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
4571    /// free-form-prose homepage axis every kind carries — the
4572    /// substrate routes the same string through two load-bearing
4573    /// consumers in the [`caixa-helm`] renderer:
4574    ///
4575    ///   - `build_chart_yaml` folds it verbatim into the rendered
4576    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
4577    ///     field (`caixa-helm/src/lib.rs:232-235`).
4578    ///   - `build_readme` folds it verbatim into the rendered chart
4579    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
4580    ///
4581    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4582    /// substitute a `caixa.nome`-derived placeholder when the slot is
4583    /// absent (`None` → the fallback fires); a `Some("")` *skips the
4584    /// fallback* and silently passes the empty string through to
4585    /// `Chart.yaml description: ""` / a blank chart `README.md`
4586    /// header. Helm's chart spec requires a non-empty `description:`
4587    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
4588    /// `WARNING [chart.metadata.description]: description is required`),
4589    /// so the empty `Some("")` silently lands in the rendered
4590    /// artifacts and breaks at `helm lint` / `helm install` time far
4591    /// from the source `caixa.lisp`, with no field naming the
4592    /// offending `:descricao`.
4593    ///
4594    /// `None` (the canonical "omit the slot to defer to the renderer's
4595    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
4596    /// the gate is a no-op when the author didn't declare a value.
4597    /// `Some("")` is gated by the narrower
4598    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
4599    /// shape every peer per-axis empty gate uses
4600    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4601    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4602    /// [`ManifestError::RepositorioEmpty`]).
4603    ///
4604    /// Universal-axis (every kind carries `:descricao`), so wired at
4605    /// the caixa-build gate alongside the peer universal gates
4606    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4607    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4608    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4609    /// [`Self::validate_code_paths`] — before the kind-coherence
4610    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4611    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4612    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4613    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4614    /// specific slot sets.
4615    ///
4616    /// Past the empty arm the gate enforces the chart-description
4617    /// shape predicate via [`crate::render::is_chart_description_shape`]:
4618    /// the structural single-line UTF-8 floor every realistic chart
4619    /// description in the wild matches — 1..=512 bytes, no leading
4620    /// or trailing whitespace, no ASCII control characters anywhere
4621    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
4622    /// carriage return, and every other control byte), Unicode
4623    /// continuation bytes accepted (the canonical fixtures carry
4624    /// `→` and `—`). Closes the canonical paste-from-doc footguns
4625    /// the bare empty-arm gate left open: paste-from-aligned-doc
4626    /// leading / trailing whitespace (`" Checkout flow."`,
4627    /// `"Checkout flow. "`), paste-from-multiline-doc newline
4628    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
4629    /// (`"Checkout\rflow."`), tab-from-aligned-doc
4630    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
4631    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
4632    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
4633    /// [`Self::validate_edicao`] establish past their own empty arms
4634    /// on the sibling universal-axis `Option<String>` Caixa-level
4635    /// value-shape surfaces.
4636    ///
4637    /// The empty-first cascade discipline mirrors every peer per-axis
4638    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
4639    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
4640    /// diagnostic surfaces on `Some("")` rather than the broader
4641    /// shape-predicate diagnostic — peer with how
4642    /// [`ManifestError::LicencaEmpty`] runs before
4643    /// [`ManifestError::LicencaInvalid`],
4644    /// [`ManifestError::EdicaoEmpty`] runs before
4645    /// [`ManifestError::EdicaoInvalid`],
4646    /// [`ManifestError::RepositorioEmpty`] runs before
4647    /// [`ManifestError::RepositorioInvalid`].
4648    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
4649        let Some(s) = self.descricao() else {
4650            return Ok(());
4651        };
4652        if s.is_empty() {
4653            return Err(ManifestError::DescricaoEmpty);
4654        }
4655        crate::render::is_chart_description_shape(s).map_err(|reason| {
4656            ManifestError::DescricaoInvalid {
4657                descricao: s.to_string(),
4658                reason,
4659            }
4660        })?;
4661        Ok(())
4662    }
4663
4664    /// Reject `:licenca` values that are the empty string. The flat
4665    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
4666    /// SPDX-shaped license-expression axis every kind carries — the
4667    /// substrate routes the same string through the [`caixa-helm`]
4668    /// renderer's `build_readme` which folds it verbatim into the
4669    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
4670    /// section (`caixa-helm/src/lib.rs:361`) via
4671    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
4672    /// fallback only fires on `None`; a `Some("")` *skips the
4673    /// fallback* and silently passes the empty string through to a
4674    /// chart `README.md` whose `License` section renders as the bare
4675    /// trailing period (`.\n`) — peer footgun with the
4676    /// `Some("")`-skips-`unwrap_or_else` shape the
4677    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
4678    /// gates close on the sibling free-form-prose and git-URL axes.
4679    ///
4680    /// `None` (the canonical "omit the slot to defer to the
4681    /// renderer's `MIT` fallback" shape every existing fixture
4682    /// carries) is accepted trivially — the gate is a no-op when the
4683    /// author didn't declare a value. `Some("")` is gated by the
4684    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
4685    /// empty-arm shape every peer per-axis empty gate uses
4686    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4687    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4688    /// [`ManifestError::RepositorioEmpty`],
4689    /// [`ManifestError::DescricaoEmpty`]).
4690    ///
4691    /// Universal-axis (every kind carries `:licenca`), so wired at
4692    /// the caixa-build gate alongside the peer universal gates
4693    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4694    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4695    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4696    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
4697    /// — before the kind-coherence gates
4698    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4699    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4700    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4701    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4702    /// specific slot sets.
4703    ///
4704    /// Past the empty arm the gate enforces the SPDX-expression shape
4705    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
4706    /// structural alphabet floor every realistic SPDX expression in
4707    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
4708    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
4709    /// single ASCII space (token separator). Closes the canonical
4710    /// paste-from-doc footguns the bare empty-arm gate left open:
4711    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
4712    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
4713    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
4714    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
4715    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
4716    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
4717    /// Apache-2.0"`), and semicolon-list-separator confusion
4718    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
4719    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
4720    /// establish past their own empty arms.
4721    ///
4722    /// The empty-first cascade discipline mirrors every peer per-axis
4723    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
4724    /// [`ManifestError::LicencaInvalid`], so the narrower empty
4725    /// diagnostic surfaces on `Some("")` rather than the broader
4726    /// shape-predicate diagnostic — peer with how
4727    /// [`ManifestError::EdicaoEmpty`] runs before
4728    /// [`ManifestError::EdicaoInvalid`],
4729    /// [`ManifestError::RepositorioEmpty`] runs before
4730    /// [`ManifestError::RepositorioInvalid`].
4731    ///
4732    /// A future tightening on this axis can extend the alphabet
4733    /// floor into a full SPDX expression parser + license-id
4734    /// allowlist (rejecting alphabet-valid values that don't name a
4735    /// real SPDX license identifier — e.g., `"NotAReal"` is
4736    /// alphabet-valid but no `NotAReal` license-id exists). That
4737    /// parser only becomes meaningful past a real SPDX-spec
4738    /// dependency; this gate establishes the structural floor by
4739    /// refusing every non-SPDX-alphabet value at validate time.
4740    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
4741        let Some(s) = self.licenca() else {
4742            return Ok(());
4743        };
4744        if s.is_empty() {
4745            return Err(ManifestError::LicencaEmpty);
4746        }
4747        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
4748            ManifestError::LicencaInvalid {
4749                licenca: s.to_string(),
4750                reason,
4751            }
4752        })?;
4753        Ok(())
4754    }
4755
4756    /// Reject `:edicao` values that are the empty string. The flat
4757    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
4758    /// language-edition axis every kind carries — it determines the
4759    /// tatara-lisp macro surface + compatibility flags the substrate
4760    /// applies when building a caixa, and lands verbatim in the
4761    /// `Caixa::template` author-time scaffold (the canonical
4762    /// `:edicao "2026"` line every `feira init` emits via
4763    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
4764    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
4765    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
4766    /// `caixa-core/src/render.rs:2510`) via
4767    /// `edicao: Some("2026".into())`.
4768    ///
4769    /// `None` (the canonical "omit the slot to defer to the
4770    /// substrate's default edition" shape every existing
4771    /// [`caixa-resolver`] integration test fixture carries via
4772    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
4773    /// is accepted trivially — the gate is a no-op when the author
4774    /// didn't declare a value. `Some("")` is gated by the narrower
4775    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
4776    /// shape every peer per-axis empty gate uses
4777    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4778    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4779    /// [`ManifestError::RepositorioEmpty`],
4780    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
4781    ///
4782    /// Universal-axis (every kind carries `:edicao`), so wired at
4783    /// the caixa-build gate alongside the peer universal gates
4784    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4785    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4786    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4787    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4788    /// [`Self::validate_code_paths`] — before the kind-coherence
4789    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4790    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4791    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4792    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4793    /// specific slot sets.
4794    ///
4795    /// Past the empty arm the gate enforces the canonical year-shape
4796    /// predicate: every documented tatara-lisp edition is a 4-digit
4797    /// ASCII decimal year (`"2026"` is the only edition currently
4798    /// minted; future-introduced siblings will follow the same
4799    /// shape, peer with Cargo's `[package] edition` grammar which
4800    /// every value Cargo has ever accepted matches — `"2015"`,
4801    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
4802    /// 4 ASCII decimal bytes is rejected with the narrower
4803    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
4804    /// shape-predicate cascade [`Self::validate_repositorio`]
4805    /// establishes past its own empty arm
4806    /// ([`ManifestError::RepositorioEmpty`] →
4807    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
4808    /// paste-from-doc footguns the bare empty-arm gate left open:
4809    ///
4810    ///   - leading / trailing whitespace from a paste-from-doc
4811    ///     (`"2026 "`, `" 2026"`)
4812    ///   - control characters / CRLF from a paste-from-multiline-doc
4813    ///     (`"2026\n"`)
4814    ///   - non-ASCII look-alikes from a fullwidth keyboard
4815    ///     (`"2026"`) which would silently land as a non-ASCII
4816    ///     string in the rendered caixa.lisp
4817    ///   - free-form non-year values (`"x"`, `"latest"`,
4818    ///     `"nightly"`) that have no operational meaning on the
4819    ///     substrate's build-time edition selector
4820    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
4821    ///     `"r2026"`) — common version-tag idioms that don't apply
4822    ///     to the year-shaped edition axis
4823    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
4824    ///     edition is a year, not a fractional version
4825    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
4826    ///     `"00026"`) that don't name a year
4827    ///
4828    /// `None` (the canonical "omit the slot to defer to the
4829    /// substrate's default edition" shape every existing
4830    /// [`caixa-resolver`] integration test fixture carries via
4831    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
4832    /// is accepted trivially — the gate is a no-op when the author
4833    /// didn't declare a value. The empty-first cascade discipline
4834    /// mirrors every peer per-axis identity gate:
4835    /// [`ManifestError::EdicaoEmpty`] runs before
4836    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
4837    /// diagnostic surfaces on `Some("")` rather than the broader
4838    /// shape-predicate diagnostic — peer with how
4839    /// [`ManifestError::NomeEmpty`] runs before
4840    /// [`ManifestError::NomeInvalid`],
4841    /// [`ManifestError::VersaoEmpty`] runs before
4842    /// [`ManifestError::VersaoInvalid`],
4843    /// [`ManifestError::RepositorioEmpty`] runs before
4844    /// [`ManifestError::RepositorioInvalid`].
4845    ///
4846    /// A future tightening on this axis can extend the shape
4847    /// predicate into a known-edition allowlist (rejecting
4848    /// year-shaped values that don't name a tatara-lisp edition
4849    /// the substrate actually understands — e.g., `"1999"` is
4850    /// year-shaped but no `1999` edition exists). That allowlist
4851    /// only becomes meaningful past the introduction of a sibling
4852    /// edition to `"2026"`; this gate establishes the structural
4853    /// floor by refusing every non-year-shaped value at validate
4854    /// time.
4855    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
4856        let Some(s) = self.edicao() else {
4857            return Ok(());
4858        };
4859        if s.is_empty() {
4860            return Err(ManifestError::EdicaoEmpty);
4861        }
4862        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
4863            return Err(ManifestError::EdicaoInvalid {
4864                edicao: s.to_string(),
4865                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
4866            });
4867        }
4868        Ok(())
4869    }
4870
4871    /// Compose the supervisor-related flat slots into a single
4872    /// [`SupervisorSpec`] for validation. Returns `None` when the
4873    /// caixa isn't a `:kind Supervisor`.
4874    ///
4875    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
4876    /// simple (one form, no nested `:supervisor (…)` block); this view
4877    /// is the "typed shape" the operator + supervisor reconciler
4878    /// consume.
4879    #[must_use]
4880    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
4881        if !self.kind().is_supervisor() {
4882            return None;
4883        }
4884        // Fold through the shared `supervisor::duration_codec::parse`
4885        // — the same parser the serde-routed `with = "duration_codec"`
4886        // on `SupervisorSpec::restart_window`, the `:politicas
4887        // :timeout` codec, and the `:politicas :circuit-breaker
4888        // :window` codec all consume. The prior inline f64-shaped
4889        // duplicate (`parse_window_inline`) admitted every magnitude
4890        // the integer-magnitude gate (1c55a2a) rejects on the three
4891        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
4892        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
4893        // `None` (i.e. "no reset"), divergent from the shared codec's
4894        // integer-magnitude discipline by construction. The fold
4895        // closes the divergence: every value the typed
4896        // `SupervisorSpec` carries past `supervisor_view` is in the
4897        // shared codec's accepted set. The `.ok()` here preserves the
4898        // existing soft-swallow shape on this view-construction path;
4899        // the new [`Caixa::validate_restart_window`] (sibling of
4900        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
4901        // the offending raw string at build time so authoring tools
4902        // (`feira lint`, the future layout-side wire-up) surface a
4903        // self-locating diagnostic instead of a silently dropped
4904        // window.
4905        let restart_window = self
4906            .restart_window()
4907            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
4908        Some(SupervisorSpec {
4909            estrategia: self.estrategia().unwrap_or_default(),
4910            max_restarts: self.max_restarts().unwrap_or(5),
4911            restart_window,
4912            children: self.children().to_vec(),
4913        })
4914    }
4915
4916    /// A minimal starter manifest emitted by `feira init`.
4917    #[must_use]
4918    pub fn template(nome: &str) -> String {
4919        format!(
4920            "(defcaixa\n  \
4921               :nome        {nome:?}\n  \
4922               :versao      \"0.1.0\"\n  \
4923               :kind        Biblioteca\n  \
4924               :edicao      \"2026\"\n  \
4925               :descricao   \"FIXME — describe this caixa\"\n  \
4926               :autores     ()\n  \
4927               :etiquetas   ()\n  \
4928               :deps        ()\n  \
4929               :deps-dev    ()\n  \
4930               :bibliotecas (\"lib/{nome}.lisp\"))\n"
4931        )
4932    }
4933
4934    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
4935    /// back after mutation (e.g. `feira add`).
4936    ///
4937    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
4938    /// The derive-macro `compile_from_sexp` path is the inverse, so any
4939    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
4940    #[must_use]
4941    pub fn to_lisp(&self) -> String {
4942        let json = serde_json::to_value(self).expect("Caixa serialize");
4943        let sexp = tatara_lisp::domain::json_to_sexp(&json);
4944        let tatara_lisp::Sexp::List(items) = sexp else {
4945            return format!("(defcaixa {sexp})\n");
4946        };
4947        let mut out = String::from("(defcaixa");
4948        let mut i = 0;
4949        while i + 1 < items.len() {
4950            out.push_str("\n  ");
4951            out.push_str(&items[i].to_string());
4952            out.push(' ');
4953            out.push_str(&items[i + 1].to_string());
4954            i += 2;
4955        }
4956        out.push_str(")\n");
4957        out
4958    }
4959}
4960
4961/// Errors raised by top-level [`Caixa`] validators that don't fit
4962/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
4963/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
4964/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
4965/// through every substrate-side artifact's `metadata.name` /
4966/// version derivation.
4967///
4968/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
4969/// doc-comment anticipates) can hold one of each per-axis error
4970/// family without reshaping individual diagnostics; this enum is
4971/// the first such per-Caixa-identity family.
4972#[derive(Debug, Error, PartialEq, Eq)]
4973pub enum ManifestError {
4974    #[error(
4975        ":nome is empty (every caixa must name itself; the value flows \
4976         into every K8s artifact's `metadata.name` derivation and into \
4977         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
4978    )]
4979    NomeEmpty,
4980    #[error(
4981        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
4982         apiserver enforces this rule on every `metadata.name` the \
4983         caixa's substrate-side renderers derive from `:nome` — the \
4984         `lareira-<nome>` Helm chart name, the programs.yaml entry \
4985         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
4986         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
4987         name; use a lowercase alphanumeric + hyphen identifier like \
4988         `\"checkout\"` or `\"cart-v2\"`)"
4989    )]
4990    NomeInvalid { nome: String, reason: String },
4991    #[error(
4992        ":nome {nome:?} overflows the joint-length budget on the canonical \
4993         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
4994         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
4995         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
4996         `chart:` slot, `caixa-tatara`'s `release_name` + \
4997         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
4998         joint name through the canonical `lareira_chart_name` helper, and \
4999         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
5000         DNS-1123 label cap on every chart-name-derived `metadata.name` \
5001         reject any joint name exceeding 63 bytes; the narrower \
5002         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
5003         arm gates the chart-name budget downstream renderers inherit)"
5004    )]
5005    NomeChartNameBudgetExceeded { nome: String, reason: String },
5006    #[error(
5007        ":versao is empty (every caixa must pin its own version; the value flows \
5008         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
5009         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
5010         `:latest` tags, the lacre closure's `concrete_versao`, and the \
5011         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
5012    )]
5013    VersaoEmpty,
5014    #[error(
5015        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
5016         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
5017         with optional `-prerelease` and `+build` — across every artifact derived \
5018         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
5019         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
5020         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
5021         and the `:upgrade-from :from` peers that match against this exact shape; \
5022         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
5023         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
5024         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
5025    )]
5026    VersaoInvalid { versao: String, reason: String },
5027    #[error(
5028        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
5029         substrate consumes this string through the shared \
5030         `supervisor::duration_codec` — the same parser routed via `with = \
5031         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
5032         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
5033         the canonical authoring form is `<integer><unit>` where the unit is one \
5034         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
5035         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
5036         Without this gate a malformed `:restart-window` silently produced a \
5037         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
5038         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
5039         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
5040         layer with the offending value named verbatim. Omit the slot entirely to \
5041         express \"no reset\"; carry a positive integer duration to express the \
5042         sliding window)"
5043    )]
5044    RestartWindowMalformed {
5045        restart_window: String,
5046        reason: String,
5047    },
5048    #[error(
5049        "{slot} entry is an empty path string — every {slot} entry must name \
5050         a file relative to the caixa root; omit the entry to omit the file \
5051         (the layout checker's `root.join(\"\")` resolves to the caixa root \
5052         itself, so an empty entry silently aliases the project root as a \
5053         declared {slot} file, then fails downstream at parse / existence \
5054         time with a diagnostic that names the root rather than the offending \
5055         entry)"
5056    )]
5057    CodePathEmpty { slot: &'static str },
5058    #[error(
5059        "{slot} entry {} is an absolute path — entries must be relative to \
5060         the caixa root, since `Path::join` replaces the base with an absolute \
5061         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
5062         outside the caixa root sandbox; rewrite the entry as a relative path \
5063         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
5064         `\"servicos/<name>.computeunit.yaml\"`)",
5065        path.display()
5066    )]
5067    CodePathAbsolute { slot: &'static str, path: PathBuf },
5068    #[error(
5069        "{slot} entry {} contains a `..` component — entries must not traverse \
5070         above the caixa root (the layout's `starts_with(<dir>)` fence on \
5071         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
5072         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
5073         has no such fence, so a leading `..` escapes unconditionally if the \
5074         resolved target happens to exist)",
5075        path.display()
5076    )]
5077    CodePathParentEscape { slot: &'static str, path: PathBuf },
5078    #[error(
5079        "{slot} entry {} does not terminate in the `.lisp` extension — every \
5080         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
5081         loop reads through `tatara_lisp::read` at parse time, so any other \
5082         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
5083         structurally a parser error far from the source caixa.lisp, with \
5084         no field naming the offending `:bibliotecas` entry. Pin a relative \
5085         path under the caixa root whose terminating extension is \
5086         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
5087         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
5088         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
5089         (33cc830) axes already carry through the same lifted \
5090         `is_lisp_extension` predicate",
5091        path.display()
5092    )]
5093    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
5094    #[error(
5095        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
5096         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
5097         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
5098         through `serde_yaml::from_str` at chart / FluxCD bundle render \
5099         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
5100         off-by-one-segment `.computeunit-yaml`, the editor-backup \
5101         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
5102         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
5103         source caixa.lisp, with no field naming the offending `:servicos` \
5104         entry. Pin a relative path under the caixa root whose terminating \
5105         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
5106         `\"servicos/<name>.computeunit.yaml\"`, \
5107         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
5108         contract the sibling `:bibliotecas` axis (64772a9) already carries \
5109         on the tatara-lisp-source axis through the peer lifted \
5110         `is_lisp_extension` predicate, here on the compound-suffix axis \
5111         `Path::extension` can't express on its own through the lifted \
5112         `is_computeunit_yaml_extension` predicate",
5113        path.display()
5114    )]
5115    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
5116    #[error(
5117        "{slot} entry {} appears more than once (the code-path list is \
5118         a set, not a multiset; every peer Vec-shaped author-supplied \
5119         list past validate is set-not-multiset — `:membros :caixa`, \
5120         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5121         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
5122         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
5123         code-path lists are the last Vec-shaped author-supplied slots on \
5124         the typed Caixa surface still admitting a duplicate entry. \
5125         `:bibliotecas` duplicates re-parse the same file at \
5126         `feira build` time and silently mask the author's intent to \
5127         declare a *second* biblioteca; `:exe` duplicates collide on the \
5128         flake `packages.<name>` derivation key at the future \
5129         `caixa-flake` materializer; `:servicos` duplicates surface as the \
5130         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
5131         rejection far from the source `caixa.lisp`. Drop the duplicate \
5132         or rename it to the actual second file intended)",
5133        path.display()
5134    )]
5135    CodePathDuplicate { slot: &'static str, path: PathBuf },
5136    #[error(
5137        ":etiquetas entry is empty (every tag must carry a non-empty \
5138         registry-search identifier; the empty entry has no operational \
5139         meaning — it indexes nothing in the future caixa-registry search \
5140         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
5141         with a no-op tag; omit the entry to express \"no tag on this \
5142         position\")"
5143    )]
5144    EtiquetaEmpty,
5145    #[error(
5146        ":etiquetas entry {etiqueta:?} appears more than once (the \
5147         registry-search tag set is a set, not a multiset; duplicate \
5148         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
5149         at chart render — a \"second wins / one silently disappears\" \
5150         shape divergent from every peer typed-graph set gate \
5151         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
5152         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
5153         duplicate or rename it to the actual tag intended)"
5154    )]
5155    EtiquetaDuplicate { etiqueta: String },
5156    #[error(
5157        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
5158         {reason} (the substrate consumes this string through the shared \
5159         `crate::render::is_chart_keyword_shape` predicate — the same \
5160         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
5161         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
5162         continuation. The canonical authoring shapes are short kebab-case \
5163         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
5164         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
5165         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
5166         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
5167         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
5168         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
5169         `\"mesh,http,grpc\"` — the author meant to author three separate \
5170         list entries; path-separator confusion `\"caixa/servico\"`; \
5171         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
5172         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
5173         `\"café\"` — every legitimate search tag is strict ASCII; \
5174         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
5175         passed `from_lisp` + `validate_etiquetas` + \
5176         `StandardLayout::verify` and landed in the rendered \
5177         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
5178         malformed search tag — Artifact Hub's keyword index + the future \
5179         caixa-registry's keyword index would either silently drop the \
5180         tag or fail to index it far from the source caixa.lisp; the gate \
5181         moves the diagnostic to the manifest layer with the offending \
5182         value named verbatim)"
5183    )]
5184    EtiquetaInvalid { etiqueta: String, reason: String },
5185    #[error(
5186        ":autores entry is empty (every maintainer must carry a non-empty \
5187         identifier; the empty entry has no operational meaning — it \
5188         identifies no one in the substrate's authorship index and renders \
5189         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
5190         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
5191         omit the entry to express \"no maintainer on this position\")"
5192    )]
5193    AutorEmpty,
5194    #[error(
5195        ":autores entry {autor:?} appears more than once (the maintainer \
5196         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
5197         `maintainers:` rendering does *no* dedup — duplicate entries \
5198         stack verbatim in `Chart.yaml` as two identical \
5199         `Maintainer {{ name, email: None }}` records, divergent from every \
5200         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
5201         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5202         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
5203         rename it to the actual author intended)"
5204    )]
5205    AutorDuplicate { autor: String },
5206    #[error(
5207        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
5208         {reason} (the substrate consumes this string through the shared \
5209         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
5210         single-line-UTF-8 floor every realistic chart maintainer name carries: \
5211         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
5212         characters anywhere, Unicode bytes accepted. The canonical authoring \
5213         shapes are short single-line identifiers like `\"pleme-io\"`, \
5214         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
5215         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
5216         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
5217         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
5218         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
5219         records into one entry instead of splitting into one entry per author; \
5220         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
5221         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
5222         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5223         `validate_autores` + `StandardLayout::verify` and landed in the \
5224         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
5225         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
5226         round-trip — every chart-aware UI (`helm list`, `helm search`, \
5227         Artifact Hub maintainer index) would render the maintainer name in a \
5228         single-line column far from the source caixa.lisp; the gate moves the \
5229         diagnostic to the manifest layer with the offending value named \
5230         verbatim)"
5231    )]
5232    AutorInvalid { autor: String, reason: String },
5233    #[error(
5234        ":repositorio is the empty string (every published caixa names its \
5235         git source via a non-empty `:repositorio` locator — the value \
5236         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
5237         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
5238         `GitRepository.spec.url` via `caixa-flux`'s \
5239         `ClusterBundleOpts::for_caixa`; both consumers' \
5240         `Option::unwrap_or_else` fallbacks only fire when the slot is \
5241         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
5242         `url: \"\"` in the rendered artifacts and breaks at `helm \
5243         template` / FluxCD source-controller reconcile time far from the \
5244         source caixa.lisp; omit the slot entirely to defer to the \
5245         renderer's `https://github.com/pleme-io/<nome>` / \
5246         `caixa.nome`-derived fallback, or carry a canonical authoring \
5247         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
5248         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
5249         `\"file:///path\"`)"
5250    )]
5251    RepositorioEmpty,
5252    #[error(
5253        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
5254         (the substrate consumes this string through the shared \
5255         `crate::render::is_git_repo_url` predicate — the same parser the \
5256         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
5257         value through via `DepSource::validate`; the canonical authoring \
5258         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
5259         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
5260         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
5261         scp-style SSH form. Without this gate a malformed `:repositorio` \
5262         (whitespace from a paste-from-doc; control characters / CRLF \
5263         from a paste-from-multiline-doc; a leading `-` from a \
5264         CLI-argument-injection footgun; a missing `:` separator from a \
5265         bare `org/repo` shape git treats as a relative filesystem path) \
5266         silently landed in the rendered `Chart.yaml home:` and the \
5267         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
5268         FluxCD reconcile time far from the source caixa.lisp; the gate \
5269         moves the diagnostic to the manifest layer with the offending \
5270         value named verbatim)"
5271    )]
5272    RepositorioInvalid { repositorio: String, reason: String },
5273    #[error(
5274        ":descricao is the empty string (every published caixa names \
5275         its purpose via a non-empty `:descricao` summary — the value \
5276         flows verbatim into the rendered `lareira-<nome>` Helm \
5277         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
5278         `build_chart_yaml` and into the chart `README.md` header via \
5279         `build_readme`; both consumers' `Option::unwrap_or_else` \
5280         `caixa.nome`-derived fallbacks only fire when the slot is \
5281         `None`, so an empty `Some(\"\")` silently lands as \
5282         `description: \"\"` / a blank `README.md` header in the \
5283         rendered artifacts and breaks at `helm lint` time \
5284         (`WARNING [chart.metadata.description]: description is \
5285         required` on `apiVersion: v2` charts) far from the source \
5286         caixa.lisp; omit the slot entirely to defer to the \
5287         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
5288         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
5289         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
5290         Servico.\"`)"
5291    )]
5292    DescricaoEmpty,
5293    #[error(
5294        ":descricao {descricao:?} is not a valid chart-description shape: \
5295         {reason} (the substrate consumes this string through the shared \
5296         `crate::render::is_chart_description_shape` predicate — the same \
5297         single-line-UTF-8 floor every realistic chart description carries: \
5298         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
5299         characters anywhere, Unicode prose bytes accepted. The canonical \
5300         authoring shapes are short single-line summaries like `\"Canonical \
5301         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
5302         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
5303         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
5304         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
5305         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
5306         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
5307         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
5308         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5309         `validate_descricao` + `StandardLayout::verify` and landed in the \
5310         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
5311         field + `README.md` header paragraph as a YAML-illegal multi-line \
5312         scalar or a silently-trimmed whitespace round-trip — every \
5313         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
5314         render the description in a single-line column far from the source \
5315         caixa.lisp; the gate moves the diagnostic to the manifest layer \
5316         with the offending value named verbatim)"
5317    )]
5318    DescricaoInvalid { descricao: String, reason: String },
5319    #[error(
5320        ":licenca is the empty string (every published caixa names \
5321         its license via a non-empty `:licenca` SPDX expression — the \
5322         value flows verbatim into the rendered `lareira-<nome>` Helm \
5323         chart's `README.md` `## License` section via `caixa-helm`'s \
5324         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
5325         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
5326         only fires when the slot is `None`, so an empty `Some(\"\")` \
5327         silently lands as a bare trailing period in the rendered \
5328         chart `README.md` `License` section far from the source \
5329         caixa.lisp; omit the slot entirely to defer to the \
5330         renderer's `MIT` fallback, or carry a canonical SPDX \
5331         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
5332         `\"Apache-2.0 OR MIT\"`)"
5333    )]
5334    LicencaEmpty,
5335    #[error(
5336        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
5337         (the substrate consumes this string through the shared \
5338         `crate::render::is_spdx_expression_shape` predicate — the same \
5339         alphabet-floor parser every peer per-axis value-shape gate routes \
5340         its value through; the canonical authoring shapes are single \
5341         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
5342         compound expressions like `\"Apache-2.0 OR MIT\"`, \
5343         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
5344         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
5345         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
5346         like `\"LicenseRef-MyLicense\"` / \
5347         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
5348         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
5349         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
5350         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
5351         a smart-quote paste; underscore-instead-of-hyphen typo \
5352         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
5353         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
5354         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
5355         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
5356         `README.md` `## License` section + a future SPDX-aware \
5357         `Chart.yaml license:` emitter would refuse the value at \
5358         `helm lint` time far from the source caixa.lisp; the gate moves \
5359         the diagnostic to the manifest layer with the offending value \
5360         named verbatim)"
5361    )]
5362    LicencaInvalid { licenca: String, reason: String },
5363    #[error(
5364        ":edicao is the empty string (every published caixa names \
5365         its language edition via a non-empty `:edicao` value — the \
5366         edition determines the tatara-lisp macro surface + \
5367         compatibility flags the substrate applies when building \
5368         the caixa; the canonical `Caixa::template` scaffold every \
5369         `feira init` emits carries `:edicao \"2026\"` verbatim and \
5370         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
5371         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
5372         construction, so an empty `Some(\"\")` silently lands as a \
5373         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
5374         a future renderer-side consumer that folds it through \
5375         `Option::unwrap_or_else` will skip the fallback and pass the \
5376         empty edition through to the substrate's build-time edition \
5377         selector far from the source caixa.lisp; omit the slot \
5378         entirely to defer to the substrate's default edition, or \
5379         carry a canonical edition like `\"2026\"`)"
5380    )]
5381    EdicaoEmpty,
5382    #[error(
5383        ":edicao {edicao:?} is not a valid edition: {reason} (every \
5384         documented tatara-lisp edition is a 4-digit ASCII decimal \
5385         year — `\"2026\"` is the only edition currently minted; \
5386         future-introduced siblings will follow the same shape, peer \
5387         with Cargo's `[package] edition` grammar which every value \
5388         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
5389         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
5390         paste-from-doc footguns silently passed: a trailing space \
5391         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
5392         from a paste-from-multiline-doc, a fullwidth-keyboard \
5393         look-alike (`\"2026\"`), a free-form non-year value \
5394         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
5395         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
5396         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
5397         wrong-length numeric value (`\"26\"`, `\"202\"`, \
5398         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
5399         rendered caixa.lisp and broke at the substrate's \
5400         build-time edition selector far from the source caixa.lisp; \
5401         omit the slot entirely to defer to the substrate's default \
5402         edition, or carry a canonical 4-digit ASCII decimal year \
5403         like `\"2026\"`)"
5404    )]
5405    EdicaoInvalid { edicao: String, reason: String },
5406}
5407
5408#[cfg(test)]
5409mod tests {
5410    use super::*;
5411
5412    #[test]
5413    fn template_round_trips() {
5414        let src = Caixa::template("demo");
5415        let c = Caixa::from_lisp(&src).expect("template must parse");
5416        assert_eq!(c.nome, "demo");
5417        assert_eq!(c.versao, "0.1.0");
5418        assert_eq!(c.kind, CaixaKind::Biblioteca);
5419        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
5420        assert!(c.deps.is_empty());
5421        assert!(c.deps_dev.is_empty());
5422    }
5423
5424    #[test]
5425    fn register_populates_registry() {
5426        Caixa::register();
5427        let kws = tatara_lisp::domain::registered_keywords();
5428        assert!(kws.contains(&"defcaixa"));
5429    }
5430
5431    #[test]
5432    fn to_lisp_round_trips() {
5433        let src = Caixa::template("demo");
5434        let c1 = Caixa::from_lisp(&src).unwrap();
5435        let emitted = c1.to_lisp();
5436        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
5437        assert_eq!(c1, c2);
5438    }
5439
5440    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
5441    //
5442    // The compounding pin: the variant stores only the typed
5443    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
5444    // (canonical keyword, description, consumer) routes through the enum's
5445    // own accessors at Display time. Prior to that closure the variant
5446    // carried each accessor's return value as a stored `&'static str`
5447    // snapshot alongside `dialeto`; a caller could construct the variant
5448    // with a snapshot that drifted from what `dialeto`'s accessors would
5449    // return, and every downstream user-facing projection would silently
5450    // disagree with the classification. Storing only the axis makes the
5451    // drift structurally impossible.
5452
5453    #[test]
5454    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
5455        // Single-field construction is the whole compounding shape — a
5456        // future re-introduction of a snapshot field (a `palavra_canonica:
5457        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
5458        // would re-open the drift surface and this construction would fail
5459        // to compile with "missing field" until every snapshot was seeded
5460        // at the call site again. The compile-time guarantee is the
5461        // invariant; the assertion below only witnesses that the
5462        // construction is well-formed after the closure.
5463        let err = LeituraError::DialetoEstrangeiro {
5464            dialeto: crate::dialeto::CaixaDialeto::Molde,
5465        };
5466        assert!(matches!(
5467            err,
5468            LeituraError::DialetoEstrangeiro {
5469                dialeto: crate::dialeto::CaixaDialeto::Molde,
5470            }
5471        ));
5472    }
5473
5474    #[test]
5475    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
5476        // For every foreign-dialect classification the variant surfaces —
5477        // [`crate::dialeto::CaixaDialeto::Molde`] and
5478        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
5479        // variants [`Caixa::from_lisp`] raises this error for — the
5480        // rendered [`std::fmt::Display`] byte-string must interpolate each
5481        // typed accessor's return verbatim. A future re-introduction of a
5482        // stored `&'static str` snapshot alongside `dialeto` that Display
5483        // read instead of the accessor would fail this pin as soon as the
5484        // two disagreed; a future accessor rebrand (a per-dialect
5485        // consumer rename, a canonical-keyword shift once the substrate
5486        // migration named in [`crate::dialeto`] completes) reaches every
5487        // consumer through one typed dispatch and this pin verifies the
5488        // display path is one of them.
5489        for d in [
5490            crate::dialeto::CaixaDialeto::Molde,
5491            crate::dialeto::CaixaDialeto::MoldePosicional,
5492        ] {
5493            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
5494            assert!(
5495                rendered.contains(d.palavra_canonica()),
5496                "Display must interpolate `dialeto.palavra_canonica()` \
5497                 verbatim — a stored snapshot would silently drift from \
5498                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
5499            );
5500            assert!(
5501                rendered.contains(d.descricao()),
5502                "Display must interpolate `dialeto.descricao()` verbatim. \
5503                 dialect: {d}, rendered: {rendered:?}"
5504            );
5505            assert!(
5506                rendered.contains(d.consumidor()),
5507                "Display must interpolate `dialeto.consumidor()` verbatim. \
5508                 dialect: {d}, rendered: {rendered:?}"
5509            );
5510        }
5511    }
5512
5513    #[test]
5514    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
5515        // The end-to-end pin the compounding closure defends: a
5516        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
5517        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
5518        // rendered Display byte-string names the Molde accessors'
5519        // returns verbatim. Any future path that constructed the variant
5520        // with a mismatched snapshot (a stored `palavra_canonica:
5521        // "defcaixa"` on a `Molde` classification) would land Display
5522        // pointing at `defcaixa` while the typed axis said `Molde` — the
5523        // exact drift the closure removes.
5524        let src = r#"
5525          (defcaixa
5526            :name "x"
5527            :kind :Biblioteca
5528            :ecosystem :rust-single-crate
5529            :package {:name "x" :version "0.1.0"})
5530        "#;
5531        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
5532        match err {
5533            LeituraError::DialetoEstrangeiro { dialeto } => {
5534                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
5535                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5536                assert!(rendered.contains(dialeto.palavra_canonica()));
5537                assert!(rendered.contains(dialeto.consumidor()));
5538                assert!(rendered.contains(dialeto.descricao()));
5539            }
5540            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
5541        }
5542    }
5543
5544    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
5545
5546    #[test]
5547    fn limits_round_trip_via_json() {
5548        use crate::LimitsSpec;
5549        use std::time::Duration;
5550        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5551        c.limits = Some(LimitsSpec {
5552            memory: Some(64 * 1024 * 1024),
5553            fuel: Some(1_000_000),
5554            wall_clock: Some(Duration::from_secs(30)),
5555            cpu: Some(500),
5556        });
5557        let json = serde_json::to_string(&c).unwrap();
5558        assert!(json.contains("\"limits\""));
5559        assert!(json.contains("\"64MiB\""));
5560        assert!(json.contains("\"30s\""));
5561        assert!(json.contains("\"500m\""));
5562        let back: Caixa = serde_json::from_str(&json).unwrap();
5563        assert_eq!(c.limits, back.limits);
5564    }
5565
5566    #[test]
5567    fn behavior_round_trip_via_json() {
5568        use crate::BehaviorSpec;
5569        use std::path::PathBuf;
5570        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5571        c.behavior = Some(BehaviorSpec {
5572            on_init: Some(PathBuf::from("lib/init.lisp")),
5573            on_call: Some(PathBuf::from("lib/handlers.lisp")),
5574            ..Default::default()
5575        });
5576        let json = serde_json::to_string(&c).unwrap();
5577        let back: Caixa = serde_json::from_str(&json).unwrap();
5578        assert_eq!(c.behavior, back.behavior);
5579    }
5580
5581    #[test]
5582    fn upgrade_from_round_trip_via_json() {
5583        use crate::{UpgradeFromEntry, UpgradeInstruction};
5584        use std::path::PathBuf;
5585        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5586        c.upgrade_from = vec![UpgradeFromEntry {
5587            from: "0.1.0".into(),
5588            instructions: vec![
5589                UpgradeInstruction::LoadModule {
5590                    module: "demo".into(),
5591                },
5592                UpgradeInstruction::StateChange {
5593                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
5594                },
5595                UpgradeInstruction::SoftPurge {
5596                    module: "demo-old".into(),
5597                },
5598            ],
5599        }];
5600        let json = serde_json::to_string(&c).unwrap();
5601        let back: Caixa = serde_json::from_str(&json).unwrap();
5602        assert_eq!(c.upgrade_from, back.upgrade_from);
5603    }
5604
5605    #[test]
5606    fn supervisor_view_returns_typed_shape() {
5607        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
5608        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
5609        c.kind = CaixaKind::Supervisor;
5610        c.bibliotecas.clear();
5611        c.estrategia = Some(RestartStrategy::OneForOne);
5612        c.max_restarts = Some(5);
5613        c.restart_window = Some("60s".into());
5614        c.children = vec![ChildSpec {
5615            caixa: "worker".into(),
5616            versao: "^0.1".into(),
5617            restart: RestartPolicy::Permanent,
5618        }];
5619        let view = c.supervisor_view().expect("Supervisor kind has a view");
5620        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
5621        assert_eq!(view.max_restarts, 5);
5622        assert_eq!(
5623            view.restart_window,
5624            Some(std::time::Duration::from_secs(60))
5625        );
5626        assert_eq!(view.children.len(), 1);
5627        view.validate().unwrap();
5628    }
5629
5630    #[test]
5631    fn supervisor_view_none_for_non_supervisor_kinds() {
5632        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5633        assert!(c.supervisor_view().is_none());
5634    }
5635
5636    #[test]
5637    fn declared_mesh_slots_empty_for_bare_caixa() {
5638        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5639        assert!(c.declared_mesh_slots().is_empty());
5640    }
5641
5642    #[test]
5643    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
5644        use crate::{Entrada, Membro};
5645        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5646        // Set a non-adjacent pair (:membros + :entrada) to pin that the
5647        // canonical declaration order is preserved regardless of which
5648        // subset is populated.
5649        c.membros = vec![Membro {
5650            caixa: "a".into(),
5651            versao: "^0.1".into(),
5652        }];
5653        c.entrada = Some(Entrada {
5654            host: "x.example.com".into(),
5655            para: "a".into(),
5656            paths: vec![],
5657            port: 8080,
5658        });
5659        assert_eq!(
5660            c.declared_mesh_slots(),
5661            vec![
5662                crate::render::M3_AUTHOR_KEY_MEMBROS,
5663                crate::render::M3_AUTHOR_KEY_ENTRADA,
5664            ]
5665        );
5666    }
5667
5668    #[test]
5669    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
5670        // Scalar-value pin: the five author-facing kebab-case labels the
5671        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
5672        // mesh slot axis, one arm per typed slot. Mirrors the peer
5673        // scalar-value pin the sibling
5674        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
5675        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
5676        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
5677        // carry (f49c8b0), so both altitudes of the typed-slot algebra
5678        // (per-Servico M2 + per-Aplicacao M3) share the same
5679        // "one canonical byte-string per arm" discipline. A future
5680        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
5681        // `:politicas` → `:policies`, `:placement` → `:distribution`,
5682        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
5683        // and every consumer that reaches for the label picks it up at
5684        // build time rather than at runtime as a downstream mismatch.
5685        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
5686        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
5687        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
5688        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
5689        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
5690    }
5691
5692    #[test]
5693    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
5694        // Production-through-const pin: the five per-arm labels the
5695        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
5696        // `Vec` route through the lifted
5697        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
5698        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
5699        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
5700        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
5701        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
5702        // declaration order. A future re-order or drift at the tagger
5703        // (a rename that reaches the tagger but not the const, or vice
5704        // versa) surfaces here at build time rather than at runtime as
5705        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
5706        // `slots: <stale-kebab-case>` diagnostic far from the rename's
5707        // commit. Mirror of the peer
5708        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
5709        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
5710        // axis.
5711        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
5712        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5713        c.membros = vec![Membro {
5714            caixa: "a".into(),
5715            versao: "^0.1".into(),
5716        }];
5717        c.contratos = vec![WitContract {
5718            de: "a".into(),
5719            para: "a".into(),
5720            wit: "wasi:http/proxy".into(),
5721            endpoint: Some("/x".into()),
5722            subject: None,
5723            slot: None,
5724        }];
5725        c.politicas = Some(MeshPolicy::default());
5726        c.placement = Some(Placement {
5727            estrategia: PlacementStrategy::Replicated,
5728            clusters: vec!["rio".into()],
5729            affinity: None,
5730            shard_key: None,
5731        });
5732        c.entrada = Some(Entrada {
5733            host: "x.example.com".into(),
5734            para: "a".into(),
5735            paths: vec![],
5736            port: 8080,
5737        });
5738        assert_eq!(
5739            c.declared_mesh_slots(),
5740            vec![
5741                crate::render::M3_AUTHOR_KEY_MEMBROS,
5742                crate::render::M3_AUTHOR_KEY_CONTRATOS,
5743                crate::render::M3_AUTHOR_KEY_POLITICAS,
5744                crate::render::M3_AUTHOR_KEY_PLACEMENT,
5745                crate::render::M3_AUTHOR_KEY_ENTRADA,
5746            ]
5747        );
5748    }
5749
5750    #[test]
5751    fn declared_supervisor_slots_empty_for_bare_caixa() {
5752        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5753        assert!(c.declared_supervisor_slots().is_empty());
5754    }
5755
5756    #[test]
5757    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
5758        use crate::RestartStrategy;
5759        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5760        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
5761        // that the canonical declaration order is preserved regardless
5762        // of which subset is populated.
5763        c.estrategia = Some(RestartStrategy::OneForOne);
5764        c.restart_window = Some("60s".into());
5765        assert_eq!(
5766            c.declared_supervisor_slots(),
5767            vec![
5768                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
5769                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
5770            ]
5771        );
5772    }
5773
5774    #[test]
5775    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
5776        // Scalar-value pin: the four author-facing kebab-case labels the
5777        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
5778        // supervision-tree slot axis, one arm per typed slot. Mirrors the
5779        // peer scalar-value pins the sibling
5780        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
5781        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
5782        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
5783        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
5784        // top-level M3 slot consts carry, so all three kind-scoped
5785        // typed-slot-family author-facing-label axes route through one
5786        // canonical per-arm declaration. A future rebrand
5787        // (`:estrategia` → `:strategy` for English uniformity,
5788        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
5789        // `MaxIntensity` name, `:restart-window` → `:period` matching
5790        // OTP's `Period` name, `:children` → `:workers` matching Elixir
5791        // idiom) lands as an edit to exactly one const, and every
5792        // consumer that reaches for the label picks it up at build time
5793        // rather than at runtime as a downstream mismatch.
5794        assert_eq!(
5795            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
5796            ":estrategia"
5797        );
5798        assert_eq!(
5799            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
5800            ":max-restarts"
5801        );
5802        assert_eq!(
5803            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
5804            ":restart-window"
5805        );
5806        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
5807    }
5808
5809    #[test]
5810    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
5811        // Production-through-const pin: the four per-arm labels the
5812        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
5813        // return `Vec` route through the lifted
5814        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
5815        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
5816        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
5817        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
5818        // canonical declaration order. A future re-order or drift at the
5819        // tagger (a rename that reaches the tagger but not the const, or
5820        // vice versa) surfaces here at build time rather than at runtime
5821        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
5822        // `slots: <stale-kebab-case>` diagnostic far from the rename's
5823        // commit. Mirror of the peer
5824        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
5825        // (f49c8b0) and
5826        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
5827        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
5828        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
5829        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5830        c.estrategia = Some(RestartStrategy::OneForOne);
5831        c.max_restarts = Some(5);
5832        c.restart_window = Some("60s".into());
5833        c.children = vec![ChildSpec {
5834            caixa: "worker".into(),
5835            versao: "^0.1".into(),
5836            restart: RestartPolicy::Permanent,
5837        }];
5838        assert_eq!(
5839            c.declared_supervisor_slots(),
5840            vec![
5841                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
5842                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
5843                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
5844                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
5845            ]
5846        );
5847    }
5848
5849    #[test]
5850    fn declared_servico_slots_empty_for_bare_caixa() {
5851        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5852        assert!(c.declared_servico_slots().is_empty());
5853    }
5854
5855    #[test]
5856    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
5857        use crate::{UpgradeFromEntry, UpgradeInstruction};
5858        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5859        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
5860        // the canonical declaration order is preserved regardless of
5861        // which subset is populated.
5862        c.limits = Some(crate::LimitsSpec {
5863            fuel: Some(1_000_000),
5864            ..Default::default()
5865        });
5866        c.upgrade_from = vec![UpgradeFromEntry {
5867            from: "0.1.0".into(),
5868            instructions: vec![UpgradeInstruction::Restart],
5869        }];
5870        assert_eq!(
5871            c.declared_servico_slots(),
5872            vec![
5873                crate::render::M2_AUTHOR_KEY_LIMITS,
5874                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
5875            ]
5876        );
5877    }
5878
5879    #[test]
5880    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
5881        // Scalar-value pin: the three author-facing kebab-case labels
5882        // the `(defcaixa … :<slot> (…))` surface admits on the M2
5883        // top-level slot axis, one arm per typed slot. Mirrors the peer
5884        // scalar-value pin the sibling renderer-side
5885        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
5886        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
5887        // consts carry, so both halves of the M2 top-level slot dual
5888        // axis (author-facing kebab-case label + renderer-side
5889        // camelCase overlay-container wire key) route through one
5890        // canonical per-arm declaration. A future rebrand
5891        // (`:limits` → `:sandbox` matching Lunatic per-process
5892        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
5893        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
5894        // matching Erlang's verbatim appup name) lands as an edit to
5895        // exactly one const, and every consumer that reaches for the
5896        // label picks it up at build time rather than at runtime as a
5897        // downstream mismatch.
5898        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
5899        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
5900        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
5901    }
5902
5903    #[test]
5904    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
5905        // Production-through-const pin: the three per-arm labels the
5906        // [`Caixa::declared_servico_slots`] tagger pushes onto its
5907        // return `Vec` route through the lifted
5908        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
5909        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
5910        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
5911        // declaration order. A future re-order or drift at the tagger
5912        // (a rename that reaches the tagger but not the const, or vice
5913        // versa) surfaces here at build time rather than at runtime as
5914        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
5915        // `slots: <stale-kebab-case>` diagnostic far from the rename's
5916        // commit. Mirror of the peer
5917        // [`crate::behavior::BehaviorSpec::declared_slots`] production
5918        // tagger pin (889dc18) on the sibling per-callback axis.
5919        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
5920        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5921        c.limits = Some(crate::LimitsSpec {
5922            fuel: Some(1_000_000),
5923            ..Default::default()
5924        });
5925        c.behavior = Some(BehaviorSpec {
5926            on_init: Some(PathBuf::from("lib/init.lisp")),
5927            ..Default::default()
5928        });
5929        c.upgrade_from = vec![UpgradeFromEntry {
5930            from: "0.1.0".into(),
5931            instructions: vec![UpgradeInstruction::Restart],
5932        }];
5933        assert_eq!(
5934            c.declared_servico_slots(),
5935            vec![
5936                crate::render::M2_AUTHOR_KEY_LIMITS,
5937                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
5938                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
5939            ]
5940        );
5941    }
5942
5943    #[test]
5944    fn existing_manifests_unaffected_by_new_optional_slots() {
5945        // Regression test: a caixa.lisp authored before M2 typed slots
5946        // should still parse + serialize cleanly. The bare `defcaixa`
5947        // emitted by `Caixa::template` has none of the new fields.
5948        let src = Caixa::template("legacy");
5949        let c = Caixa::from_lisp(&src).unwrap();
5950        assert!(c.limits.is_none());
5951        assert!(c.behavior.is_none());
5952        assert!(c.upgrade_from.is_empty());
5953        assert!(c.estrategia.is_none());
5954        assert!(c.children.is_empty());
5955
5956        // And to_lisp emits a manifest with the new slots in the
5957        // empty/default state — round-trippable.
5958        let emitted = c.to_lisp();
5959        let back = Caixa::from_lisp(&emitted).unwrap();
5960        assert_eq!(c, back);
5961    }
5962
5963    #[test]
5964    fn validate_deps_accepts_canonical_caixa() {
5965        // Positive control: the bare template — zero deps, zero
5966        // deps_dev — passes the gate trivially. A future axis added to
5967        // `Dep::validate` mustn't regress an empty-deps caixa to a
5968        // build error.
5969        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5970        c.validate_deps().unwrap();
5971    }
5972
5973    #[test]
5974    fn validate_deps_rejects_invalid_versao_in_deps() {
5975        // Fail-before-pass-after pin: a malformed `:deps :versao`
5976        // surfaces at validate_deps() time, not at lacre-resolve time.
5977        // Mirrors `rejects_invalid_membro_versao_requirement` and
5978        // `validate_rejects_invalid_child_versao_requirement` on the
5979        // other two `:versao` axes.
5980        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5981        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
5982        let err = c.validate_deps().unwrap_err();
5983        assert!(
5984            matches!(
5985                err,
5986                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
5987                    if nome == "caixa-teia" && versao == "^bad-version"
5988            ),
5989            "got {err:?}"
5990        );
5991    }
5992
5993    #[test]
5994    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
5995        // Parity pin: `:deps-dev` must run through the same per-entry
5996        // validator as `:deps` — a typo in either axis surfaces the
5997        // same diagnostic. Without this leg, `:deps-dev` would be a
5998        // second-class citizen of the typed surface and an author
5999        // could land a build that passes validate_deps but fails at
6000        // `feira lock`-time when the dev-dep is resolved for a test
6001        // build.
6002        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6003        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
6004        let err = c.validate_deps().unwrap_err();
6005        assert!(
6006            matches!(
6007                err,
6008                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6009                    if nome == "tatara-check" && versao == "^^0.1"
6010            ),
6011            "got {err:?}"
6012        );
6013    }
6014
6015    #[test]
6016    fn validate_deps_runs_deps_before_deps_dev() {
6017        // Order pin: when both lists carry typos, the `:deps`
6018        // diagnostic surfaces first. The author's mental model is
6019        // "runtime deps are load-bearing; dev deps are scaffolding";
6020        // surfacing the runtime axis first matches that hierarchy.
6021        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6022        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
6023        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
6024        let err = c.validate_deps().unwrap_err();
6025        assert!(
6026            matches!(
6027                err,
6028                crate::dep::DepError::VersaoInvalid { ref nome, .. }
6029                    if nome == "runtime-dep"
6030            ),
6031            "expected `:deps` typo to surface first, got {err:?}"
6032        );
6033    }
6034
6035    #[test]
6036    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
6037        // Positive control sweep across both lists. Pin every
6038        // canonical Cargo-shaped form so a future tightening of the
6039        // accepted set surfaces here as a test failure (parity with
6040        // `accepts_canonical_membro_versao_forms` and
6041        // `validate_accepts_canonical_child_versao_forms`).
6042        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6043        c.deps = vec![
6044            Dep::simple("caret", "^0.1"),
6045            Dep::simple("tilde", "~0.1.2"),
6046            Dep::simple("exact", "0.1.0"),
6047            Dep::simple("wildcard", "*"),
6048            Dep::simple("multi-range", ">=0.1, <2"),
6049        ];
6050        c.deps_dev = vec![
6051            Dep::simple("dev-caret", "^0.1"),
6052            Dep::simple("dev-wildcard", "*"),
6053        ];
6054        c.validate_deps().unwrap();
6055    }
6056
6057    #[test]
6058    fn validate_deps_diagnostic_carries_offending_dep() {
6059        // Diagnostic-shape pin: the error names the offending entry's
6060        // `:nome` + `:versao` verbatim and carries a non-empty
6061        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
6062        // run can render the diagnostic without re-parsing.
6063        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6064        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
6065        let err = c.validate_deps().unwrap_err();
6066        let crate::dep::DepError::VersaoInvalid {
6067            nome,
6068            versao,
6069            reason,
6070        } = err
6071        else {
6072            panic!("expected VersaoInvalid, got other variant");
6073        };
6074        assert_eq!(nome, "caixa-teia");
6075        assert_eq!(versao, "not-a-req");
6076        assert!(
6077            !reason.is_empty(),
6078            "VersaoInvalid `reason` must carry the parser's wording verbatim"
6079        );
6080    }
6081
6082    #[test]
6083    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
6084        // Cross-axis pin: `validate_deps` walks both :deps and
6085        // :deps-dev through `Dep::validate`, and the new fonte gate
6086        // (`:tag` + `:branch` both set — the canonical "pin drift"
6087        // footgun) must surface from the :deps-dev arm with the
6088        // offending entry's :nome named. Pin the :deps-dev arm
6089        // explicitly so a future shortcut that only walks :deps
6090        // surfaces here as a regression.
6091        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6092        c.deps_dev = vec![Dep {
6093            nome: "dev-only".into(),
6094            versao: "^0.1".into(),
6095            fonte: Some(crate::DepSource::Git {
6096                repo: "github:p/x".into(),
6097                tag: Some("v1".into()),
6098                rev: None,
6099                branch: Some("main".into()),
6100            }),
6101            opcional: false,
6102            caracteristicas: vec![],
6103        }];
6104        let err = c.validate_deps().unwrap_err();
6105        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
6106            panic!("expected FontePinAmbiguous from :deps-dev walk");
6107        };
6108        assert_eq!(nome, "dev-only");
6109        assert!(pins.contains(":tag") && pins.contains(":branch"));
6110    }
6111
6112    #[test]
6113    fn validate_deps_rejects_empty_repo_in_deps() {
6114        // Parity pin on the :deps arm: an empty :repo on the runtime
6115        // deps list surfaces the same FonteRepoEmpty diagnostic the
6116        // dep.rs per-entry tests pin, naming the offending entry.
6117        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6118        c.deps = vec![Dep {
6119            nome: "runtime".into(),
6120            versao: "^0.1".into(),
6121            fonte: Some(crate::DepSource::Git {
6122                repo: String::new(),
6123                tag: Some("v1".into()),
6124                rev: None,
6125                branch: None,
6126            }),
6127            opcional: false,
6128            caracteristicas: vec![],
6129        }];
6130        let err = c.validate_deps().unwrap_err();
6131        assert!(
6132            matches!(
6133                err,
6134                crate::dep::DepError::FonteRepoEmpty { ref nome }
6135                    if nome == "runtime"
6136            ),
6137            "got {err:?}"
6138        );
6139    }
6140
6141    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
6142
6143    #[test]
6144    fn validate_deps_rejects_duplicate_nome_in_deps() {
6145        // Fail-before-pass-after pin: two `:deps` entries naming the same
6146        // caixa carry two `:versao` / `:fonte` / feature triples that the
6147        // caixa-resolver's lacre pipeline collapses (the second silently
6148        // overwrites the first at `concrete_versao`-resolve time). The
6149        // gate surfaces the duplicate at validate-time, naming the
6150        // offending caixa + the list, before the resolver-side silent
6151        // drop. Mirrors the peer typed-graph duplicate gates
6152        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
6153        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6154        c.deps = vec![
6155            Dep::simple("caixa-teia", "^0.1"),
6156            Dep::simple("caixa-teia", "^0.2"),
6157        ];
6158        let err = c.validate_deps().unwrap_err();
6159        assert!(
6160            matches!(
6161                err,
6162                crate::dep::DepError::DuplicateNome { ref nome, list }
6163                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6164            ),
6165            "got {err:?}"
6166        );
6167    }
6168
6169    #[test]
6170    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
6171        // Parity pin: `:deps-dev` runs through the same per-list
6172        // duplicate check as `:deps` — neither axis is a second-class
6173        // citizen of the set-not-multiset discipline.
6174        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6175        c.deps_dev = vec![
6176            Dep::simple("tatara-check", "*"),
6177            Dep::simple("tatara-check", "^0.1"),
6178        ];
6179        let err = c.validate_deps().unwrap_err();
6180        assert!(
6181            matches!(
6182                err,
6183                crate::dep::DepError::DuplicateNome { ref nome, list }
6184                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
6185            ),
6186            "got {err:?}"
6187        );
6188    }
6189
6190    #[test]
6191    fn validate_deps_accepts_cross_list_same_nome() {
6192        // The Cargo `[dependencies]` + `[dev-dependencies]` override
6193        // convention is preserved: a name appearing in *both* lists is
6194        // valid (the dev-pin overrides at test/dev time). Only
6195        // within-list duplicates are structurally incoherent — pin the
6196        // permissive cross-list semantics so a future shortcut that
6197        // collapses the two seen-sets into one surfaces here as a test
6198        // failure.
6199        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6200        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
6201        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
6202        c.validate_deps().unwrap();
6203    }
6204
6205    #[test]
6206    fn validate_deps_accepts_distinct_nome_in_both_lists() {
6207        // Positive control: distinct names within each list pass — the
6208        // gate's identity element on the canonical authoring shape.
6209        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6210        c.deps = vec![
6211            Dep::simple("caixa-teia", "^0.1"),
6212            Dep::simple("pleme-mesh", "*"),
6213        ];
6214        c.deps_dev = vec![
6215            Dep::simple("tatara-check", "*"),
6216            Dep::simple("dev-shim", "^0.1"),
6217        ];
6218        c.validate_deps().unwrap();
6219    }
6220
6221    #[test]
6222    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
6223        // Diagnostic-precedence pin: a malformed `:versao` on the
6224        // duplicating entry surfaces its narrower `VersaoInvalid`
6225        // diagnostic first, before the cross-entry duplicate gate fires
6226        // — the canonical "per-entry shape before cross-entry uniqueness"
6227        // precedence every peer set-not-multiset gate establishes
6228        // (`*_invalid_fires_before_duplicate_check` pins on
6229        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
6230        // `validate_upgrade_from`).
6231        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6232        c.deps = vec![
6233            Dep::simple("caixa-teia", "^0.1"),
6234            Dep::simple("caixa-teia", "^bad-version"),
6235        ];
6236        let err = c.validate_deps().unwrap_err();
6237        assert!(
6238            matches!(
6239                err,
6240                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6241                    if nome == "caixa-teia" && versao == "^bad-version"
6242            ),
6243            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
6244        );
6245    }
6246
6247    #[test]
6248    fn validate_deps_duplicate_diagnostic_names_first_collision() {
6249        // First-collision determinism pin: with three entries naming the
6250        // same caixa, the first colliding pair surfaces — not the last.
6251        // Mirrors the peer first-collision posture on every
6252        // duplicate-target gate
6253        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
6254        // — the second entry is the first collision; this gate uses the
6255        // same shape: the second entry's `:nome` lands in the diagnostic
6256        // because `seen.insert(first.nome)` already populated the set).
6257        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6258        c.deps = vec![
6259            Dep::simple("caixa-teia", "^0.1"),
6260            Dep::simple("caixa-teia", "^0.2"),
6261            Dep::simple("caixa-teia", "^0.3"),
6262        ];
6263        let err = c.validate_deps().unwrap_err();
6264        // The diagnostic carries the offending caixa name; the
6265        // implementation surfaces on the *second* entry (the first
6266        // collision), so the test pins the `:nome` value.
6267        assert!(
6268            matches!(
6269                err,
6270                crate::dep::DepError::DuplicateNome { ref nome, list }
6271                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6272            ),
6273            "got {err:?}"
6274        );
6275    }
6276
6277    #[test]
6278    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
6279        // Cross-list precedence pin: when both lists carry duplicates,
6280        // the `:deps` diagnostic surfaces first — same author-mental-
6281        // model ordering the `validate_deps_runs_deps_before_deps_dev`
6282        // pin establishes for malformed `:versao` (runtime axis before
6283        // dev axis).
6284        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6285        c.deps = vec![
6286            Dep::simple("runtime-dep", "^0.1"),
6287            Dep::simple("runtime-dep", "^0.2"),
6288        ];
6289        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
6290        let err = c.validate_deps().unwrap_err();
6291        assert!(
6292            matches!(
6293                err,
6294                crate::dep::DepError::DuplicateNome { ref nome, list }
6295                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6296            ),
6297            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
6298        );
6299    }
6300
6301    #[test]
6302    fn validate_deps_empty_lists_pass_duplicate_gate() {
6303        // Empty-set identity pin: the bare template (zero deps, zero
6304        // deps_dev) passes the duplicate gate as the gate's identity
6305        // element. A future tighten that conflates "empty" with
6306        // "missing" would regress this baseline.
6307        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6308        c.validate_deps().unwrap();
6309    }
6310
6311    #[test]
6312    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
6313        // Diagnostic-shape pin: the `list:` field tags which list the
6314        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
6315        // `feira lint` run can route the author to the right block in
6316        // their caixa.lisp without re-deriving the list from context.
6317        // Same self-locating shape every peer per-axis diagnostic
6318        // already exposes.
6319        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6320        c.deps_dev = vec![
6321            Dep::simple("dev-thing", "*"),
6322            Dep::simple("dev-thing", "^0.1"),
6323        ];
6324        let err = c.validate_deps().unwrap_err();
6325        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
6326            panic!("expected DuplicateNome from :deps-dev walk");
6327        };
6328        assert_eq!(nome, "dev-thing");
6329        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
6330    }
6331
6332    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
6333
6334    #[test]
6335    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
6336        // Thread-through pin on `:deps`: the per-entry
6337        // `Dep::validate_caracteristicas` gate fires inside
6338        // `Caixa::validate_deps`'s linear walk, so a malformed feature
6339        // list on any `:deps` entry surfaces as a `DepError` from
6340        // `validate_deps` — the same reachability shape every per-entry
6341        // `Dep::validate` arm threads through. Without this pin a future
6342        // shortcut that skips the per-entry `Dep::validate` call on the
6343        // cross-entry-uniqueness path would mask the within-entry
6344        // `:caracteristicas` gates.
6345        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6346        c.deps = vec![Dep {
6347            nome: "caixa-teia".into(),
6348            versao: "^0.1".into(),
6349            fonte: None,
6350            opcional: false,
6351            caracteristicas: vec!["http".into(), "http".into()],
6352        }];
6353        let err = c.validate_deps().unwrap_err();
6354        let crate::dep::DepError::CaracteristicaDuplicate {
6355            nome,
6356            caracteristica,
6357        } = err
6358        else {
6359            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
6360        };
6361        assert_eq!(nome, "caixa-teia");
6362        assert_eq!(caracteristica, "http");
6363    }
6364
6365    #[test]
6366    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
6367        // Peer thread-through pin on `:deps-dev`: same reachability as
6368        // the `:deps` arm above, on the dev-only authoring axis. Pins
6369        // that the `validate_deps` walk visits both lists' per-entry
6370        // gates uniformly. The empty-feature arm carries here so both
6371        // new `:caracteristicas` arms are surfaced via at least one
6372        // `validate_deps` thread-through.
6373        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6374        c.deps_dev = vec![Dep {
6375            nome: "caixa-teia".into(),
6376            versao: "^0.1".into(),
6377            fonte: None,
6378            opcional: false,
6379            caracteristicas: vec![String::new()],
6380        }];
6381        let err = c.validate_deps().unwrap_err();
6382        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
6383            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
6384        };
6385        assert_eq!(nome, "caixa-teia");
6386    }
6387
6388    #[test]
6389    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
6390        // Thread-through pin on `:deps`: the per-entry
6391        // `Dep::validate_caracteristicas` value-shape gate (lifted via
6392        // `crate::render::is_cargo_feature_name`) fires inside
6393        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
6394        // a structurally invalid feature name on any `:deps` entry
6395        // surfaces as `DepError::CaracteristicaInvalid` from
6396        // `validate_deps` — the same reachability shape every per-entry
6397        // `Dep::validate` arm threads through. Without this pin a
6398        // future shortcut that skips the per-entry `Dep::validate` call
6399        // on the cross-entry-uniqueness path would mask the within-
6400        // entry `:caracteristicas` value-shape gate.
6401        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6402        c.deps = vec![Dep {
6403            nome: "caixa-teia".into(),
6404            versao: "^0.1".into(),
6405            fonte: None,
6406            opcional: false,
6407            caracteristicas: vec!["+http".into()],
6408        }];
6409        let err = c.validate_deps().unwrap_err();
6410        let crate::dep::DepError::CaracteristicaInvalid {
6411            nome,
6412            caracteristica,
6413            ..
6414        } = err
6415        else {
6416            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
6417        };
6418        assert_eq!(nome, "caixa-teia");
6419        assert_eq!(caracteristica, "+http");
6420    }
6421
6422    #[test]
6423    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
6424        // Peer thread-through pin on `:deps-dev`: same reachability as
6425        // the `:deps` arm above, on the dev-only authoring axis. The
6426        // `http/json` shape carries here so the segment-separator
6427        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
6428        // confusion footgun) is surfaced via the cross-entry walk too —
6429        // pinning that the `:deps-dev` list visits the same per-entry
6430        // value-shape gate as the `:deps` list.
6431        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6432        c.deps_dev = vec![Dep {
6433            nome: "caixa-teia".into(),
6434            versao: "^0.1".into(),
6435            fonte: None,
6436            opcional: false,
6437            caracteristicas: vec!["http/json".into()],
6438        }];
6439        let err = c.validate_deps().unwrap_err();
6440        let crate::dep::DepError::CaracteristicaInvalid {
6441            nome,
6442            caracteristica,
6443            ..
6444        } = err
6445        else {
6446            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
6447        };
6448        assert_eq!(nome, "caixa-teia");
6449        assert_eq!(caracteristica, "http/json");
6450    }
6451
6452    #[test]
6453    fn to_lisp_preserves_deps() {
6454        let src = r#"
6455(defcaixa
6456  :nome "x"
6457  :versao "0.1.0"
6458  :kind Biblioteca
6459  :deps ((:nome "a" :versao "^0.1")
6460         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
6461"#;
6462        let c1 = Caixa::from_lisp(src).unwrap();
6463        let emitted = c1.to_lisp();
6464        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
6465        assert_eq!(c1.deps, c2.deps);
6466    }
6467
6468    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
6469
6470    fn caixa_with_nome(nome: &str) -> Caixa {
6471        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
6472        c.nome = nome.to_string();
6473        c
6474    }
6475
6476    #[test]
6477    fn validate_nome_accepts_canonical_template() {
6478        // Positive control: the bare `feira init`-style template's
6479        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
6480        // not regress this baseline shape. A future tightening of the
6481        // accepted set surfaces here as a test failure first.
6482        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6483        c.validate_nome().unwrap();
6484    }
6485
6486    #[test]
6487    fn validate_nome_accepts_canonical_forms() {
6488        // Positive-set sweep: each realistic caixa-name shape the K8s
6489        // apiserver accepts as a `metadata.name` label must pass —
6490        // single-word, hyphen-joined, version-suffixed, single-char,
6491        // two-char, digit-start (DNS-1123 allows this; the stricter
6492        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
6493        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
6494        // the peer member-name axis.
6495        for nome in [
6496            "checkout",
6497            "cart-v2",
6498            "a",
6499            "db",
6500            "3rd-party-shim",
6501            "payment-retry",
6502            "0",
6503        ] {
6504            caixa_with_nome(nome)
6505                .validate_nome()
6506                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
6507        }
6508    }
6509
6510    #[test]
6511    fn validate_nome_rejects_empty() {
6512        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
6513        // an empty `:nome` (the derive macro stores the raw String);
6514        // the gate's empty arm names the offending axis with a narrower
6515        // diagnostic than the `NomeInvalid` parse arm would emit.
6516        let c = caixa_with_nome("");
6517        let err = c.validate_nome().unwrap_err();
6518        assert_eq!(err, ManifestError::NomeEmpty);
6519    }
6520
6521    #[test]
6522    fn validate_nome_rejects_uppercase() {
6523        // The canonical "I copied the TitleCase display name verbatim"
6524        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
6525        // admission on every derived artifact (Helm chart, ComputeUnit,
6526        // CNP, HTTPRoute, label values); the gate moves the diagnostic
6527        // to the source `caixa.lisp` and the reason suggests the
6528        // lowercased fix verbatim.
6529        let c = caixa_with_nome("MyApp");
6530        let err = c.validate_nome().unwrap_err();
6531        let ManifestError::NomeInvalid { nome, reason } = err else {
6532            panic!("expected NomeInvalid for uppercase :nome");
6533        };
6534        assert_eq!(nome, "MyApp");
6535        assert!(
6536            reason.contains("uppercase") && reason.contains("myapp"),
6537            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
6538        );
6539    }
6540
6541    #[test]
6542    fn validate_nome_rejects_underscore() {
6543        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
6544        // `_`; the apiserver rejects on admission across every derived
6545        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
6546        // and `:children :caixa` (31bfa43).
6547        let c = caixa_with_nome("my_app");
6548        let err = c.validate_nome().unwrap_err();
6549        assert!(
6550            matches!(
6551                err,
6552                ManifestError::NomeInvalid { ref nome, ref reason }
6553                    if nome == "my_app" && reason.contains('_')
6554            ),
6555            "got {err:?}"
6556        );
6557    }
6558
6559    #[test]
6560    fn validate_nome_rejects_dot() {
6561        // A `:nome` is a single DNS-1123 label, not a subdomain. The
6562        // "I want to namespace with `.`" footgun the gate redirects to
6563        // `-` via the shared predicate's reason wording.
6564        let c = caixa_with_nome("team.app");
6565        let err = c.validate_nome().unwrap_err();
6566        assert!(
6567            matches!(
6568                err,
6569                ManifestError::NomeInvalid { ref nome, ref reason }
6570                    if nome == "team.app" && reason.contains('.')
6571            ),
6572            "got {err:?}"
6573        );
6574    }
6575
6576    #[test]
6577    fn validate_nome_rejects_leading_hyphen() {
6578        // DNS-1123 boundary rule: the label must start with an ASCII
6579        // alphanumeric. Pin the leading-`-` arm explicitly.
6580        let c = caixa_with_nome("-app");
6581        let err = c.validate_nome().unwrap_err();
6582        assert!(
6583            matches!(
6584                err,
6585                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
6586            ),
6587            "got {err:?}"
6588        );
6589    }
6590
6591    #[test]
6592    fn validate_nome_rejects_trailing_hyphen() {
6593        // Symmetric arm of the boundary rule, pinned separately so a
6594        // future relaxation that only checks the leading position
6595        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
6596        // and `_with_trailing_hyphen` on the supervisor / aplicacao
6597        // axes.
6598        let c = caixa_with_nome("app-");
6599        let err = c.validate_nome().unwrap_err();
6600        assert!(
6601            matches!(
6602                err,
6603                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
6604            ),
6605            "got {err:?}"
6606        );
6607    }
6608
6609    #[test]
6610    fn validate_nome_rejects_unicode() {
6611        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
6612        // bytes are rejected by the K8s apiserver on every name axis.
6613        let c = caixa_with_nome("café");
6614        let err = c.validate_nome().unwrap_err();
6615        assert!(
6616            matches!(
6617                err,
6618                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
6619            ),
6620            "got {err:?}"
6621        );
6622    }
6623
6624    #[test]
6625    fn validate_nome_rejects_whitespace() {
6626        // The paste-from-sketch / paste-from-spec footgun. Internal
6627        // whitespace is rejected by every K8s name axis.
6628        let c = caixa_with_nome("my app");
6629        let err = c.validate_nome().unwrap_err();
6630        assert!(
6631            matches!(
6632                err,
6633                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
6634            ),
6635            "got {err:?}"
6636        );
6637    }
6638
6639    #[test]
6640    fn validate_nome_rejects_too_long() {
6641        // 64-byte boundary pin: the K8s apiserver rejects any
6642        // `metadata.name` over 63 bytes at admission; the diagnostic
6643        // names both the 63-byte cap and the actual length so the
6644        // author can shorten in one edit. Mirrors `_too_long` on the
6645        // peer member-/cluster-/child-name axes.
6646        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
6647        let c = caixa_with_nome(&over);
6648        let err = c.validate_nome().unwrap_err();
6649        let ManifestError::NomeInvalid { nome, reason } = err else {
6650            panic!("expected NomeInvalid for over-cap :nome");
6651        };
6652        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
6653        assert!(
6654            reason.contains("63") && reason.contains("64"),
6655            "diagnostic must name the cap + actual length, got {reason:?}"
6656        );
6657    }
6658
6659    #[test]
6660    fn nome_max_length_validates() {
6661        // The 63-byte cap exactly — the boundary-accepting case pinned
6662        // alongside `validate_nome_rejects_too_long` so a future cap
6663        // shift surfaces both arms simultaneously. Mirrors
6664        // `membro_caixa_max_length_validates`,
6665        // `placement_cluster_max_length_validates`,
6666        // `child_caixa_max_length_validates`.
6667        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
6668        caixa_with_nome(&at_cap).validate_nome().unwrap();
6669    }
6670
6671    #[test]
6672    fn nome_empty_takes_precedence_over_invalid() {
6673        // Order pin: the empty arm fires before the predicate is
6674        // consulted. Empty < invalid in self-locating-ness — the
6675        // narrower `NomeEmpty` diagnostic doesn't carry a useless
6676        // `nome: ""` reference into the parser-shaped reason. Mirrors
6677        // `membro_caixa_empty_takes_precedence_over_invalid` on the
6678        // peer axis (3f9d7a0).
6679        let c = caixa_with_nome("");
6680        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
6681    }
6682
6683    #[test]
6684    fn nome_invalid_diagnostic_carries_offending_nome() {
6685        // Diagnostic-shape pin: the error names the offending `:nome`
6686        // verbatim with a non-empty parser-shaped reason, so a `feira
6687        // lint` run can render the diagnostic without re-parsing.
6688        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
6689        let c = caixa_with_nome("MyApp");
6690        let err = c.validate_nome().unwrap_err();
6691        let ManifestError::NomeInvalid { nome, reason } = err else {
6692            panic!("expected NomeInvalid variant");
6693        };
6694        assert_eq!(nome, "MyApp");
6695        assert!(
6696            !reason.is_empty(),
6697            "NomeInvalid `reason` must carry the predicate's wording verbatim"
6698        );
6699    }
6700
6701    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
6702    //
6703    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
6704    // via DNS-1123; this second-axis gate caps the joint
6705    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
6706    // canonical [`crate::lareira_chart_name`] helper's doc comment
6707    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
6708    // "the M4 admission webhook will pin the joint-length invariant
6709    // when it lands". These tests pin it at the manifest-validate
6710    // layer instead, fail-before-pass-after on the 56-byte boundary.
6711
6712    #[test]
6713    fn validate_nome_chart_name_budget_accepts_canonical_template() {
6714        // Positive control: the bare `feira init`-style template's
6715        // `:nome` ("demo") sits far below the cap; the gate must not
6716        // regress this baseline. Same shape every peer
6717        // value-shape-gate baseline pin uses.
6718        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6719        c.validate_nome_chart_name_budget().unwrap();
6720    }
6721
6722    #[test]
6723    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
6724        // Positive-set sweep across the canonical author surface every
6725        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
6726        // `worker`, the `checkout-aplicacao` example members, the
6727        // `akeyless-attest` caixa-tatara fixture). Every value sits
6728        // far below the 55-byte per-`:nome` budget. Same shape every
6729        // peer per-axis baseline pin uses.
6730        for nome in [
6731            "hello-rio",
6732            "cart",
6733            "checkout",
6734            "worker",
6735            "akeyless-attest",
6736            "demo",
6737            "a",
6738        ] {
6739            caixa_with_nome(nome)
6740                .validate_nome_chart_name_budget()
6741                .unwrap_or_else(|e| {
6742                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
6743                });
6744        }
6745    }
6746
6747    #[test]
6748    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
6749        // Boundary-accepting case at the 55-byte per-`:nome` budget —
6750        // the joint chart name is exactly 63 bytes, the DNS-1123 label
6751        // cap. Pinned alongside the rejecting-arm test so a future cap
6752        // shift surfaces both arms simultaneously. Mirrors
6753        // `nome_max_length_validates` on the peer bare-`:nome` axis.
6754        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
6755        caixa_with_nome(&at_cap)
6756            .validate_nome_chart_name_budget()
6757            .unwrap();
6758    }
6759
6760    #[test]
6761    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
6762        // Fail-before-pass-after pin on the 56-byte boundary: the
6763        // smallest `:nome` length that overflows the joint chart-name
6764        // cap. The inner [`is_dns_1123_label`] gate
6765        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
6766        // this gate it silently passed the manifest-validate cascade
6767        // and surfaced as a `helm lint` / apiserver rejection on the
6768        // rendered chart name far from the source `caixa.lisp`, with
6769        // no field naming the overflow. With this gate the diagnostic
6770        // names the offending `:nome` verbatim alongside the rendered
6771        // chart name and the budget, so the author can shorten in one
6772        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
6773        // bare-`:nome` axis.
6774        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
6775        let c = caixa_with_nome(&over);
6776        let err = c.validate_nome_chart_name_budget().unwrap_err();
6777        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
6778            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
6779        };
6780        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
6781        assert_eq!(nome, over);
6782        assert!(
6783            reason.contains("63") && reason.contains("64") && reason.contains("55"),
6784            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
6785             and the per-`:nome` budget (55), got {reason:?}"
6786        );
6787    }
6788
6789    #[test]
6790    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
6791        // The 63-byte `:nome` boundary — passes the bare-`:nome`
6792        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
6793        // joint chart name that overflows the DNS-1123 label cap
6794        // structurally. The most stringent fail-before-pass-after
6795        // surface: every `:nome` in the 56..=63-byte range passed the
6796        // prior cascade and broke at admission.
6797        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
6798        let c = caixa_with_nome(&bare_max);
6799        // The bare-`:nome` gate accepts the 63-byte length.
6800        c.validate_nome().unwrap();
6801        // The new joint-length gate rejects it.
6802        let err = c.validate_nome_chart_name_budget().unwrap_err();
6803        assert!(
6804            matches!(
6805                err,
6806                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
6807                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
6808            ),
6809            "got {err:?}"
6810        );
6811    }
6812
6813    #[test]
6814    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
6815        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
6816        // name appears verbatim in the diagnostic so the author sees
6817        // exactly the string the apiserver / `helm lint` would have
6818        // rejected — no re-derivation required to grep the source.
6819        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
6820        // on the bare-`:nome` axis.
6821        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
6822        let c = caixa_with_nome(&over);
6823        let err = c.validate_nome_chart_name_budget().unwrap_err();
6824        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
6825            panic!("expected NomeChartNameBudgetExceeded variant");
6826        };
6827        assert_eq!(nome, over);
6828        let expected_chart = crate::lareira_chart_name(&over);
6829        assert!(
6830            reason.contains(&expected_chart),
6831            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
6832             got {reason:?}"
6833        );
6834        assert!(
6835            reason.contains("lareira-"),
6836            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
6837        );
6838    }
6839
6840    #[test]
6841    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
6842        // Order pin on the layout cascade: the narrower
6843        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
6844        // joint-length budget. A structurally-malformed `:nome` (here:
6845        // uppercase) surfaces its specific shape error rather than
6846        // the chart-name-budget error, even when the joint length
6847        // would also overflow — the narrower diagnostic is more
6848        // self-locating. Mirrors the cascade-precedence pins peer
6849        // gates already use (e.g. `EntradaParaEmpty` before
6850        // `EntradaParaInvalid`).
6851        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
6852        let c = caixa_with_nome(&over);
6853        // The bare-shape gate fires first.
6854        let err = c.validate_nome().unwrap_err();
6855        assert!(
6856            matches!(err, ManifestError::NomeInvalid { .. }),
6857            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
6858        );
6859        // And the layout verify cascade surfaces that diagnostic, not
6860        // the budget arm. Inject a path-exists oracle so the cascade
6861        // gets past the manifest-presence check and into the
6862        // value-shape gates.
6863        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
6864        let err = crate::LayoutInvariants::verify(
6865            &layout,
6866            &c,
6867            std::path::Path::new("/tmp/caixa-test-fake-root"),
6868        )
6869        .unwrap_err();
6870        let issue = err.to_string();
6871        assert!(
6872            issue.contains("DNS-1123") || issue.contains("uppercase"),
6873            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
6874             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
6875        );
6876    }
6877
6878    #[test]
6879    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
6880        // Cross-axis envelope pin: the layout cascade wraps both
6881        // bare-`:nome` and joint-length-`:nome` failures through the
6882        // same [`LayoutError::NomeViolation`] envelope, since both
6883        // arms are on the `:nome` axis. The user's diagnostic stays
6884        // self-locating ("which axis"), and a future consumer that
6885        // dispatches on the layout-error variant (e.g. a `feira lint`
6886        // exit-code mapping) sees a single per-axis envelope. The
6887        // wrapped `issue:` carries the full inner diagnostic.
6888        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
6889        let c = caixa_with_nome(&over);
6890        // The bare-shape gate accepts.
6891        c.validate_nome().unwrap();
6892        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
6893        let err = crate::LayoutInvariants::verify(
6894            &layout,
6895            &c,
6896            std::path::Path::new("/tmp/caixa-test-fake-root"),
6897        )
6898        .unwrap_err();
6899        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
6900            panic!("expected LayoutError::NomeViolation, got {err:?}");
6901        };
6902        assert_eq!(caixa, over);
6903        assert!(
6904            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
6905            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
6906        );
6907    }
6908
6909    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
6910
6911    fn caixa_with_versao(versao: &str) -> Caixa {
6912        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6913        c.versao = versao.to_string();
6914        c
6915    }
6916
6917    #[test]
6918    fn validate_versao_accepts_canonical_template() {
6919        // Positive control: the bare `feira init`-style template's
6920        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
6921        // must not regress this baseline shape. A future tightening of
6922        // the accepted set surfaces here as a test failure first.
6923        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6924        c.validate_versao().unwrap();
6925    }
6926
6927    #[test]
6928    fn validate_versao_accepts_canonical_forms() {
6929        // Positive-set sweep: each realistic SemVer-2 shape the
6930        // substrate's downstream consumers accept must pass — bare
6931        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
6932        // build metadata (`+build.42`), the combined form, and the
6933        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
6934        // the peer `:nome` axis (6c992f8).
6935        for versao in [
6936            "0.1.0",
6937            "0.0.0",
6938            "1.0.0",
6939            "0.2.0-rc.1",
6940            "1.0.0-alpha.0",
6941            "1.0.0+build.42",
6942            "1.0.0-rc.1+build.42",
6943            "10.20.30",
6944        ] {
6945            caixa_with_versao(versao)
6946                .validate_versao()
6947                .unwrap_or_else(|e| {
6948                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
6949                });
6950        }
6951    }
6952
6953    #[test]
6954    fn validate_versao_rejects_empty() {
6955        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
6956        // an empty `:versao` (the derive macro stores the raw String);
6957        // the gate's empty arm names the offending axis with a narrower
6958        // diagnostic than the `VersaoInvalid` parse arm would emit.
6959        // Mirrors `validate_nome_rejects_empty` (6c992f8).
6960        let c = caixa_with_versao("");
6961        let err = c.validate_versao().unwrap_err();
6962        assert_eq!(err, ManifestError::VersaoEmpty);
6963    }
6964
6965    #[test]
6966    fn validate_versao_rejects_git_tag_shape() {
6967        // The canonical "I copied the git tag verbatim" footgun —
6968        // `feira publish` *emits* `v<versao>` git tags, so a leaked
6969        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
6970        // shift every downstream consumer's version axis. `semver`
6971        // rejects the leading `v` at parse time; the gate moves the
6972        // diagnostic to the source `caixa.lisp`.
6973        let c = caixa_with_versao("v0.1.0");
6974        let err = c.validate_versao().unwrap_err();
6975        let ManifestError::VersaoInvalid { versao, reason } = err else {
6976            panic!("expected VersaoInvalid for git-tag-shape :versao");
6977        };
6978        assert_eq!(versao, "v0.1.0");
6979        assert!(
6980            !reason.is_empty(),
6981            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
6982        );
6983    }
6984
6985    #[test]
6986    fn validate_versao_rejects_missing_patch() {
6987        // The canonical "I shortened it" footgun — SemVer-2 requires
6988        // three parts. Cargo's `version =` field accepts the shortened
6989        // form as a requirement, conflating the two leaks across the
6990        // typed `:deps :versao` vs top-level `:versao` axes; the gate
6991        // pins the top-level axis to the strict three-part shape.
6992        let c = caixa_with_versao("0.1");
6993        let err = c.validate_versao().unwrap_err();
6994        assert!(
6995            matches!(
6996                err,
6997                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
6998            ),
6999            "got {err:?}"
7000        );
7001    }
7002
7003    #[test]
7004    fn validate_versao_rejects_requirement_shape() {
7005        // The canonical "I leaked a requirement into a version" footgun —
7006        // the typed `:deps :versao` / `:membros :versao` axes accept
7007        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
7008        // concrete `Version`. Without this gate the two typed surfaces
7009        // would silently overlap, and a top-level `^0.1` would surface
7010        // at `helm install` time as a Chart.yaml version rejection far
7011        // from the source `caixa.lisp`.
7012        let c = caixa_with_versao("^0.1");
7013        let err = c.validate_versao().unwrap_err();
7014        assert!(
7015            matches!(
7016                err,
7017                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
7018            ),
7019            "got {err:?}"
7020        );
7021    }
7022
7023    #[test]
7024    fn validate_versao_rejects_docker_tag_shape() {
7025        // The "I confused it with a docker tag" footgun — `latest`,
7026        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
7027        // SemVer rejects at parse time; the gate moves the diagnostic
7028        // to the source `caixa.lisp`.
7029        for bad in ["latest", "main", "stable"] {
7030            let c = caixa_with_versao(bad);
7031            let err = c.validate_versao().unwrap_err();
7032            assert!(
7033                matches!(
7034                    err,
7035                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
7036                ),
7037                "got {err:?} for {bad:?}"
7038            );
7039        }
7040    }
7041
7042    #[test]
7043    fn validate_versao_rejects_four_part_form() {
7044        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
7045        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
7046        // semver crate rejects the extra `.0` at parse time.
7047        let c = caixa_with_versao("0.1.0.0");
7048        let err = c.validate_versao().unwrap_err();
7049        assert!(
7050            matches!(
7051                err,
7052                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
7053            ),
7054            "got {err:?}"
7055        );
7056    }
7057
7058    #[test]
7059    fn versao_empty_takes_precedence_over_invalid() {
7060        // Order pin: the empty arm fires before the parser is consulted.
7061        // Empty < invalid in self-locating-ness — the narrower
7062        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
7063        // reference into the parser-shaped reason. Mirrors
7064        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
7065        // peer axis.
7066        let c = caixa_with_versao("");
7067        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
7068    }
7069
7070    #[test]
7071    fn versao_invalid_diagnostic_carries_offending_versao() {
7072        // Diagnostic-shape pin: the error names the offending `:versao`
7073        // verbatim with a non-empty parser-shaped reason, so a `feira
7074        // lint` run can render the diagnostic without re-parsing.
7075        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
7076        let c = caixa_with_versao("v0.1.0");
7077        let err = c.validate_versao().unwrap_err();
7078        let ManifestError::VersaoInvalid { versao, reason } = err else {
7079            panic!("expected VersaoInvalid variant");
7080        };
7081        assert_eq!(versao, "v0.1.0");
7082        assert!(
7083            !reason.is_empty(),
7084            "VersaoInvalid `reason` must carry the parser's wording verbatim"
7085        );
7086    }
7087
7088    #[test]
7089    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
7090        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
7091        // for `:upgrade-from :from` must also pass `validate_versao` —
7092        // the two `:versao`-typed surfaces (top-level `:versao`,
7093        // `:upgrade-from :from`) consume the *same* `semver::Version`
7094        // parser, so they must agree on the accepted set. Without this
7095        // pin, a future tightening of one axis could silently diverge
7096        // from the other. Mirrors the `:versao` requirement-axis
7097        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
7098        // commits established.
7099        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
7100            // From the canonical UpgradeFromEntry round-trip fixture
7101            // (`upgrade::tests::round_trip_load_module` peers).
7102            let entry = crate::UpgradeFromEntry {
7103                from: versao.to_string(),
7104                instructions: Vec::new(),
7105            };
7106            entry
7107                .validate()
7108                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
7109            caixa_with_versao(versao)
7110                .validate_versao()
7111                .unwrap_or_else(|e| {
7112                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
7113                });
7114        }
7115    }
7116
7117    // ── Caixa::validate_restart_window — supervisor restart-window
7118    //    folds through the shared `supervisor::duration_codec` ────────
7119
7120    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
7121        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
7122        c.kind = CaixaKind::Supervisor;
7123        c.restart_window = window.map(str::to_string);
7124        c
7125    }
7126
7127    #[test]
7128    fn validate_restart_window_accepts_none() {
7129        // The canonical "omit the slot to express no reset" shape — a
7130        // `None` raw string is the absence of the typed
7131        // `:restart-window` slot, which is exactly the SupervisorSpec
7132        // "never reset" semantics. The gate must be a no-op here; a
7133        // future tightening that rejected `None` would force every
7134        // supervisor caixa to authoring-time pin a window even when
7135        // the OTP semantics call for none.
7136        caixa_with_restart_window(None)
7137            .validate_restart_window()
7138            .unwrap();
7139    }
7140
7141    #[test]
7142    fn validate_restart_window_accepts_canonical_forms() {
7143        // Positive-set sweep across the canonical authoring units the
7144        // shared `supervisor::duration_codec::parse` accepts —
7145        // matches the codec-side `parse_accepts_integer_canonical_units`
7146        // pin in supervisor::tests so a future codec-side tightening
7147        // surfaces simultaneously on both axes.
7148        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
7149            caixa_with_restart_window(Some(window))
7150                .validate_restart_window()
7151                .unwrap_or_else(|e| {
7152                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
7153                });
7154        }
7155    }
7156
7157    #[test]
7158    fn validate_restart_window_rejects_fractional_seconds() {
7159        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
7160        // as f64 to 1.5 → renders back as `"1500ms"` on first
7161        // serialize). Prior to the fold + this gate, the inline
7162        // `parse_window_inline` accepted f64 magnitudes and silently
7163        // produced a `Duration::from_secs_f64(1.5)`, divergent from
7164        // the shared codec's integer-magnitude discipline on the
7165        // serde-routed siblings. The gate now surfaces a self-locating
7166        // diagnostic at the manifest layer.
7167        let err = caixa_with_restart_window(Some("1.5s"))
7168            .validate_restart_window()
7169            .unwrap_err();
7170        let ManifestError::RestartWindowMalformed {
7171            restart_window,
7172            reason,
7173        } = err
7174        else {
7175            panic!("expected RestartWindowMalformed for fractional seconds");
7176        };
7177        assert_eq!(restart_window, "1.5s");
7178        assert!(
7179            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
7180            "diagnostic must carry shared-codec wording, got {reason:?}"
7181        );
7182    }
7183
7184    #[test]
7185    fn validate_restart_window_rejects_decimal_shaped_integer() {
7186        // The `"1.0s"` class — numerically `1s` exactly, but the
7187        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
7188        // gets the same canonical-form diagnostic.
7189        let err = caixa_with_restart_window(Some("1.0s"))
7190            .validate_restart_window()
7191            .unwrap_err();
7192        assert!(
7193            matches!(
7194                err,
7195                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7196                    if restart_window == "1.0s"
7197            ),
7198            "got {err:?}"
7199        );
7200    }
7201
7202    #[test]
7203    fn validate_restart_window_rejects_half_unit_minute() {
7204        // `"0.5m"` is the unit-fraction footgun — author writes a
7205        // human-readable half-minute, the prior inline parser silently
7206        // produced `Duration::from_secs_f64(30.0)` and serde
7207        // re-emitted as `"30s"`, rewriting author intent. The gate
7208        // closes the loop at the manifest layer.
7209        let err = caixa_with_restart_window(Some("0.5m"))
7210            .validate_restart_window()
7211            .unwrap_err();
7212        let ManifestError::RestartWindowMalformed {
7213            restart_window,
7214            reason,
7215        } = err
7216        else {
7217            panic!("expected RestartWindowMalformed");
7218        };
7219        assert_eq!(restart_window, "0.5m");
7220        assert!(
7221            reason.contains("\"30s\""),
7222            "diagnostic must point at the canonical-form remediation, got {reason:?}"
7223        );
7224    }
7225
7226    #[test]
7227    fn validate_restart_window_rejects_leading_sign() {
7228        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
7229        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
7230        // and was caught by the `num < 0.0` arm which silently
7231        // returned `None`, dropping the author-supplied window). The
7232        // shared codec's digit-only gate rejects both with a unified
7233        // canonical-form diagnostic; the manifest-layer wrapper names
7234        // the offending value.
7235        for bad in ["+30s", "-30s"] {
7236            let err = caixa_with_restart_window(Some(bad))
7237                .validate_restart_window()
7238                .unwrap_err();
7239            assert!(
7240                matches!(
7241                    err,
7242                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
7243                        if restart_window == bad
7244                ),
7245                "got {err:?} for {bad:?}"
7246            );
7247        }
7248    }
7249
7250    #[test]
7251    fn validate_restart_window_rejects_unknown_unit() {
7252        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
7253        // unit dispatch surfaces an `unknown duration unit` reason;
7254        // the manifest-layer wrapper names the offending value.
7255        let err = caixa_with_restart_window(Some("30x"))
7256            .validate_restart_window()
7257            .unwrap_err();
7258        let ManifestError::RestartWindowMalformed {
7259            restart_window,
7260            reason,
7261        } = err
7262        else {
7263            panic!("expected RestartWindowMalformed for unknown unit");
7264        };
7265        assert_eq!(restart_window, "30x");
7266        assert!(
7267            reason.contains("unknown duration unit"),
7268            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
7269        );
7270    }
7271
7272    #[test]
7273    fn validate_restart_window_rejects_garbage() {
7274        // Pure non-numeric magnitude (`"abc"`) falls through to the
7275        // shared codec's narrower `"bad duration magnitude"` arm. Same
7276        // diagnostic shape as the codec-side
7277        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
7278        let err = caixa_with_restart_window(Some("abc"))
7279            .validate_restart_window()
7280            .unwrap_err();
7281        let ManifestError::RestartWindowMalformed {
7282            restart_window,
7283            reason,
7284        } = err
7285        else {
7286            panic!("expected RestartWindowMalformed for garbage");
7287        };
7288        assert_eq!(restart_window, "abc");
7289        assert!(
7290            reason.contains("bad duration magnitude"),
7291            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
7292        );
7293    }
7294
7295    #[test]
7296    fn validate_restart_window_rejects_empty_string() {
7297        // The empty-after-trim edge case — distinct from the `None`
7298        // canonical "omit the slot" shape. The shared codec's
7299        // digit-only gate refuses an empty magnitude; the manifest
7300        // layer names the offending `""` so the author can grep for
7301        // the literal empty value in their `caixa.lisp` and either
7302        // remove the slot (the canonical "no reset" shape) or pin a
7303        // positive duration.
7304        let err = caixa_with_restart_window(Some(""))
7305            .validate_restart_window()
7306            .unwrap_err();
7307        assert!(
7308            matches!(
7309                err,
7310                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7311                    if restart_window.is_empty()
7312            ),
7313            "got {err:?}"
7314        );
7315    }
7316
7317    #[test]
7318    fn validate_restart_window_diagnostic_carries_offending_value() {
7319        // Diagnostic-shape pin (peer with
7320        // `nome_invalid_diagnostic_carries_offending_nome` /
7321        // `versao_invalid_diagnostic_carries_offending_versao`): the
7322        // error names the offending raw `:restart-window` verbatim
7323        // with a non-empty shared-codec-shaped reason, so a `feira
7324        // lint` run can render the diagnostic without re-parsing.
7325        let err = caixa_with_restart_window(Some("1.5s"))
7326            .validate_restart_window()
7327            .unwrap_err();
7328        let ManifestError::RestartWindowMalformed {
7329            restart_window,
7330            reason,
7331        } = err
7332        else {
7333            panic!("expected RestartWindowMalformed variant");
7334        };
7335        assert_eq!(restart_window, "1.5s");
7336        assert!(
7337            !reason.is_empty(),
7338            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
7339        );
7340    }
7341
7342    #[test]
7343    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
7344        // Behavioral parity pin after the fold (`parse_window_inline`
7345        // deletion): the canonical `"60s"` still produces
7346        // `Duration::from_secs(60)` on the typed view — the fold is
7347        // semantically equivalent to the prior inline parser on the
7348        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
7349        // pin, narrowed to the parser-side contract.
7350        let c = caixa_with_restart_window(Some("60s"));
7351        let view = c.supervisor_view().expect("Supervisor kind has a view");
7352        assert_eq!(
7353            view.restart_window,
7354            Some(std::time::Duration::from_secs(60))
7355        );
7356    }
7357
7358    #[test]
7359    fn supervisor_view_soft_swallows_what_validate_rejects() {
7360        // Parity pin between the view-construction path and the
7361        // manifest-level validator: the same `"1.5s"` that surfaces
7362        // `RestartWindowMalformed` at `validate_restart_window` time
7363        // becomes `restart_window: None` on the typed view (the fold
7364        // preserves the existing best-effort shape of `supervisor_view`).
7365        // The contract is: a layout-verifier / `feira lint` flow that
7366        // cares about the malformed-window axis MUST consult
7367        // `validate_restart_window` — relying solely on the view's
7368        // `None` swallows the diagnostic silently. This pin makes the
7369        // expectation a typed invariant.
7370        let c = caixa_with_restart_window(Some("1.5s"));
7371        let view = c.supervisor_view().expect("Supervisor kind has a view");
7372        assert_eq!(
7373            view.restart_window, None,
7374            "view-construction path soft-swallows the parse error to None"
7375        );
7376        // And the manifest-level validator does NOT soft-swallow:
7377        assert!(
7378            matches!(
7379                c.validate_restart_window().unwrap_err(),
7380                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7381                    if restart_window == "1.5s"
7382            ),
7383            "validator must surface the offending value",
7384        );
7385    }
7386
7387    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
7388
7389    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
7390        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7391        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
7392        c.exe = exe.into_iter().map(String::from).collect();
7393        c.servicos = servicos.into_iter().map(String::from).collect();
7394        c
7395    }
7396
7397    #[test]
7398    fn validate_code_paths_accepts_canonical_template() {
7399        // The bare `Caixa::template` shape is the gate's identity element
7400        // on the canonical authoring shape — `:bibliotecas
7401        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
7402        // that the gate is non-disruptive against every existing caixa.
7403        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7404        c.validate_code_paths().unwrap();
7405    }
7406
7407    #[test]
7408    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
7409        // Positive control sweep: a canonical-shaped path on every slot
7410        // passes. Mirrors the peer
7411        // `behavior::validate_every_slot_relative_is_ok` pin.
7412        let c = caixa_with_code_paths(
7413            vec!["lib/demo.lisp", "lib/helpers.lisp"],
7414            vec!["exe/demo", "exe/tool"],
7415            vec!["servicos/demo.computeunit.yaml"],
7416        );
7417        c.validate_code_paths().unwrap();
7418    }
7419
7420    #[test]
7421    fn validate_code_paths_accepts_all_empty_lists() {
7422        // The empty-list identity element: every Caixa with no declared
7423        // code paths trivially passes (Supervisor / Aplicacao kinds rely
7424        // on this — the OwnCode gate already rejected them before the
7425        // path-shape gate runs in the layout, but the validator itself
7426        // must accept the empty shape).
7427        let c = caixa_with_code_paths(vec![], vec![], vec![]);
7428        c.validate_code_paths().unwrap();
7429    }
7430
7431    #[test]
7432    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
7433        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
7434        let err = c.validate_code_paths().unwrap_err();
7435        assert!(
7436            matches!(
7437                err,
7438                ManifestError::CodePathEmpty {
7439                    slot: ":bibliotecas"
7440                }
7441            ),
7442            "got {err:?}",
7443        );
7444    }
7445
7446    #[test]
7447    fn validate_code_paths_rejects_empty_exe_entry() {
7448        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
7449        let err = c.validate_code_paths().unwrap_err();
7450        assert!(
7451            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
7452            "got {err:?}",
7453        );
7454    }
7455
7456    #[test]
7457    fn validate_code_paths_rejects_empty_servicos_entry() {
7458        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
7459        let err = c.validate_code_paths().unwrap_err();
7460        assert!(
7461            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
7462            "got {err:?}",
7463        );
7464    }
7465
7466    #[test]
7467    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
7468        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
7469        // so an absolute path that resolves on disk silently passes the
7470        // layout's existence check — the canonical sandbox-escape on
7471        // the biblioteca axis.
7472        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7473        let err = c.validate_code_paths().unwrap_err();
7474        let ManifestError::CodePathAbsolute { slot, path } = err else {
7475            panic!("expected CodePathAbsolute, got {err:?}");
7476        };
7477        assert_eq!(slot, ":bibliotecas");
7478        assert_eq!(path, PathBuf::from("/etc/passwd"));
7479    }
7480
7481    #[test]
7482    fn validate_code_paths_rejects_absolute_exe_entry() {
7483        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
7484        let err = c.validate_code_paths().unwrap_err();
7485        let ManifestError::CodePathAbsolute { slot, path } = err else {
7486            panic!("expected CodePathAbsolute, got {err:?}");
7487        };
7488        assert_eq!(slot, ":exe");
7489        assert_eq!(path, PathBuf::from("/usr/bin/env"));
7490    }
7491
7492    #[test]
7493    fn validate_code_paths_rejects_absolute_servicos_entry() {
7494        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
7495        let err = c.validate_code_paths().unwrap_err();
7496        let ManifestError::CodePathAbsolute { slot, path } = err else {
7497            panic!("expected CodePathAbsolute, got {err:?}");
7498        };
7499        assert_eq!(slot, ":servicos");
7500        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
7501    }
7502
7503    #[test]
7504    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
7505        // Canonical "I want a lib from a sibling caixa" footgun on the
7506        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
7507        // downstream, so a leading `..` traverses to the parent of the
7508        // caixa root with no diagnostic at layout time if the resolved
7509        // target exists.
7510        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
7511        let err = c.validate_code_paths().unwrap_err();
7512        let ManifestError::CodePathParentEscape { slot, path } = err else {
7513            panic!("expected CodePathParentEscape, got {err:?}");
7514        };
7515        assert_eq!(slot, ":bibliotecas");
7516        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
7517    }
7518
7519    #[test]
7520    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
7521        // Mid-path `..` defeats the layout's component-aware
7522        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
7523        // `starts_with(<root>/exe)` is true, but the canonical resolution
7524        // lives outside the caixa root. Caught regardless of where the
7525        // `..` sits — mirrors the peer
7526        // `behavior::validate_rejects_parent_escape_mid_path` pin.
7527        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
7528        let err = c.validate_code_paths().unwrap_err();
7529        let ManifestError::CodePathParentEscape { slot, path } = err else {
7530            panic!("expected CodePathParentEscape, got {err:?}");
7531        };
7532        assert_eq!(slot, ":exe");
7533        assert_eq!(path, PathBuf::from("exe/../../escape"));
7534    }
7535
7536    #[test]
7537    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
7538        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
7539        let err = c.validate_code_paths().unwrap_err();
7540        let ManifestError::CodePathParentEscape { slot, path } = err else {
7541            panic!("expected CodePathParentEscape, got {err:?}");
7542        };
7543        assert_eq!(slot, ":servicos");
7544        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
7545    }
7546
7547    #[test]
7548    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
7549        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
7550        // `:servicos`. A manifest with malformed entries on all three
7551        // surfaces surfaces the `:bibliotecas` defect first, mirroring
7552        // the canonical declaration order
7553        // `Caixa::declared_foreign_code_slots` already establishes for
7554        // the foreign-code-slot diagnostic.
7555        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
7556        let err = c.validate_code_paths().unwrap_err();
7557        assert!(
7558            matches!(
7559                err,
7560                ManifestError::CodePathEmpty {
7561                    slot: ":bibliotecas"
7562                }
7563            ),
7564            "got {err:?}",
7565        );
7566    }
7567
7568    #[test]
7569    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
7570        // Within-slot precedence pin: empty → absolute → parent-escape,
7571        // matching the [`PathShapeViolation`] arm-ordering every peer
7572        // `is_sandboxed_relative_path` caller follows (b0c8389
7573        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
7574        // `:bibliotecas` list whose first entry is empty *and* whose
7575        // later entries are absolute/parent-escape surfaces the empty
7576        // arm first, on the lexicographically-earliest offending entry.
7577        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
7578        let err = c.validate_code_paths().unwrap_err();
7579        assert!(
7580            matches!(
7581                err,
7582                ManifestError::CodePathEmpty {
7583                    slot: ":bibliotecas"
7584                }
7585            ),
7586            "got {err:?}",
7587        );
7588    }
7589
7590    #[test]
7591    fn validate_code_paths_first_offender_per_slot_wins() {
7592        // Within a single slot, the first declaration-order offender
7593        // surfaces — pins that the gate is left-to-right deterministic
7594        // (peer of every `*_first_collision_*` pin on duplicate gates).
7595        let c = caixa_with_code_paths(
7596            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
7597            vec![],
7598            vec![],
7599        );
7600        let err = c.validate_code_paths().unwrap_err();
7601        let ManifestError::CodePathAbsolute { slot, path } = err else {
7602            panic!("expected CodePathAbsolute, got {err:?}");
7603        };
7604        assert_eq!(slot, ":bibliotecas");
7605        assert_eq!(path, PathBuf::from("/etc/escape"));
7606    }
7607
7608    #[test]
7609    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
7610        // Diagnostic-shape pin (peer with
7611        // `nome_invalid_diagnostic_carries_offending_nome` /
7612        // `versao_invalid_diagnostic_carries_offending_versao`): the
7613        // error's Display surfaces both the offending `:slot` tag and
7614        // the offending path verbatim, so a `feira lint` run can render
7615        // the diagnostic without re-parsing.
7616        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7617        let rendered = c.validate_code_paths().unwrap_err().to_string();
7618        assert!(
7619            rendered.contains(":bibliotecas"),
7620            "diagnostic must name the offending slot: {rendered}",
7621        );
7622        assert!(
7623            rendered.contains("/etc/passwd"),
7624            "diagnostic must quote the offending path: {rendered}",
7625        );
7626    }
7627
7628    #[test]
7629    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
7630        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
7631        // axis. Without the gate `feira build` re-parses the same lib
7632        // twice, wasting work and silently masking the author's intent
7633        // to declare a *second* biblioteca.
7634        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
7635        let err = c.validate_code_paths().unwrap_err();
7636        let ManifestError::CodePathDuplicate { slot, path } = err else {
7637            panic!("expected CodePathDuplicate, got {err:?}");
7638        };
7639        assert_eq!(slot, ":bibliotecas");
7640        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
7641    }
7642
7643    #[test]
7644    fn validate_code_paths_rejects_duplicate_exe_entry() {
7645        // Same footgun on the Binario surface. The future `caixa-flake`
7646        // emitter that materializes each `:exe` entry as a flake
7647        // `packages.<name>` derivation would collide on the duplicate
7648        // package key — surfaced here at the typed-validate layer with a
7649        // self-locating diagnostic instead.
7650        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
7651        let err = c.validate_code_paths().unwrap_err();
7652        let ManifestError::CodePathDuplicate { slot, path } = err else {
7653            panic!("expected CodePathDuplicate, got {err:?}");
7654        };
7655        assert_eq!(slot, ":exe");
7656        assert_eq!(path, PathBuf::from("exe/cli"));
7657    }
7658
7659    #[test]
7660    fn validate_code_paths_rejects_duplicate_servicos_entry() {
7661        // Same footgun on the Servico surface. The peer caixa-helm /
7662        // caixa-flux renderers refuse `:servicos.len() != 1` with the
7663        // narrower `UnsupportedServicoCount` diagnostic, but that
7664        // diagnostic surfaces "too many servicos" without naming
7665        // "duplicate entry" — the typed self-locating framing only lands
7666        // at this gate.
7667        let c = caixa_with_code_paths(
7668            vec![],
7669            vec![],
7670            vec![
7671                "servicos/demo.computeunit.yaml",
7672                "servicos/demo.computeunit.yaml",
7673            ],
7674        );
7675        let err = c.validate_code_paths().unwrap_err();
7676        let ManifestError::CodePathDuplicate { slot, path } = err else {
7677            panic!("expected CodePathDuplicate, got {err:?}");
7678        };
7679        assert_eq!(slot, ":servicos");
7680        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
7681    }
7682
7683    #[test]
7684    fn validate_code_paths_accepts_same_path_across_slots() {
7685        // Per-list scope pin: a `:bibliotecas` entry that happens to
7686        // collide with an `:exe` or `:servicos` entry as a *string* is
7687        // not a duplicate by this gate (each list gets its own HashSet),
7688        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
7689        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
7690        // shape on the dep axis). The structural `starts_with(<exe |
7691        // servicos>_dir)` fence at layout time prevents the realistic
7692        // cross-slot collision case from existing on disk, but the gate's
7693        // per-list scope is correct independent of that downstream fence.
7694        let c = caixa_with_code_paths(
7695            vec!["lib/x.lisp"],
7696            vec!["exe/x"],
7697            vec!["servicos/x.computeunit.yaml"],
7698        );
7699        c.validate_code_paths().unwrap();
7700    }
7701
7702    #[test]
7703    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
7704        // Within-slot ordering pin: structural defects (empty / absolute
7705        // / parent-escape) fire before the duplicate gate on the same
7706        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
7707        // surfaces the narrower `CodePathEmpty` for the empty entry
7708        // first, not the duplicate on the later pair — same arm-ordering
7709        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
7710        // `:autores` 86c769b, `:deps` 359fba5).
7711        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
7712        let err = c.validate_code_paths().unwrap_err();
7713        assert!(
7714            matches!(
7715                err,
7716                ManifestError::CodePathEmpty {
7717                    slot: ":bibliotecas"
7718                }
7719            ),
7720            "got {err:?}",
7721        );
7722    }
7723
7724    #[test]
7725    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
7726        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
7727        // duplicates surface before `:exe` duplicates, matching the
7728        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
7729        // order every peer per-slot diagnostic on this surface follows.
7730        let c = caixa_with_code_paths(
7731            vec!["lib/x.lisp", "lib/x.lisp"],
7732            vec!["exe/y", "exe/y"],
7733            vec![],
7734        );
7735        let err = c.validate_code_paths().unwrap_err();
7736        let ManifestError::CodePathDuplicate { slot, path } = err else {
7737            panic!("expected CodePathDuplicate, got {err:?}");
7738        };
7739        assert_eq!(slot, ":bibliotecas");
7740        assert_eq!(path, PathBuf::from("lib/x.lisp"));
7741    }
7742
7743    #[test]
7744    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
7745        // Diagnostic-shape pin (peer with
7746        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
7747        // on the structural arm): the duplicate-arm Display surfaces both
7748        // the offending `:slot` tag and the offending path verbatim, so a
7749        // `feira lint` run can render the diagnostic without re-parsing.
7750        let c = caixa_with_code_paths(
7751            vec![],
7752            vec![],
7753            vec![
7754                "servicos/demo.computeunit.yaml",
7755                "servicos/demo.computeunit.yaml",
7756            ],
7757        );
7758        let rendered = c.validate_code_paths().unwrap_err().to_string();
7759        assert!(
7760            rendered.contains(":servicos"),
7761            "diagnostic must name the offending slot: {rendered}",
7762        );
7763        assert!(
7764            rendered.contains("servicos/demo.computeunit.yaml"),
7765            "diagnostic must quote the offending path: {rendered}",
7766        );
7767    }
7768
7769    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
7770    //
7771    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
7772    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
7773    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
7774    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
7775    // at parse time — the same downstream consumer the peer `:behavior
7776    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
7777    // `:upgrade-from :state-change :script` (33cc830,
7778    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
7779    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
7780    // nix-built executable surface (`"exe/<name>"` shape per the canonical
7781    // [`crate::LayoutError::ExeOutsideDir`] error message and every
7782    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
7783    // is the `.computeunit.yaml` ComputeUnit-CR axis.
7784
7785    #[test]
7786    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
7787        // Canonical "I dragged the wrong file from the workspace tree"
7788        // footgun on the biblioteca axis. Without the gate `feira build`
7789        // hands the extensionless path to `tatara_lisp::read` and fails
7790        // with a parser-shaped diagnostic far from the source caixa.lisp,
7791        // with no field naming the offending `:bibliotecas` entry.
7792        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
7793            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
7794            let err = c.validate_code_paths().unwrap_err();
7795            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
7796                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
7797            };
7798            assert_eq!(slot, ":bibliotecas");
7799            assert_eq!(path, PathBuf::from(relpath));
7800        }
7801    }
7802
7803    #[test]
7804    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
7805        // Wrong-extension sweep across common authoring footguns. Same
7806        // sweep posture as the peer
7807        // `behavior::validate_rejects_wrong_extension` (c97815a) and
7808        // `upgrade::tests::state_change_rejects_wrong_extension_script`
7809        // (33cc830) cases.
7810        for relpath in [
7811            "lib/demo.rs",
7812            "lib/demo.txt",
7813            "lib/demo.md",
7814            "lib/demo.json",
7815            "lib/demo.yaml",
7816            "lib/demo.toml",
7817            "lib/demo.lisp.bak",
7818            "lib/demo.lispx",
7819            "lib/demo.lis",
7820        ] {
7821            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
7822            let err = c.validate_code_paths().unwrap_err();
7823            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
7824                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
7825            };
7826            assert_eq!(slot, ":bibliotecas");
7827            assert_eq!(path, PathBuf::from(relpath));
7828        }
7829    }
7830
7831    #[test]
7832    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
7833        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
7834        // contract. An uppercase `.LISP` shape that the layout's existence
7835        // check would (case-insensitively, on case-insensitive volumes)
7836        // match the on-disk file still mismatches the canonical form the
7837        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
7838        // contract. Mirrors the peer
7839        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
7840        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
7841        // (33cc830) sweeps.
7842        for relpath in [
7843            "lib/demo.LISP",
7844            "lib/demo.Lisp",
7845            "lib/demo.LiSp",
7846            "lib/demo.lISP",
7847        ] {
7848            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
7849            let err = c.validate_code_paths().unwrap_err();
7850            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
7851                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
7852            };
7853            assert_eq!(slot, ":bibliotecas");
7854            assert_eq!(path, PathBuf::from(relpath));
7855        }
7856    }
7857
7858    #[test]
7859    fn validate_code_paths_accepts_canonical_lisp_shapes() {
7860        // Positive-control sweep through every canonical authoring shape
7861        // every in-tree fixture and the `Caixa::template` scaffold use.
7862        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
7863        // (c97815a) and the lifted predicate's own
7864        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
7865        // (33cc830).
7866        for relpath in [
7867            "lib/demo.lisp",
7868            "lib/handlers.lisp",
7869            "lib/migrations/v01-to-v02.lisp",
7870            "demo.lisp",
7871            "a.lisp",
7872            "./lib/demo.lisp",
7873            "lib/./handlers.lisp",
7874            "lib/migrations/v.0.1.lisp",
7875        ] {
7876            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
7877            c.validate_code_paths()
7878                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
7879        }
7880    }
7881
7882    #[test]
7883    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
7884        // The file-type gate is per-slot — only `:bibliotecas` carries the
7885        // tatara-lisp-source contract. An extensionless `:exe` entry
7886        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
7887        // canonical shapes every in-tree fixture uses, and must continue
7888        // to pass validate. Pins that a future tightening that broadens
7889        // the `.lisp` gate to either axis surfaces as a test failure
7890        // rather than as a silent breaking change to existing valid
7891        // manifests.
7892        let c = caixa_with_code_paths(
7893            vec![],
7894            vec!["exe/demo", "exe/tool"],
7895            vec!["servicos/demo.computeunit.yaml"],
7896        );
7897        c.validate_code_paths().unwrap();
7898    }
7899
7900    #[test]
7901    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
7902        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
7903        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
7904        // sandbox-shape diagnostic first (the `.lisp` remediation would
7905        // be misleading when the offending path can never resolve under
7906        // the caixa root anyway). Mirrors the peer
7907        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
7908        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
7909        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
7910        // on `:upgrade-from :state-change :script` (33cc830).
7911        //
7912        // Empty wins (the strictly-smaller-scope structural arm).
7913        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
7914        assert!(
7915            matches!(
7916                c.validate_code_paths().unwrap_err(),
7917                ManifestError::CodePathEmpty {
7918                    slot: ":bibliotecas"
7919                }
7920            ),
7921            "empty must win over non-lisp-extension",
7922        );
7923        // Absolute wins (the path can't resolve under the caixa root).
7924        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7925        let err = c.validate_code_paths().unwrap_err();
7926        let ManifestError::CodePathAbsolute { slot, .. } = err else {
7927            panic!("absolute must win over non-lisp-extension, got {err:?}");
7928        };
7929        assert_eq!(slot, ":bibliotecas");
7930        // ParentEscape wins (the path escapes the caixa root).
7931        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
7932        let err = c.validate_code_paths().unwrap_err();
7933        let ManifestError::CodePathParentEscape { slot, .. } = err else {
7934            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
7935        };
7936        assert_eq!(slot, ":bibliotecas");
7937    }
7938
7939    #[test]
7940    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
7941        // Within-slot precedence pin: the per-entry file-type shape gate
7942        // fires before the cross-entry duplicate gate, so the narrower
7943        // structural defect dominates the uniqueness diagnostic. A
7944        // `("lib/x.txt" "lib/x.txt")` shape surfaces
7945        // `CodePathNonLispExtension` on the first entry rather than
7946        // `CodePathDuplicate` on the pair — same posture every per-entry
7947        // shape-gate-precedes-duplicate cascade follows on this surface
7948        // (the empty / absolute / parent-escape arms already precede the
7949        // duplicate arm; the lifted file-type arm joins that set).
7950        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
7951        let err = c.validate_code_paths().unwrap_err();
7952        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
7953            panic!("expected CodePathNonLispExtension, got {err:?}");
7954        };
7955        assert_eq!(slot, ":bibliotecas");
7956        assert_eq!(path, PathBuf::from("lib/x.txt"));
7957    }
7958
7959    #[test]
7960    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
7961        // Diagnostic-shape pin (peer with
7962        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
7963        // on the sandbox-shape arms and
7964        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
7965        // on the duplicate arm): the file-type-arm Display surfaces both
7966        // the offending `:slot` tag, the offending path verbatim, and the
7967        // expected `.lisp` extension named in the remediation text, so a
7968        // `feira lint` run can render the diagnostic without re-parsing.
7969        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
7970        let rendered = c.validate_code_paths().unwrap_err().to_string();
7971        assert!(
7972            rendered.contains(":bibliotecas"),
7973            "diagnostic must name the offending slot: {rendered}",
7974        );
7975        assert!(
7976            rendered.contains("lib/demo.rs"),
7977            "diagnostic must quote the offending path: {rendered}",
7978        );
7979        assert!(
7980            rendered.contains(".lisp"),
7981            "diagnostic must name the expected extension: {rendered}",
7982        );
7983    }
7984
7985    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
7986    //
7987    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
7988    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
7989    // contract. The peer caixa-helm / caixa-flux renderers consume each
7990    // `:servicos` entry through `serde_yaml::from_str` as a typed
7991    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
7992    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
7993    // axis `Path::extension` can't express on its own.
7994
7995    #[test]
7996    fn validate_code_paths_rejects_no_extension_servicos_entry() {
7997        // Canonical "I dragged the wrong file from the workspace tree"
7998        // footgun on the Servico axis. Without the gate the peer
7999        // caixa-helm / caixa-flux renderers hand the extensionless path
8000        // to `serde_yaml::from_str` and fail with a parser-shaped
8001        // diagnostic far from the source caixa.lisp, with no field
8002        // naming the offending `:servicos` entry.
8003        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
8004            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8005            let err = c.validate_code_paths().unwrap_err();
8006            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8007                panic!(
8008                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8009                     got {err:?}"
8010                );
8011            };
8012            assert_eq!(slot, ":servicos");
8013            assert_eq!(path, PathBuf::from(relpath));
8014        }
8015    }
8016
8017    #[test]
8018    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
8019        // Wrong-extension sweep across common authoring footguns on the
8020        // Servico axis. Bare `.yaml` is the canonical "I forgot the
8021        // `.computeunit` segment" typo; the off-by-one-segment shapes
8022        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
8023        // bare `Path::extension` view but mismatch the typed compound
8024        // suffix the renderers' `serde_yaml::from_str` consumer demands.
8025        // Same sweep-posture as the peer
8026        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
8027        // (64772a9) on the sibling tatara-lisp-source axis.
8028        for relpath in [
8029            "servicos/demo.yaml",
8030            "servicos/demo.yml",
8031            "servicos/demo.json",
8032            "servicos/demo.toml",
8033            "servicos/demo.txt",
8034            "servicos/demo.computeunit.yaml.bak",
8035            "servicos/demo.computeunit.yam",
8036            "servicos/demo.computeunit",
8037            "servicos/demo-computeunit.yaml",
8038            "servicos/demo_computeunit.yaml",
8039        ] {
8040            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8041            let err = c.validate_code_paths().unwrap_err();
8042            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8043                panic!(
8044                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8045                     got {err:?}"
8046                );
8047            };
8048            assert_eq!(slot, ":servicos");
8049            assert_eq!(path, PathBuf::from(relpath));
8050        }
8051    }
8052
8053    #[test]
8054    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
8055        // Case-sensitivity sweep — pins the strict lowercase
8056        // `.computeunit.yaml` contract. A case-folded shape that the
8057        // layout's existence check would (case-insensitively, on
8058        // case-insensitive volumes) match the on-disk file still
8059        // mismatches the canonical form the codec emits, breaking the
8060        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
8061        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
8062        // (64772a9) sweep on the sibling tatara-lisp-source axis.
8063        for relpath in [
8064            "servicos/demo.ComputeUnit.yaml",
8065            "servicos/demo.COMPUTEUNIT.yaml",
8066            "servicos/demo.computeunit.YAML",
8067            "servicos/demo.computeunit.Yaml",
8068            "servicos/demo.COMPUTEUNIT.YAML",
8069        ] {
8070            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8071            let err = c.validate_code_paths().unwrap_err();
8072            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8073                panic!(
8074                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8075                     got {err:?}"
8076                );
8077            };
8078            assert_eq!(slot, ":servicos");
8079            assert_eq!(path, PathBuf::from(relpath));
8080        }
8081    }
8082
8083    #[test]
8084    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
8085        // Degenerate hidden-file shape: a file name exactly equal to the
8086        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
8087        // the structural "Servico declared with no identity" footgun.
8088        // The substrate identifies each ComputeUnit by the file-stem
8089        // segment that precedes `.computeunit.yaml` (the rendered
8090        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
8091        // the M3 `:contratos` membership lookup), so an empty stem
8092        // leaves the Servico unidentifiable. Pinned at the typed-axis
8093        // level so a future regression that drops the `name.len() >
8094        // SUFFIX.len()` bound at the predicate surfaces here, not
8095        // piecemeal as a `lareira-` chart-name collision at render time.
8096        for relpath in ["servicos/.computeunit.yaml"] {
8097            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8098            let err = c.validate_code_paths().unwrap_err();
8099            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8100                panic!(
8101                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8102                     got {err:?}"
8103                );
8104            };
8105            assert_eq!(slot, ":servicos");
8106            assert_eq!(path, PathBuf::from(relpath));
8107        }
8108    }
8109
8110    #[test]
8111    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
8112        // Positive-control sweep through every canonical authoring shape
8113        // every in-tree fixture and the `Caixa::template` scaffold use.
8114        // Mirrors the peer
8115        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
8116        // and the lifted predicate's own
8117        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
8118        // render.rs.
8119        for relpath in [
8120            "servicos/demo.computeunit.yaml",
8121            "servicos/hello-rio.computeunit.yaml",
8122            "servicos/my-service.computeunit.yaml",
8123            "servicos/a.computeunit.yaml",
8124            "./servicos/demo.computeunit.yaml",
8125            "servicos/./demo.computeunit.yaml",
8126            "servicos/sub/nested.computeunit.yaml",
8127            "servicos/v0.1.computeunit.yaml",
8128        ] {
8129            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8130            c.validate_code_paths()
8131                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8132        }
8133    }
8134
8135    #[test]
8136    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
8137        // The file-type gate is per-slot — only `:servicos` carries the
8138        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
8139        // entry and an extensionless `:exe` entry are the canonical
8140        // shapes every in-tree fixture uses, and must continue to pass
8141        // validate. Peer of
8142        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
8143        // (64772a9) — together pin that the typed
8144        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
8145        // cross-axis leakage in either direction.
8146        let c = caixa_with_code_paths(
8147            vec!["lib/demo.lisp"],
8148            vec!["exe/demo", "exe/tool"],
8149            vec!["servicos/demo.computeunit.yaml"],
8150        );
8151        c.validate_code_paths().unwrap();
8152    }
8153
8154    #[test]
8155    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
8156        // Cross-arm precedence pin: a `:servicos` entry that is *both*
8157        // sandbox-escaping and wrong-extension surfaces the more
8158        // fundamental sandbox-shape diagnostic first (the
8159        // `.computeunit.yaml` remediation would be misleading when the
8160        // offending path can never resolve under the caixa root
8161        // anyway). Mirrors the peer
8162        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
8163        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
8164        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
8165        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
8166        // table establishes.
8167        //
8168        // Empty wins (the strictly-smaller-scope structural arm).
8169        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
8170        assert!(
8171            matches!(
8172                c.validate_code_paths().unwrap_err(),
8173                ManifestError::CodePathEmpty { slot: ":servicos" }
8174            ),
8175            "empty must win over non-computeunit-yaml-extension",
8176        );
8177        // Absolute wins (the path can't resolve under the caixa root).
8178        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
8179        let err = c.validate_code_paths().unwrap_err();
8180        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8181            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
8182        };
8183        assert_eq!(slot, ":servicos");
8184        // ParentEscape wins (the path escapes the caixa root).
8185        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
8186        let err = c.validate_code_paths().unwrap_err();
8187        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8188            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
8189        };
8190        assert_eq!(slot, ":servicos");
8191    }
8192
8193    #[test]
8194    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
8195        // Within-slot precedence pin: the per-entry file-type shape gate
8196        // fires before the cross-entry duplicate gate, so the narrower
8197        // structural defect dominates the uniqueness diagnostic. A
8198        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
8199        // `CodePathNonComputeUnitYamlExtension` on the first entry
8200        // rather than `CodePathDuplicate` on the pair — same posture
8201        // every per-entry shape-gate-precedes-duplicate cascade follows
8202        // on this surface, peer of the 64772a9 `:bibliotecas`
8203        // `("lib/x.txt" "lib/x.txt")` ordering.
8204        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
8205        let err = c.validate_code_paths().unwrap_err();
8206        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8207            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
8208        };
8209        assert_eq!(slot, ":servicos");
8210        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
8211    }
8212
8213    #[test]
8214    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
8215     {
8216        // Diagnostic-shape pin (peer with
8217        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
8218        // on the sibling tatara-lisp-source axis): the file-type-arm
8219        // Display surfaces both the offending `:slot` tag, the
8220        // offending path verbatim, and the expected
8221        // `.computeunit.yaml` compound suffix named in the remediation
8222        // text, so a `feira lint` run can render the diagnostic without
8223        // re-parsing.
8224        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
8225        let rendered = c.validate_code_paths().unwrap_err().to_string();
8226        assert!(
8227            rendered.contains(":servicos"),
8228            "diagnostic must name the offending slot: {rendered}",
8229        );
8230        assert!(
8231            rendered.contains("servicos/demo.yaml"),
8232            "diagnostic must quote the offending path: {rendered}",
8233        );
8234        assert!(
8235            rendered.contains(".computeunit.yaml"),
8236            "diagnostic must name the expected compound suffix: {rendered}",
8237        );
8238    }
8239
8240    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
8241
8242    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
8243        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8244        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
8245        c
8246    }
8247
8248    #[test]
8249    fn validate_etiquetas_accepts_empty_list() {
8250        // The empty-list identity: every caixa with no declared tags
8251        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
8252        // so the gate is non-disruptive against every existing manifest.
8253        let c = caixa_with_etiquetas(vec![]);
8254        c.validate_etiquetas().unwrap();
8255    }
8256
8257    #[test]
8258    fn validate_etiquetas_accepts_canonical_forms() {
8259        // Positive control sweep: a canonical-shaped non-empty distinct
8260        // tag list passes, mirroring the example checkout-aplicacao
8261        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
8262        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
8263        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
8264        c.validate_etiquetas().unwrap();
8265    }
8266
8267    #[test]
8268    fn validate_etiquetas_rejects_empty_entry() {
8269        // Canonical paste-from-blank-doc footgun. Without the gate the
8270        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
8271        // no-op tag indexing nothing in the future caixa-registry.
8272        let c = caixa_with_etiquetas(vec![""]);
8273        let err = c.validate_etiquetas().unwrap_err();
8274        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8275    }
8276
8277    #[test]
8278    fn validate_etiquetas_rejects_duplicate_entry() {
8279        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
8280        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
8281        // collect at chart render — a "second wins / one silently
8282        // disappears" shape divergent from every peer typed-graph set
8283        // gate. The duplicate-arm names the offending tag verbatim.
8284        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8285        let err = c.validate_etiquetas().unwrap_err();
8286        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8287            panic!("expected EtiquetaDuplicate, got {err:?}");
8288        };
8289        assert_eq!(etiqueta, "demo");
8290    }
8291
8292    #[test]
8293    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
8294        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
8295        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
8296        // structural "this entry has no value" defect dominates the
8297        // cross-entry uniqueness diagnostic. Mirrors the peer
8298        // empty-before-duplicate cascades on `:caracteristicas`
8299        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8300        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
8301        // `MembroDuplicate`).
8302        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
8303        let err = c.validate_etiquetas().unwrap_err();
8304        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8305    }
8306
8307    #[test]
8308    fn validate_etiquetas_duplicate_reports_first_collision() {
8309        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8310        // duplicate (the lexicographically-earliest offending position
8311        // — the second `"a"` at index 2 collides with the first `"a"`
8312        // at index 0), not the later `"b"` collision at index 3,
8313        // peer with every other first-collision diagnostic posture on
8314        // this surface (`validate_load_singularity_reports_first_collision`,
8315        // `validate_cleanup_singularity_reports_first_collision`).
8316        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
8317        let err = c.validate_etiquetas().unwrap_err();
8318        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8319            panic!("expected EtiquetaDuplicate, got {err:?}");
8320        };
8321        assert_eq!(etiqueta, "a");
8322    }
8323
8324    #[test]
8325    fn validate_etiquetas_case_sensitive() {
8326        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
8327        // mirroring the peer `:membros :caixa` / `:children :caixa`
8328        // exact-string-match discipline. The shape gate this routine
8329        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
8330        // grammar) accepts mixed case — crates.io's keyword rule is
8331        // "case-insensitive" at the index layer but admits mixed case
8332        // at the entry layer (the canonical Helm chart `keywords:`
8333        // shape is lowercase by convention, but the grammar admits
8334        // uppercase). Case-sensitivity at the duplicate-set layer
8335        // remains structural — two distinct strings are two distinct
8336        // entries.
8337        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
8338        c.validate_etiquetas().unwrap();
8339    }
8340
8341    #[test]
8342    fn validate_etiquetas_diagnostic_carries_offending_tag() {
8343        // Diagnostic-shape pin (peer with
8344        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
8345        // the error's Display surfaces the offending tag verbatim, so a
8346        // `feira lint` run can render the diagnostic without re-parsing
8347        // and the author can grep their caixa.lisp for the offending
8348        // value.
8349        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8350        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8351        assert!(
8352            rendered.contains(":etiquetas"),
8353            "diagnostic must name the offending slot: {rendered}",
8354        );
8355        assert!(
8356            rendered.contains("demo"),
8357            "diagnostic must quote the offending tag: {rendered}",
8358        );
8359    }
8360
8361    #[test]
8362    fn validate_etiquetas_rejects_leading_whitespace_entry() {
8363        // Canonical paste-from-aligned-doc footgun. Without the shape
8364        // gate `" mesh"` silently passed validate and landed as a
8365        // YAML plain-style scalar with leading whitespace in the
8366        // rendered Chart.yaml `keywords:` array — every YAML 1.2
8367        // dumper trims leading whitespace from plain-style scalars,
8368        // so the authored space round-tripped inconsistently back
8369        // through `caixa.lisp`. Mirrors the peer
8370        // `validate_autores_rejects_leading_whitespace_entry`.
8371        let c = caixa_with_etiquetas(vec![" mesh"]);
8372        let err = c.validate_etiquetas().unwrap_err();
8373        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8374            panic!("expected EtiquetaInvalid, got {err:?}");
8375        };
8376        assert_eq!(etiqueta, " mesh");
8377        assert!(reason.contains("whitespace"), "got: {reason}");
8378    }
8379
8380    #[test]
8381    fn validate_etiquetas_rejects_embedded_newline_entry() {
8382        // Canonical paste-from-multiline-doc footgun — the author
8383        // pasted a multi-tag block into one `:etiquetas` entry
8384        // instead of splitting into one entry per tag. Without the
8385        // shape gate `"mesh\nhttp"` silently passed validate and
8386        // landed as a YAML-illegal multi-line scalar in the rendered
8387        // Chart.yaml `keywords:` array.
8388        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8389        let err = c.validate_etiquetas().unwrap_err();
8390        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8391            panic!("expected EtiquetaInvalid, got {err:?}");
8392        };
8393        assert_eq!(etiqueta, "mesh\nhttp");
8394        assert!(reason.contains("newline"), "got: {reason}");
8395    }
8396
8397    #[test]
8398    fn validate_etiquetas_rejects_embedded_comma_entry() {
8399        // Canonical CSV-list-separator-confusion footgun: the author
8400        // confused the CSV-style separator convention with the
8401        // `:etiquetas` list grammar. Without the shape gate
8402        // `"mesh,http,grpc"` silently passed validate and landed as a
8403        // single malformed search tag in the rendered Chart.yaml
8404        // `keywords:` array — Artifact Hub's keyword index would
8405        // either silently drop the tag or index it as
8406        // `mesh,http,grpc` instead of three separate tags.
8407        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
8408        let err = c.validate_etiquetas().unwrap_err();
8409        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8410            panic!("expected EtiquetaInvalid, got {err:?}");
8411        };
8412        assert_eq!(etiqueta, "mesh,http,grpc");
8413        assert!(reason.contains('`'), "got: {reason}");
8414        assert!(reason.contains(','), "got: {reason}");
8415    }
8416
8417    #[test]
8418    fn validate_etiquetas_rejects_embedded_slash_entry() {
8419        // Canonical path-separator-confusion footgun: the author
8420        // confused namespace-path notation with the keyword grammar.
8421        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
8422        let err = c.validate_etiquetas().unwrap_err();
8423        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8424            panic!("expected EtiquetaInvalid, got {err:?}");
8425        };
8426        assert_eq!(etiqueta, "caixa/servico");
8427        assert!(reason.contains('/'), "got: {reason}");
8428    }
8429
8430    #[test]
8431    fn validate_etiquetas_rejects_leading_digit_entry() {
8432        // Canonical paste-from-numbered-list footgun: the author
8433        // copied `1. mesh` from a numbered doc and the `1` leaked
8434        // into the tag.
8435        let c = caixa_with_etiquetas(vec!["1mesh"]);
8436        let err = c.validate_etiquetas().unwrap_err();
8437        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8438            panic!("expected EtiquetaInvalid, got {err:?}");
8439        };
8440        assert_eq!(etiqueta, "1mesh");
8441        assert!(reason.contains("digit"), "got: {reason}");
8442    }
8443
8444    #[test]
8445    fn validate_etiquetas_rejects_leading_hyphen_entry() {
8446        // Canonical kebab-leak footgun.
8447        let c = caixa_with_etiquetas(vec!["-foo"]);
8448        let err = c.validate_etiquetas().unwrap_err();
8449        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8450            panic!("expected EtiquetaInvalid, got {err:?}");
8451        };
8452        assert_eq!(etiqueta, "-foo");
8453        assert!(reason.contains('-'), "got: {reason}");
8454    }
8455
8456    #[test]
8457    fn validate_etiquetas_rejects_non_ascii_entry() {
8458        // Canonical paste-from-Unicode-doc footgun. Every legitimate
8459        // search tag is strict ASCII; raw non-ASCII silently
8460        // round-trips inconsistently across NFC/NFD normalization on
8461        // APFS / case-folding filesystems and breaks the Artifact Hub
8462        // keyword search index lookup.
8463        let c = caixa_with_etiquetas(vec!["café"]);
8464        let err = c.validate_etiquetas().unwrap_err();
8465        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8466            panic!("expected EtiquetaInvalid, got {err:?}");
8467        };
8468        assert_eq!(etiqueta, "café");
8469        assert!(reason.contains("non-ASCII"), "got: {reason}");
8470    }
8471
8472    #[test]
8473    fn validate_etiquetas_rejects_period_entry() {
8474        // Canonical namespace-confusion / version-suffix footgun
8475        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
8476        // excludes `.` from the continuation set even though the
8477        // sibling `:caracteristicas` axis (Cargo's feature-name
8478        // grammar) admits it. Tighter than the sibling axis, peer
8479        // with Cargo's own crates.io keyword shape.
8480        let c = caixa_with_etiquetas(vec!["http.1"]);
8481        let err = c.validate_etiquetas().unwrap_err();
8482        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8483            panic!("expected EtiquetaInvalid, got {err:?}");
8484        };
8485        assert_eq!(etiqueta, "http.1");
8486        assert!(reason.contains('.'), "got: {reason}");
8487    }
8488
8489    #[test]
8490    fn validate_etiquetas_empty_takes_precedence_over_shape() {
8491        // Per-entry empty-first cascade pin: an entry that is both
8492        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
8493        // narrower "this entry has no value" structural defect
8494        // dominates the broader shape-predicate diagnostic). The
8495        // empty arm fires before the shape predicate is consulted,
8496        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
8497        // cascade established on the sibling universal-axis Vec<String>
8498        // surface.
8499        let c = caixa_with_etiquetas(vec![""]);
8500        let err = c.validate_etiquetas().unwrap_err();
8501        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8502    }
8503
8504    #[test]
8505    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
8506        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
8507        // entry that is malformed surfaces `EtiquetaInvalid` even when
8508        // a later entry would have collided on duplicate. The
8509        // per-entry shape arm fires inside the same loop iteration as
8510        // the empty arm, before the seen-set insert at end-of-iteration
8511        // — structural per-entry defects dominate the cross-entry
8512        // uniqueness diagnostic. Mirrors the peer
8513        // `validate_autores_shape_takes_precedence_over_duplicate`.
8514        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
8515        let err = c.validate_etiquetas().unwrap_err();
8516        assert!(
8517            matches!(err, ManifestError::EtiquetaInvalid { .. }),
8518            "got {err:?}",
8519        );
8520    }
8521
8522    #[test]
8523    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
8524        // Diagnostic-shape pin on the new shape arm (peer with
8525        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
8526        // the rendered Display surfaces both the offending slot name
8527        // and the offending value verbatim, so a `feira lint` run
8528        // points the author at the exact `:etiquetas` entry to fix.
8529        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8530        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8531        assert!(
8532            rendered.contains(":etiquetas"),
8533            "diagnostic must name the offending slot: {rendered}",
8534        );
8535        assert!(
8536            rendered.contains("mesh\\nhttp"),
8537            "diagnostic must quote the offending value (debug-escaped): {rendered}",
8538        );
8539    }
8540
8541    #[test]
8542    fn validate_etiquetas_rejects_at_21_byte_boundary() {
8543        // The 20-byte cap pin — boundary-exceeding case rejected,
8544        // boundary-accepting case passes. Mirrors the peer
8545        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
8546        // side pin, surfaced at the per-axis caller so the cap
8547        // propagates through validate end-to-end. Constructed as a
8548        // single all-`a` token so only the cap arm fires.
8549        let max_ok = "a".repeat(20);
8550        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
8551        c.validate_etiquetas().unwrap();
8552        let too_long = "a".repeat(21);
8553        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
8554        let err = c.validate_etiquetas().unwrap_err();
8555        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
8556            panic!("expected EtiquetaInvalid, got {err:?}");
8557        };
8558        assert!(reason.contains("20"), "got: {reason}");
8559        assert!(reason.contains("21"), "got: {reason}");
8560    }
8561
8562    #[test]
8563    fn validate_etiquetas_accepts_canonical_shaped_forms() {
8564        // Positive control sweep: every canonical-shaped tag from the
8565        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
8566        // example fixtures plus the substrate-fixed tags caixa-helm
8567        // unions in at chart render. Drift between this list and the
8568        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
8569        // sweep surfaces here — one source of truth for the rule.
8570        let c = caixa_with_etiquetas(vec![
8571            "example",
8572            "aplicacao",
8573            "mesh",
8574            "ecommerce",
8575            "demo",
8576            "infrastructure",
8577            "aws",
8578            "akeyless",
8579            "pangea-native",
8580            "hello-world",
8581            "wasm",
8582            "rust",
8583            "tatara-lisp",
8584            "caixa-servico",
8585            "lareira",
8586        ]);
8587        c.validate_etiquetas().unwrap();
8588    }
8589
8590    // ── validate_autores — universal-axis maintainer shape ────────────
8591
8592    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
8593        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8594        c.autores = autores.into_iter().map(String::from).collect();
8595        c
8596    }
8597
8598    #[test]
8599    fn validate_autores_accepts_empty_list() {
8600        // The empty-list identity: `Caixa::template` emits `:autores ()`,
8601        // so the gate is non-disruptive against every existing manifest.
8602        let c = caixa_with_autores(vec![]);
8603        c.validate_autores().unwrap();
8604    }
8605
8606    #[test]
8607    fn validate_autores_accepts_canonical_forms() {
8608        // Positive control sweep: every canonical-shaped non-empty
8609        // distinct maintainer list passes — the hello-rio / checkout-
8610        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
8611        // multi-author shape downstream packaging surfaces emit.
8612        let c = caixa_with_autores(vec!["pleme-io"]);
8613        c.validate_autores().unwrap();
8614        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
8615        c.validate_autores().unwrap();
8616    }
8617
8618    #[test]
8619    fn validate_autores_rejects_empty_entry() {
8620        // Canonical paste-from-blank-doc footgun. Without the gate the
8621        // empty entry rendered as `maintainers: [{name: "", email: null}]`
8622        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
8623        // to.
8624        let c = caixa_with_autores(vec![""]);
8625        let err = c.validate_autores().unwrap_err();
8626        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8627    }
8628
8629    #[test]
8630    fn validate_autores_rejects_duplicate_entry() {
8631        // Canonical copy-paste-the-wrong-author footgun. Unlike the
8632        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
8633        // dedups the rendered `keywords:` array), the `maintainers:`
8634        // rendering has *no* dedup — duplicates stack verbatim. The
8635        // duplicate-arm names the offending author verbatim.
8636        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
8637        let err = c.validate_autores().unwrap_err();
8638        let ManifestError::AutorDuplicate { autor } = err else {
8639            panic!("expected AutorDuplicate, got {err:?}");
8640        };
8641        assert_eq!(autor, "pleme-io");
8642    }
8643
8644    #[test]
8645    fn validate_autores_empty_takes_precedence_over_duplicate() {
8646        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
8647        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
8648        // "this entry has no value" defect dominates the cross-entry
8649        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
8650        // cascades on `:etiquetas` (`EtiquetaEmpty` before
8651        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
8652        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8653        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
8654        // `MembroDuplicate`).
8655        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
8656        let err = c.validate_autores().unwrap_err();
8657        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8658    }
8659
8660    #[test]
8661    fn validate_autores_duplicate_reports_first_collision() {
8662        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8663        // duplicate (the lexicographically-earliest offending position
8664        // — the second `"a"` at index 2 collides with the first `"a"`
8665        // at index 0), not the later `"b"` collision at index 3,
8666        // peer with every other first-collision diagnostic posture on
8667        // this surface.
8668        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
8669        let err = c.validate_autores().unwrap_err();
8670        let ManifestError::AutorDuplicate { autor } = err else {
8671            panic!("expected AutorDuplicate, got {err:?}");
8672        };
8673        assert_eq!(autor, "a");
8674    }
8675
8676    #[test]
8677    fn validate_autores_case_sensitive() {
8678        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
8679        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
8680        // / `:children :caixa` exact-string-match discipline.
8681        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
8682        c.validate_autores().unwrap();
8683    }
8684
8685    #[test]
8686    fn validate_autores_diagnostic_carries_offending_author() {
8687        // Diagnostic-shape pin (peer with
8688        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
8689        // error's Display surfaces the offending author verbatim, so a
8690        // `feira lint` run can render the diagnostic without re-parsing
8691        // and the author can grep their caixa.lisp for the offending
8692        // value.
8693        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
8694        let rendered = c.validate_autores().unwrap_err().to_string();
8695        assert!(
8696            rendered.contains(":autores"),
8697            "diagnostic must name the offending slot: {rendered}",
8698        );
8699        assert!(
8700            rendered.contains("pleme-io"),
8701            "diagnostic must quote the offending author: {rendered}",
8702        );
8703    }
8704
8705    #[test]
8706    fn validate_autores_rejects_leading_whitespace_entry() {
8707        // Canonical paste-from-aligned-doc footgun. Without the shape
8708        // gate `" pleme-io"` silently passed validate and landed as a
8709        // YAML plain-style scalar with leading whitespace in the
8710        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
8711        // dumper trims leading whitespace from plain-style scalars, so
8712        // the authored space round-tripped inconsistently back through
8713        // `caixa.lisp`. Mirrors the peer
8714        // `validate_descricao_rejects_leading_whitespace`.
8715        let c = caixa_with_autores(vec![" pleme-io"]);
8716        let err = c.validate_autores().unwrap_err();
8717        let ManifestError::AutorInvalid { autor, reason } = err else {
8718            panic!("expected AutorInvalid, got {err:?}");
8719        };
8720        assert_eq!(autor, " pleme-io");
8721        assert!(reason.contains("whitespace"), "got: {reason}");
8722    }
8723
8724    #[test]
8725    fn validate_autores_rejects_trailing_whitespace_entry() {
8726        // Canonical paste-from-doc footgun.
8727        let c = caixa_with_autores(vec!["pleme-io "]);
8728        let err = c.validate_autores().unwrap_err();
8729        let ManifestError::AutorInvalid { autor, reason } = err else {
8730            panic!("expected AutorInvalid, got {err:?}");
8731        };
8732        assert_eq!(autor, "pleme-io ");
8733        assert!(reason.contains("whitespace"), "got: {reason}");
8734    }
8735
8736    #[test]
8737    fn validate_autores_rejects_embedded_newline_entry() {
8738        // Canonical paste-from-multiline-doc footgun — the author
8739        // pasted a multi-line block of author records into one
8740        // `:autores` entry instead of splitting into one entry per
8741        // author. Without the shape gate `"alice\nbob"` silently
8742        // passed validate and landed as a YAML-illegal multi-line
8743        // scalar in the rendered Chart.yaml `maintainers:` array.
8744        let c = caixa_with_autores(vec!["alice\nbob"]);
8745        let err = c.validate_autores().unwrap_err();
8746        let ManifestError::AutorInvalid { autor, reason } = err else {
8747            panic!("expected AutorInvalid, got {err:?}");
8748        };
8749        assert_eq!(autor, "alice\nbob");
8750        assert!(reason.contains("newline"), "got: {reason}");
8751    }
8752
8753    #[test]
8754    fn validate_autores_rejects_embedded_carriage_return_entry() {
8755        // Canonical paste-from-Windows-CRLF-doc footgun.
8756        let c = caixa_with_autores(vec!["alice\rbob"]);
8757        let err = c.validate_autores().unwrap_err();
8758        let ManifestError::AutorInvalid { autor, reason } = err else {
8759            panic!("expected AutorInvalid, got {err:?}");
8760        };
8761        assert_eq!(autor, "alice\rbob");
8762        assert!(reason.contains("carriage return"), "got: {reason}");
8763    }
8764
8765    #[test]
8766    fn validate_autores_rejects_embedded_tab_entry() {
8767        // Canonical tab-from-aligned-doc footgun.
8768        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
8769        let err = c.validate_autores().unwrap_err();
8770        let ManifestError::AutorInvalid { autor, reason } = err else {
8771            panic!("expected AutorInvalid, got {err:?}");
8772        };
8773        assert_eq!(autor, "Pleme\tContributors");
8774        assert!(reason.contains("tab"), "got: {reason}");
8775    }
8776
8777    #[test]
8778    fn validate_autores_rejects_embedded_control_bytes_entry() {
8779        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
8780        // surface the same control-byte arm.
8781        for entry in [
8782            "alice\x00bob",
8783            "alice\x07bob",
8784            "alice\x1bbob",
8785            "alice\x7fbob",
8786        ] {
8787            let c = caixa_with_autores(vec![entry]);
8788            let err = c.validate_autores().unwrap_err();
8789            let ManifestError::AutorInvalid { autor, reason } = err else {
8790                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
8791            };
8792            assert_eq!(autor, entry);
8793            assert!(
8794                reason.contains("control character"),
8795                "{entry:?} reason: {reason}",
8796            );
8797        }
8798    }
8799
8800    #[test]
8801    fn validate_autores_accepts_unicode_entry() {
8802        // Unicode positive control: realistic maintainer names carry
8803        // Unicode (`François`, `日本語`, `naïve`). The predicate must
8804        // round-trip Unicode losslessly, peer with the
8805        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
8806        // sweep.
8807        let c = caixa_with_autores(vec![
8808            "François Dupont",
8809            "日本語の名前",
8810            "naïve <naive@example.com>",
8811        ]);
8812        c.validate_autores().unwrap();
8813    }
8814
8815    #[test]
8816    fn validate_autores_empty_takes_precedence_over_shape() {
8817        // Per-entry empty-first cascade pin: an entry that is both
8818        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
8819        // "this entry has no value" structural defect dominates the
8820        // broader shape-predicate diagnostic). The empty arm fires
8821        // before the shape predicate is consulted, mirroring the peer
8822        // `validate_repositorio_empty_takes_precedence_over_shape`
8823        // cascade on the universal `Option<String>` siblings — and now
8824        // established on the Vec<String> per-entry surface.
8825        let c = caixa_with_autores(vec![""]);
8826        let err = c.validate_autores().unwrap_err();
8827        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8828    }
8829
8830    #[test]
8831    fn validate_autores_shape_takes_precedence_over_duplicate() {
8832        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
8833        // entry that is malformed surfaces `AutorInvalid` even when a
8834        // later entry would have collided on duplicate. The per-entry
8835        // shape arm fires inside the same loop iteration as the empty
8836        // arm, before the seen-set insert at end-of-iteration —
8837        // structural per-entry defects dominate the cross-entry
8838        // uniqueness diagnostic.
8839        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
8840        let err = c.validate_autores().unwrap_err();
8841        assert!(
8842            matches!(err, ManifestError::AutorInvalid { .. }),
8843            "got {err:?}",
8844        );
8845    }
8846
8847    #[test]
8848    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
8849        // Diagnostic-shape pin on the new shape arm (peer with
8850        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
8851        // the rendered Display surfaces both the offending slot name
8852        // and the offending value verbatim, so a `feira lint` run
8853        // points the author at the exact `:autores` entry to fix.
8854        let c = caixa_with_autores(vec!["alice\nbob"]);
8855        let rendered = c.validate_autores().unwrap_err().to_string();
8856        assert!(
8857            rendered.contains(":autores"),
8858            "diagnostic must name the offending slot: {rendered}",
8859        );
8860        assert!(
8861            rendered.contains("alice\\nbob"),
8862            "diagnostic must quote the offending value (debug-escaped): {rendered}",
8863        );
8864    }
8865
8866    #[test]
8867    fn validate_autores_rejects_at_129_byte_boundary() {
8868        // The 128-byte cap pin — boundary-exceeding case rejected,
8869        // boundary-accepting case passes. Mirrors the peer
8870        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
8871        // substrate-side pin, surfaced at the per-axis caller so the
8872        // cap propagates through validate end-to-end. Constructed as
8873        // a single all-`a` token so only the cap arm fires.
8874        let max_ok = "a".repeat(128);
8875        let c = caixa_with_autores(vec![max_ok.as_str()]);
8876        c.validate_autores().unwrap();
8877        let too_long = "a".repeat(129);
8878        let c = caixa_with_autores(vec![too_long.as_str()]);
8879        let err = c.validate_autores().unwrap_err();
8880        let ManifestError::AutorInvalid { reason, .. } = err else {
8881            panic!("expected AutorInvalid, got {err:?}");
8882        };
8883        assert!(reason.contains("128"), "got: {reason}");
8884        assert!(reason.contains("129"), "got: {reason}");
8885    }
8886
8887    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
8888
8889    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
8890        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8891        c.repositorio = repositorio.map(String::from);
8892        c
8893    }
8894
8895    #[test]
8896    fn validate_repositorio_accepts_none() {
8897        // The omit-the-slot identity: `:repositorio` is optional. The
8898        // gate is a no-op when the author didn't declare a value —
8899        // every caixa without a `:repositorio` line trivially passes,
8900        // and the substrate-side renderers fall back to their
8901        // documented placeholder (`caixa-helm`'s `home: None`,
8902        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
8903        // URL). Mirrors the peer `validate_restart_window_accepts_none`
8904        // posture on the other `Option<String>` Caixa slot.
8905        let c = caixa_with_repositorio(None);
8906        c.validate_repositorio().unwrap();
8907    }
8908
8909    #[test]
8910    fn validate_repositorio_accepts_canonical_forms() {
8911        // Positive control sweep across every documented `:repositorio`
8912        // authoring shape — the same union the shared
8913        // `crate::render::is_git_repo_url` predicate accepts and the
8914        // peer `:deps :fonte :repo` axis already routes through.
8915        // Covers the `github:` shorthand (the canonical pleme-io
8916        // convention used in the `:repositorio` field of every
8917        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
8918        // `examples/`), the `https://…` URL the README quickstart uses,
8919        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
8920        // `file://` URL schemes the shared predicate documents.
8921        for repo in [
8922            "github:pleme-io/hello-rio",
8923            "github:pleme-io/checkout",
8924            "https://github.com/pleme-io/hello-rio",
8925            "ssh://git@github.com/pleme-io/hello-rio.git",
8926            "git://github.com/pleme-io/hello-rio.git",
8927            "git@github.com:pleme-io/hello-rio.git",
8928            "file:///srv/pleme/hello-rio",
8929        ] {
8930            let c = caixa_with_repositorio(Some(repo));
8931            c.validate_repositorio()
8932                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
8933        }
8934    }
8935
8936    #[test]
8937    fn validate_repositorio_rejects_empty_some() {
8938        // Canonical paste-from-blank-doc footgun. The narrower
8939        // [`ManifestError::RepositorioEmpty`] arm fires before the
8940        // shape predicate is consulted, mirroring the empty-first
8941        // cascade every peer per-axis identity gate uses
8942        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
8943        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
8944        // the empty `Some("")` silently passed the renderer's
8945        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
8946        // on `None`) and landed as `home: ""` in `Chart.yaml` /
8947        // `url: ""` in the FluxCD `GitRepository`.
8948        let c = caixa_with_repositorio(Some(""));
8949        let err = c.validate_repositorio().unwrap_err();
8950        assert!(
8951            matches!(err, ManifestError::RepositorioEmpty),
8952            "got {err:?}",
8953        );
8954    }
8955
8956    #[test]
8957    fn validate_repositorio_rejects_whitespace() {
8958        // Paste-from-doc whitespace footgun. The shared
8959        // `is_git_repo_url` predicate refuses any whitespace byte; a
8960        // trailing space in a `:repositorio` value silently broke
8961        // `git clone '<value> '` at clone time. The diagnostic names
8962        // the offending value verbatim.
8963        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
8964        let err = c.validate_repositorio().unwrap_err();
8965        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
8966            panic!("expected RepositorioInvalid, got {err:?}");
8967        };
8968        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
8969    }
8970
8971    #[test]
8972    fn validate_repositorio_rejects_control_char() {
8973        // Paste-from-multiline-doc CRLF footgun — control characters
8974        // at the URL boundary are a class of subprocess-arg injection
8975        // and break git's URL parser at every porcelain entry point.
8976        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
8977        let err = c.validate_repositorio().unwrap_err();
8978        assert!(
8979            matches!(err, ManifestError::RepositorioInvalid { .. }),
8980            "got {err:?}",
8981        );
8982    }
8983
8984    #[test]
8985    fn validate_repositorio_rejects_leading_dash() {
8986        // Canonical CLI-argument-injection footgun: `git clone <repo>`
8987        // interprets a leading `-` as a CLI flag, so a
8988        // `-upload-pack=…` value escapes the subprocess argument
8989        // boundary. The shared predicate refuses every leading-`-`
8990        // shape at validate time.
8991        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
8992        let err = c.validate_repositorio().unwrap_err();
8993        assert!(
8994            matches!(err, ManifestError::RepositorioInvalid { .. }),
8995            "got {err:?}",
8996        );
8997    }
8998
8999    #[test]
9000    fn validate_repositorio_rejects_missing_colon_separator() {
9001        // The bare `org/repo` ambiguity footgun — `git clone` reads
9002        // a no-`:` form as a relative filesystem path rather than the
9003        // GitHub-shorthand expansion the author probably intended.
9004        // The shared predicate refuses every shape without a `:`
9005        // separator.
9006        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9007        let err = c.validate_repositorio().unwrap_err();
9008        assert!(
9009            matches!(err, ManifestError::RepositorioInvalid { .. }),
9010            "got {err:?}",
9011        );
9012    }
9013
9014    #[test]
9015    fn validate_repositorio_rejects_fragment_anchor() {
9016        // Paste-from-browser-address-bar footgun on the
9017        // `:repositorio` axis — an author copies a GitHub permalink
9018        // to a README section / line-permalink and forgets to trim
9019        // the `#fragment` tail. The shared `is_git_repo_url`
9020        // predicate refuses the byte at the URL-grammar layer
9021        // (libcurl strips the fragment before opening the
9022        // transport, so the byte rides verbatim into the rendered
9023        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
9024        // fields but is silently dropped on the wire — two
9025        // manifest variants whose values differ only in their
9026        // fragment anchor lock to two distinct rendered artifacts
9027        // for the byte-identical clone, defeating the THEORY.md
9028        // §V.2 render-determinism contract on the `:repositorio`
9029        // axis the peer `:fonte :repo` axis already closes).
9030        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
9031        let err = c.validate_repositorio().unwrap_err();
9032        let ManifestError::RepositorioInvalid {
9033            repositorio,
9034            reason,
9035        } = err
9036        else {
9037            panic!("expected RepositorioInvalid, got {err:?}");
9038        };
9039        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
9040        assert!(
9041            reason.contains("must not contain `#`"),
9042            "reason must surface the fragment-`#` arm, got {reason:?}"
9043        );
9044    }
9045
9046    #[test]
9047    fn validate_repositorio_rejects_query_string() {
9048        // Paste-from-browser-address-bar footgun on the
9049        // `:repositorio` axis (peer with the a68f818 fragment-`#`
9050        // arm on the same axis). An author copies a GitHub tab
9051        // deep-link out of the address bar and forgets to trim
9052        // the `?tab=…` query tail. The shared `is_git_repo_url`
9053        // predicate refuses the byte at the URL-grammar layer
9054        // (GitHub / GitLab / Bitbucket silently ignore the
9055        // `?query` tail and serve the same repo regardless, so
9056        // the byte rides verbatim into the rendered `Chart.yaml`
9057        // `home:` and FluxCD `GitRepository` `url:` fields but
9058        // is silently masked at the wire — two manifest variants
9059        // whose values differ only in their query tail lock to
9060        // two distinct rendered artifacts for the byte-identical
9061        // clone, defeating the THEORY.md §V.2 render-determinism
9062        // contract on the `:repositorio` axis the peer `:fonte
9063        // :repo` axis already closes).
9064        let c = caixa_with_repositorio(Some(
9065            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
9066        ));
9067        let err = c.validate_repositorio().unwrap_err();
9068        let ManifestError::RepositorioInvalid {
9069            repositorio,
9070            reason,
9071        } = err
9072        else {
9073            panic!("expected RepositorioInvalid, got {err:?}");
9074        };
9075        assert_eq!(
9076            repositorio,
9077            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
9078        );
9079        assert!(
9080            reason.contains("must not contain `?`"),
9081            "reason must surface the query-`?` arm, got {reason:?}"
9082        );
9083    }
9084
9085    #[test]
9086    fn validate_repositorio_rejects_embedded_backslash() {
9087        // Windows-file-path-confusion footgun on the `:repositorio`
9088        // axis (peer with the prior fragment-`#` / query-`?` arms on
9089        // the same axis, and peer with the new dep-level `:fonte :repo`
9090        // backslash arm on the URL-grammar trajectory). An author
9091        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
9092        // hello-rio` into the `:repositorio` slot, expecting the
9093        // `lareira-<nome>` chart's `home:` field and the FluxCD
9094        // `GitRepository` `url:` field to render the canonical local
9095        // file-URI. The shared `is_git_repo_url` predicate refuses
9096        // the byte at the URL-grammar layer (libcurl silently
9097        // translates `\` → `/` on some platforms and refuses it on
9098        // others, so the byte rides verbatim into the rendered
9099        // artifacts but is silently rewritten or rejected at the wire
9100        // — two manifest variants whose values differ only in
9101        // backslash-vs-forward-slash lock to two distinct rendered
9102        // artifacts for the byte-identical clone, defeating the
9103        // THEORY.md §V.2 render-determinism contract on the
9104        // `:repositorio` axis the peer `:fonte :repo` axis already
9105        // closes).
9106        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
9107        let err = c.validate_repositorio().unwrap_err();
9108        let ManifestError::RepositorioInvalid {
9109            repositorio,
9110            reason,
9111        } = err
9112        else {
9113            panic!("expected RepositorioInvalid, got {err:?}");
9114        };
9115        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
9116        assert!(
9117            reason.contains("must not contain `\\`"),
9118            "reason must surface the backslash-`\\` arm, got {reason:?}"
9119        );
9120    }
9121
9122    #[test]
9123    fn validate_repositorio_rejects_uri_template_placeholder() {
9124        // URI Template (RFC 6570) placeholder footgun on the
9125        // `:repositorio` axis (peer with the prior fragment-`#` /
9126        // query-`?` / backslash-`\` arms on the same axis, and peer
9127        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
9128        // URL-grammar trajectory). An author pastes a quick-start
9129        // README snippet / OpenAPI `servers:` URL / Helm chart
9130        // `home:` template carrying unresolved `{org}` / `{repo}`
9131        // placeholders into the `:repositorio` slot, expecting the
9132        // substrate to resolve the placeholder downstream. The
9133        // shared `is_git_repo_url` predicate refuses the byte at the
9134        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
9135        // `%7B` / `%7D` on the wire, so the byte round-trips
9136        // inconsistently between the rendered `Chart.yaml home:` /
9137        // FluxCD `GitRepository url:` and the resolver's `git clone`
9138        // invocation, defeating the THEORY.md §V.2 render-
9139        // determinism contract on the `:repositorio` axis the peer
9140        // `:fonte :repo` axis already closes; every git porcelain
9141        // entry-point additionally fetches a nonexistent literal-
9142        // `{placeholder}`-named path far from the source caixa.lisp).
9143        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
9144        let err = c.validate_repositorio().unwrap_err();
9145        let ManifestError::RepositorioInvalid {
9146            repositorio,
9147            reason,
9148        } = err
9149        else {
9150            panic!("expected RepositorioInvalid, got {err:?}");
9151        };
9152        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
9153        assert!(
9154            reason.contains("must not contain `{`"),
9155            "reason must surface the open-brace `{{` arm, got {reason:?}"
9156        );
9157        assert!(
9158            reason.contains("URI Template") || reason.contains("RFC 6570"),
9159            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
9160        );
9161    }
9162
9163    #[test]
9164    fn validate_repositorio_empty_takes_precedence_over_shape() {
9165        // Empty-first cascade pin: the empty `Some("")` surfaces the
9166        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
9167        // `RepositorioInvalid`, mirroring the peer
9168        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9169        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
9170        // `is_git_repo_url` predicate also rejects the empty input
9171        // (defensively, with its own `"must not be empty"` reason),
9172        // but the manifest-layer empty arm runs first to surface the
9173        // narrower diagnostic verbatim.
9174        let c = caixa_with_repositorio(Some(""));
9175        let err = c.validate_repositorio().unwrap_err();
9176        assert!(
9177            matches!(err, ManifestError::RepositorioEmpty),
9178            "got {err:?}",
9179        );
9180    }
9181
9182    #[test]
9183    fn validate_repositorio_diagnostic_carries_offending_value() {
9184        // Diagnostic-shape pin (peer with
9185        // `validate_autores_diagnostic_carries_offending_author`): the
9186        // error's Display surfaces the offending value + slot name
9187        // verbatim, so a `feira lint` run can render the diagnostic
9188        // without re-parsing and the author can grep their caixa.lisp
9189        // for the offending `:repositorio` value.
9190        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9191        let rendered = c.validate_repositorio().unwrap_err().to_string();
9192        assert!(
9193            rendered.contains(":repositorio"),
9194            "diagnostic must name the offending slot: {rendered}",
9195        );
9196        assert!(
9197            rendered.contains("pleme-io/hello-rio"),
9198            "diagnostic must quote the offending value: {rendered}",
9199        );
9200    }
9201
9202    // ── validate_descricao — universal-axis Chart.yaml description shape ──
9203
9204    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
9205        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9206        c.descricao = descricao.map(String::from);
9207        c
9208    }
9209
9210    #[test]
9211    fn validate_descricao_accepts_none() {
9212        // The omit-the-slot identity: `:descricao` is optional. The
9213        // gate is a no-op when the author didn't declare a value —
9214        // every caixa without a `:descricao` line trivially passes,
9215        // and the substrate-side renderers fall back to their
9216        // documented `caixa.nome`-derived placeholder. Mirrors the
9217        // peer `validate_repositorio_accepts_none` posture on the
9218        // sibling `Option<String>` Caixa slot.
9219        let c = caixa_with_descricao(None);
9220        c.validate_descricao().unwrap();
9221    }
9222
9223    #[test]
9224    fn validate_descricao_accepts_canonical_summary() {
9225        // Positive control: the canonical pleme-io descricao shape —
9226        // a short free-form prose summary — passes the gate. Covers
9227        // the fixture shapes the `caixa-helm` / `caixa-flux` /
9228        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
9229        // wasip2 caixa Servico."`, `"Checkout flow."`).
9230        for desc in [
9231            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9232            "Checkout flow.",
9233            "AWS provider caixa for tatara-lisp",
9234            "FIXME — describe this caixa",
9235            "x",
9236        ] {
9237            let c = caixa_with_descricao(Some(desc));
9238            c.validate_descricao()
9239                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
9240        }
9241    }
9242
9243    #[test]
9244    fn validate_descricao_rejects_empty_some() {
9245        // Canonical paste-from-blank-doc footgun. Without this gate
9246        // the empty `Some("")` silently passed the renderer's
9247        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9248        // on `None`) and landed as `description: ""` in `Chart.yaml`
9249        // and a blank `README.md` header. Mirrors the peer
9250        // [`ManifestError::RepositorioEmpty`] empty-arm on the
9251        // sibling `Option<String>` Caixa slot.
9252        let c = caixa_with_descricao(Some(""));
9253        let err = c.validate_descricao().unwrap_err();
9254        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9255    }
9256
9257    #[test]
9258    fn validate_descricao_rejects_leading_whitespace() {
9259        // Paste-from-aligned-doc footgun: a leading ASCII space the
9260        // bare empty-arm gate accepted, the shape predicate now
9261        // refuses. The diagnostic carries the offending value
9262        // verbatim (with the leading space preserved) so the author
9263        // can grep their caixa.lisp for the exact `:descricao` line
9264        // and fix the round-trip-inconsistent leading whitespace.
9265        // Mirrors the peer
9266        // `validate_licenca_rejects_leading_whitespace` arm on the
9267        // sibling `:licenca` axis.
9268        let c = caixa_with_descricao(Some(" Checkout flow."));
9269        let err = c.validate_descricao().unwrap_err();
9270        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9271            panic!("expected DescricaoInvalid, got {err:?}");
9272        };
9273        assert_eq!(descricao, " Checkout flow.");
9274        assert!(reason.contains("whitespace"), "got: {reason:?}");
9275    }
9276
9277    #[test]
9278    fn validate_descricao_rejects_trailing_whitespace() {
9279        // Paste-from-doc footgun: a trailing ASCII space the bare
9280        // empty-arm gate accepted, the shape predicate now refuses.
9281        let c = caixa_with_descricao(Some("Checkout flow. "));
9282        let err = c.validate_descricao().unwrap_err();
9283        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9284            panic!("expected DescricaoInvalid, got {err:?}");
9285        };
9286        assert_eq!(descricao, "Checkout flow. ");
9287        assert!(reason.contains("whitespace"), "got: {reason:?}");
9288    }
9289
9290    #[test]
9291    fn validate_descricao_rejects_embedded_newline() {
9292        // Paste-from-multiline-doc footgun: an embedded LF the bare
9293        // empty-arm gate accepted, the shape predicate now refuses.
9294        // Without this gate the embedded newline silently landed in
9295        // the rendered Chart.yaml as a multi-line YAML block scalar,
9296        // and every chart-aware UI (`helm list`, `helm search`,
9297        // Artifact Hub) renders the description in a single-line
9298        // column so the embedded newline is silently dropped at
9299        // every downstream consumer.
9300        let c = caixa_with_descricao(Some("Checkout\nflow."));
9301        let err = c.validate_descricao().unwrap_err();
9302        assert!(
9303            matches!(err, ManifestError::DescricaoInvalid { .. }),
9304            "got {err:?}",
9305        );
9306        assert!(err.to_string().contains("newline"), "got {err}");
9307    }
9308
9309    #[test]
9310    fn validate_descricao_rejects_embedded_carriage_return() {
9311        // Paste-from-Windows-CRLF-doc footgun.
9312        let c = caixa_with_descricao(Some("Checkout\rflow."));
9313        let err = c.validate_descricao().unwrap_err();
9314        assert!(
9315            matches!(err, ManifestError::DescricaoInvalid { .. }),
9316            "got {err:?}",
9317        );
9318        assert!(err.to_string().contains("carriage return"), "got {err}");
9319    }
9320
9321    #[test]
9322    fn validate_descricao_rejects_embedded_tab() {
9323        // Tab-from-aligned-doc footgun.
9324        let c = caixa_with_descricao(Some("Checkout\tflow."));
9325        let err = c.validate_descricao().unwrap_err();
9326        assert!(
9327            matches!(err, ManifestError::DescricaoInvalid { .. }),
9328            "got {err:?}",
9329        );
9330        assert!(err.to_string().contains("tab"), "got {err}");
9331    }
9332
9333    #[test]
9334    fn validate_descricao_rejects_embedded_control_bytes() {
9335        // Paste-from-binary-blob footgun: every other control byte
9336        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
9337        // the peer SPDX-expression control-byte arm.
9338        for s in [
9339            "Checkout\x00flow.",
9340            "Checkout\x07flow.",
9341            "Checkout\x1bflow.",
9342            "Checkout\x7fflow.",
9343        ] {
9344            let c = caixa_with_descricao(Some(s));
9345            let err = c.validate_descricao().unwrap_err();
9346            assert!(
9347                matches!(err, ManifestError::DescricaoInvalid { .. }),
9348                "{s:?} got {err:?}",
9349            );
9350            assert!(
9351                err.to_string().contains("control character"),
9352                "{s:?} got {err}",
9353            );
9354        }
9355    }
9356
9357    #[test]
9358    fn validate_descricao_accepts_unicode_prose() {
9359        // Positive control: Unicode prose is accepted — the
9360        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
9361        // and `Caixa::template`'s `"FIXME — describe this caixa"`
9362        // scaffold every `feira init` emits must continue to pass.
9363        for s in [
9364            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9365            "FIXME — describe this caixa",
9366            "Caixa pour le projet tâche",
9367            "日本語の説明",
9368        ] {
9369            let c = caixa_with_descricao(Some(s));
9370            c.validate_descricao()
9371                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
9372        }
9373    }
9374
9375    #[test]
9376    fn validate_descricao_empty_takes_precedence_over_shape() {
9377        // Cascade pin: a `Some("")` surfaces the narrower
9378        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
9379        // shape-predicate arm. Mirrors the peer
9380        // `validate_licenca_empty_takes_precedence_over_shape` pin
9381        // on the sibling `:licenca` axis.
9382        let c = caixa_with_descricao(Some(""));
9383        let err = c.validate_descricao().unwrap_err();
9384        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9385    }
9386
9387    #[test]
9388    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
9389        // Diagnostic-shape pin: the error's Display surfaces both
9390        // the `:descricao` slot name and the offending value
9391        // verbatim, so a `feira lint` run can render the diagnostic
9392        // without re-parsing and the author can grep their caixa.lisp
9393        // for the offending `:descricao` line. Mirrors the peer
9394        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
9395        // pin (ee2e888) on the sibling `:licenca` axis.
9396        // The `{descricao:?}` Debug format escapes embedded control
9397        // bytes; the quoted offending value surfaces as
9398        // `"Checkout\nflow."` (literal backslash-n) in the rendered
9399        // diagnostic. The author can grep their caixa.lisp for the
9400        // literal `Checkout` summary prefix.
9401        let c = caixa_with_descricao(Some("Checkout\nflow."));
9402        let rendered = c.validate_descricao().unwrap_err().to_string();
9403        assert!(
9404            rendered.contains(":descricao"),
9405            "diagnostic must name the offending slot: {rendered}",
9406        );
9407        assert!(
9408            rendered.contains("Checkout\\nflow."),
9409            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9410        );
9411    }
9412
9413    #[test]
9414    fn validate_descricao_template_passes() {
9415        // Round-trip pin: the bare `Caixa::template` shape carries
9416        // `:descricao "FIXME — describe this caixa"` (a non-empty
9417        // sentinel), so the template-derived Caixa passes the gate by
9418        // construction. A future template-shape change that omits or
9419        // empties `:descricao` would surface here as a regression.
9420        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9421        c.validate_descricao().unwrap();
9422    }
9423
9424    #[test]
9425    fn validate_descricao_diagnostic_names_offending_slot() {
9426        // Diagnostic-shape pin (peer with
9427        // `validate_repositorio_diagnostic_carries_offending_value`):
9428        // the error's Display surfaces the `:descricao` slot name
9429        // verbatim, so a `feira lint` run can render the diagnostic
9430        // without re-parsing and the author can grep their caixa.lisp
9431        // for the offending `:descricao` line.
9432        let c = caixa_with_descricao(Some(""));
9433        let rendered = c.validate_descricao().unwrap_err().to_string();
9434        assert!(
9435            rendered.contains(":descricao"),
9436            "diagnostic must name the offending slot: {rendered}",
9437        );
9438    }
9439
9440    // ── validate_licenca — universal-axis chart README license shape ──
9441
9442    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
9443        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9444        c.licenca = licenca.map(String::from);
9445        c
9446    }
9447
9448    #[test]
9449    fn validate_licenca_accepts_none() {
9450        // The omit-the-slot identity: `:licenca` is optional. The
9451        // gate is a no-op when the author didn't declare a value —
9452        // every caixa without a `:licenca` line trivially passes,
9453        // and the substrate-side `caixa-helm` renderer falls back to
9454        // the documented `"MIT"` placeholder. Mirrors the peer
9455        // `validate_descricao_accepts_none` posture on the sibling
9456        // `Option<String>` Caixa slot.
9457        let c = caixa_with_licenca(None);
9458        c.validate_licenca().unwrap();
9459    }
9460
9461    #[test]
9462    fn validate_licenca_accepts_canonical_expressions() {
9463        // Positive control: every canonical SPDX expression shape
9464        // pleme-io carries in its existing fixtures + the canonical
9465        // SPDX dual-license / with-exception / `+`-suffix / grouped /
9466        // user-defined-reference shapes all pass the gate. Covers
9467        // the single-license, `OR`-compound, `AND`-compound,
9468        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
9469        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
9470        // production the SPDX 2.1 expression grammar admits that
9471        // sits within the alphabet floor the
9472        // `is_spdx_expression_shape` predicate enforces.
9473        for lic in [
9474            "MIT",
9475            "Apache-2.0",
9476            "Apache-2.0 OR MIT",
9477            "Apache-2.0 AND MIT",
9478            "BSD-3-Clause",
9479            "MPL-2.0",
9480            "GPL-3.0-or-later",
9481            "GPL-2.0+",
9482            "Apache-2.0 WITH LLVM-exception",
9483            "(MIT OR Apache-2.0) AND BSD-3-Clause",
9484            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
9485            "LicenseRef-MyLicense",
9486            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
9487            "x",
9488        ] {
9489            let c = caixa_with_licenca(Some(lic));
9490            c.validate_licenca()
9491                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
9492        }
9493    }
9494
9495    #[test]
9496    fn validate_licenca_rejects_trailing_whitespace() {
9497        // Paste-from-doc whitespace footgun. A trailing space in the
9498        // `:licenca` value would silently break a downstream SPDX
9499        // parser that splits on exact `AND` / `OR` / `WITH` keyword
9500        // boundaries. The shape predicate refuses every trailing
9501        // whitespace byte by construction. Peer with
9502        // `validate_repositorio_rejects_whitespace` and
9503        // `validate_edicao_rejects_trailing_whitespace`.
9504        let c = caixa_with_licenca(Some("MIT "));
9505        let err = c.validate_licenca().unwrap_err();
9506        let ManifestError::LicencaInvalid { licenca, .. } = err else {
9507            panic!("expected LicencaInvalid, got {err:?}");
9508        };
9509        assert_eq!(licenca, "MIT ");
9510    }
9511
9512    #[test]
9513    fn validate_licenca_rejects_leading_whitespace() {
9514        // Symmetric paste-from-doc whitespace footgun on the leading
9515        // boundary — the gate refuses every shape that starts with a
9516        // space byte by construction. Peer with
9517        // `validate_edicao_rejects_leading_whitespace`.
9518        let c = caixa_with_licenca(Some(" MIT"));
9519        let err = c.validate_licenca().unwrap_err();
9520        assert!(
9521            matches!(err, ManifestError::LicencaInvalid { .. }),
9522            "got {err:?}",
9523        );
9524    }
9525
9526    #[test]
9527    fn validate_licenca_rejects_control_char() {
9528        // Paste-from-multiline-doc CRLF footgun — control characters
9529        // at the value boundary land as a malformed line in the
9530        // rendered chart `README.md` `## License` section. Peer with
9531        // `validate_repositorio_rejects_control_char` and
9532        // `validate_edicao_rejects_control_char`.
9533        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
9534            let c = caixa_with_licenca(Some(lic));
9535            let err = c.validate_licenca().unwrap_err();
9536            assert!(
9537                matches!(err, ManifestError::LicencaInvalid { .. }),
9538                "expected LicencaInvalid on {lic:?}, got {err:?}",
9539            );
9540        }
9541    }
9542
9543    #[test]
9544    fn validate_licenca_rejects_tab() {
9545        // Tab-from-aligned-doc footgun — SPDX expressions use a
9546        // single ASCII space between tokens; a tab breaks every
9547        // downstream SPDX parser that splits on exact `" "`
9548        // boundaries.
9549        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
9550        let err = c.validate_licenca().unwrap_err();
9551        assert!(
9552            matches!(err, ManifestError::LicencaInvalid { .. }),
9553            "got {err:?}",
9554        );
9555    }
9556
9557    #[test]
9558    fn validate_licenca_rejects_non_ascii() {
9559        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
9560        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
9561        // ".")` production. The shape predicate refuses every
9562        // non-ASCII byte by construction; peer with
9563        // `validate_edicao_rejects_non_ascii_lookalike`.
9564        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
9565            let c = caixa_with_licenca(Some(lic));
9566            let err = c.validate_licenca().unwrap_err();
9567            assert!(
9568                matches!(err, ManifestError::LicencaInvalid { .. }),
9569                "expected LicencaInvalid on {lic:?}, got {err:?}",
9570            );
9571        }
9572    }
9573
9574    #[test]
9575    fn validate_licenca_rejects_underscore() {
9576        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
9577        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
9578        // snake-case identifier conventions that don't apply to the
9579        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
9580        // "-" / "."`). The shape predicate refuses every underscore
9581        // byte by construction.
9582        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
9583            let c = caixa_with_licenca(Some(lic));
9584            let err = c.validate_licenca().unwrap_err();
9585            assert!(
9586                matches!(err, ManifestError::LicencaInvalid { .. }),
9587                "expected LicencaInvalid on {lic:?}, got {err:?}",
9588            );
9589        }
9590    }
9591
9592    #[test]
9593    fn validate_licenca_rejects_comma_separator() {
9594        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
9595        // SPDX expressions compose multiple licenses via `AND` / `OR`
9596        // keywords, not the comma separator. The shape predicate
9597        // refuses every comma byte by construction.
9598        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
9599            let c = caixa_with_licenca(Some(lic));
9600            let err = c.validate_licenca().unwrap_err();
9601            assert!(
9602                matches!(err, ManifestError::LicencaInvalid { .. }),
9603                "expected LicencaInvalid on {lic:?}, got {err:?}",
9604            );
9605        }
9606    }
9607
9608    #[test]
9609    fn validate_licenca_rejects_slash_dual_license() {
9610        // Slash-dual-license colloquial idiom footgun — the
9611        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
9612        // `package.license` field but non-SPDX; the SPDX equivalent
9613        // is `MIT OR Apache-2.0`. The shape predicate refuses every
9614        // forward-slash byte by construction.
9615        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
9616            let c = caixa_with_licenca(Some(lic));
9617            let err = c.validate_licenca().unwrap_err();
9618            assert!(
9619                matches!(err, ManifestError::LicencaInvalid { .. }),
9620                "expected LicencaInvalid on {lic:?}, got {err:?}",
9621            );
9622        }
9623    }
9624
9625    #[test]
9626    fn validate_licenca_rejects_semicolon_separator() {
9627        // Semicolon-list-separator confusion footgun — adjacent to
9628        // the comma-separator idiom, every list-separator-belongs-
9629        // to-list-grammar confusion lands here.
9630        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
9631        let err = c.validate_licenca().unwrap_err();
9632        assert!(
9633            matches!(err, ManifestError::LicencaInvalid { .. }),
9634            "got {err:?}",
9635        );
9636    }
9637
9638    #[test]
9639    fn validate_licenca_empty_takes_precedence_over_shape() {
9640        // Empty-first cascade pin: the empty `Some("")` surfaces the
9641        // narrower `LicencaEmpty` not the shape-predicate-wrapped
9642        // `LicencaInvalid`, mirroring the peer
9643        // `validate_edicao_empty_takes_precedence_over_shape` and
9644        // `validate_repositorio_empty_takes_precedence_over_shape`
9645        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
9646        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
9647        // The shape predicate also refuses the empty input
9648        // (defensively — `"must not be empty"`), but the manifest-
9649        // layer empty arm runs first to surface the narrower
9650        // diagnostic verbatim.
9651        let c = caixa_with_licenca(Some(""));
9652        let err = c.validate_licenca().unwrap_err();
9653        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
9654    }
9655
9656    #[test]
9657    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
9658        // Diagnostic-shape pin on the shape-predicate arm (peer with
9659        // `validate_edicao_invalid_diagnostic_carries_offending_value`
9660        // and `validate_repositorio_diagnostic_carries_offending_value`):
9661        // the error's Display surfaces the offending value + slot
9662        // name verbatim, so a `feira lint` run can render the
9663        // diagnostic without re-parsing and the author can grep
9664        // their caixa.lisp for the offending `:licenca` value.
9665        let c = caixa_with_licenca(Some("Apache_2.0"));
9666        let rendered = c.validate_licenca().unwrap_err().to_string();
9667        assert!(
9668            rendered.contains(":licenca"),
9669            "diagnostic must name the offending slot: {rendered}",
9670        );
9671        assert!(
9672            rendered.contains("Apache_2.0"),
9673            "diagnostic must quote the offending value: {rendered}",
9674        );
9675    }
9676
9677    #[test]
9678    fn validate_licenca_rejects_empty_some() {
9679        // Canonical paste-from-blank-doc footgun. Without this gate
9680        // the empty `Some("")` silently passed the renderer's
9681        // `Option::unwrap_or_else(|| "MIT".into())` (which only
9682        // fires on `None`) and landed as a bare trailing period in
9683        // the rendered chart `README.md` `## License` section.
9684        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
9685        // arm on the sibling `Option<String>` Caixa slot.
9686        let c = caixa_with_licenca(Some(""));
9687        let err = c.validate_licenca().unwrap_err();
9688        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
9689    }
9690
9691    #[test]
9692    fn validate_licenca_template_passes() {
9693        // Round-trip pin: the bare `Caixa::template` shape (whether
9694        // it carries `:licenca` or omits it) passes the gate by
9695        // construction. A future template-shape change that
9696        // introduced `(:licenca "")` would surface here as a
9697        // regression. Mirrors the peer
9698        // `validate_descricao_template_passes` pin.
9699        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9700        c.validate_licenca().unwrap();
9701    }
9702
9703    #[test]
9704    fn validate_licenca_diagnostic_names_offending_slot() {
9705        // Diagnostic-shape pin (peer with
9706        // `validate_descricao_diagnostic_names_offending_slot`):
9707        // the error's Display surfaces the `:licenca` slot name
9708        // verbatim, so a `feira lint` run can render the diagnostic
9709        // without re-parsing and the author can grep their caixa.lisp
9710        // for the offending `:licenca` line.
9711        let c = caixa_with_licenca(Some(""));
9712        let rendered = c.validate_licenca().unwrap_err().to_string();
9713        assert!(
9714            rendered.contains(":licenca"),
9715            "diagnostic must name the offending slot: {rendered}",
9716        );
9717    }
9718
9719    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
9720
9721    #[test]
9722    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
9723        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
9724        // pin: [`Caixa::licenca`] must return the `:licenca` typed
9725        // byte-string verbatim as an `Option<&str>`, byte-equal to the
9726        // raw `self.licenca.as_deref()` access across every
9727        // representative value in the accept-set — `None` (the "omit
9728        // the slot to defer to the caixa-helm renderer's `MIT`
9729        // fallback" arm every existing fixture without a `:licenca`
9730        // line carries), `Some("")` (a past-the-guard sentinel that
9731        // pins the accessor doesn't perform a silent
9732        // `Some("") → None` collapse on the empty arm — validate
9733        // rejects `Some("")` through `LicencaEmpty` but the accessor
9734        // must ship the raw slot verbatim so a validate-time gate
9735        // regression surfaces at the caixa-helm emit boundary rather
9736        // than being silently absorbed into the fallback), `Some("MIT")`
9737        // (the canonical single-license shape every `feira init`
9738        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
9739        // canonical `OR`-compound shape the peer
9740        // `validate_licenca_accepts_canonical_expressions` positive
9741        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
9742        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
9743        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
9744        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
9745        // guard sentinels — validate rejects each through
9746        // `LicencaInvalid` but the accessor must ship the raw slot
9747        // verbatim).
9748        //
9749        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
9750        // accessor pin on the substrate primitive — opens the "outer
9751        // [`Caixa`] `Option<&str>` scalar" projection pattern the
9752        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
9753        // future lifts fold on. Sibling in shape to the peer per-`:placement`
9754        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
9755        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
9756        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
9757        // axes, extended onto the outer top-level [`Caixa`] universal-
9758        // axis surface. Pins against a future silent detour that
9759        // returned an owned `Option<String>` (which would type-check
9760        // but silently allocate on every accessor call, breaking the
9761        // zero-cost projection every peer sibling accessor carries), a
9762        // `Some("") → None` collapse (which would silently absorb the
9763        // `LicencaEmpty` refusal case at the accessor boundary and the
9764        // caixa-helm emit path would silently fall back to `"MIT"` on
9765        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
9766        // `None → Some("MIT")` collapse (which would silently reify
9767        // the caixa-helm renderer's `"MIT"` fallback at the accessor
9768        // boundary and every downstream consumer keying off the
9769        // `Option::is_none()` discriminator would lose the "author
9770        // omitted the slot" signal).
9771        for licenca in [
9772            None,
9773            Some(""),
9774            Some("MIT"),
9775            Some("Apache-2.0 OR MIT"),
9776            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
9777            Some("MIT "),
9778            Some(" MIT"),
9779            Some("MIT\n"),
9780            Some("Apache_2.0"),
9781            Some("MIT,Apache-2.0"),
9782        ] {
9783            let c = caixa_with_licenca(licenca);
9784            assert_eq!(
9785                c.licenca(),
9786                licenca,
9787                "Caixa::licenca must return :licenca verbatim (got {:?}, \
9788                 expected {licenca:?})",
9789                c.licenca(),
9790            );
9791            assert_eq!(
9792                c.licenca(),
9793                c.licenca.as_deref(),
9794                "Caixa::licenca must byte-equal the raw \
9795                 `self.licenca.as_deref()` field access across every \
9796                 value in the Option<&str> accept-set",
9797            );
9798        }
9799    }
9800
9801    #[test]
9802    fn validate_licenca_empty_arm_routes_through_accessor() {
9803        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
9804        // must key off [`Caixa::licenca`], not the raw
9805        // `self.licenca.as_deref()` field access. Structurally: a
9806        // `Caixa { licenca: Some(""), .. }` must surface the
9807        // `LicencaEmpty` refusal exactly, and a
9808        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
9809        // single-license form) must pass validate. The pair jointly
9810        // pins the accessor + validate-gate composition: any future
9811        // silent detour that had the accessor return `None` on the
9812        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
9813        // silently absorb the `LicencaEmpty` refusal at the accessor
9814        // boundary and the validate gate would accept a struct-literal
9815        // `Caixa { licenca: Some(""), .. }` — the composition pin
9816        // catches that at caixa-core build time.
9817        //
9818        // Peer of the per-`:politicas :circuit-breaker`
9819        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
9820        // accessor-composition pin
9821        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
9822        // on the sibling per-M3-mesh-slot required-`u32` axis — same
9823        // "the validate / shape-gate predicate must route through the
9824        // substrate-primitive typed dispatch" discipline extended onto
9825        // the outer top-level [`Caixa`] universal-axis
9826        // `Option<&str>`-composition surface.
9827        let c = caixa_with_licenca(Some(""));
9828        assert!(
9829            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
9830            "validate_licenca must reject licenca == Some(\"\") with \
9831             LicencaEmpty — the accessor and the validate gate must \
9832             route through the same substrate-primitive typed dispatch \
9833             on the :licenca empty arm",
9834        );
9835        let c = caixa_with_licenca(Some("MIT"));
9836        assert!(
9837            c.validate_licenca().is_ok(),
9838            "validate_licenca must accept licenca == Some(\"MIT\") \
9839             (the canonical single-license SPDX shape)",
9840        );
9841    }
9842
9843    #[test]
9844    fn licenca_projects_option_str_by_borrow() {
9845        // The by-borrow pin: [`Caixa::licenca`] returns
9846        // `Option<&str>` by borrow — the `&str` borrows the underlying
9847        // `String` storage of the `Option<String>` slot and the
9848        // accessor must not allocate a fresh `String` on every call.
9849        // Peer of the per-`:placement`
9850        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
9851        // borrow pin on the peer per-M3-mesh-slot
9852        // `Option<&str>`-return axis, extended onto the outer top-
9853        // level [`Caixa`] universal-axis `Option<&str>` shape — the
9854        // accessor's returned `&str` must borrow from `&self` (the
9855        // returned reference's lifetime is tied to `&self`), and
9856        // calling the accessor twice on the same [`Caixa`] must yield
9857        // the same `Option<&str>` verbatim (idempotent, no side
9858        // effects on `&self`).
9859        //
9860        // Pins against a future silent detour that returned an owned
9861        // `Option<String>` (which would type-check but silently
9862        // allocate on every call, breaking the zero-cost projection
9863        // every peer sibling accessor carries), or a one-arm-only
9864        // accessor that returned a saturating value on some sentinel
9865        // input (breaking the pass-through invariant the sibling
9866        // required-scalar accessors carry).
9867        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
9868            let c = caixa_with_licenca(licenca);
9869            let first = c.licenca();
9870            let second = c.licenca();
9871            assert_eq!(
9872                first, second,
9873                "Caixa::licenca must be idempotent — two successive \
9874                 calls on the same &self must return the same \
9875                 Option<&str>",
9876            );
9877            assert_eq!(
9878                first, licenca,
9879                "Caixa::licenca must return :licenca verbatim by \
9880                 borrow — got {first:?}, expected {licenca:?}",
9881            );
9882        }
9883    }
9884
9885    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
9886
9887    #[test]
9888    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
9889        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
9890        // pin: [`Caixa::repositorio`] must return the `:repositorio`
9891        // typed byte-string verbatim as an `Option<&str>`, byte-equal
9892        // to the raw `self.repositorio.as_deref()` access across every
9893        // representative value in the accept-set — `None` (the "omit
9894        // the slot to defer to the per-renderer placeholder" arm every
9895        // existing fixture without a `:repositorio` line carries),
9896        // `Some("")` (a past-the-guard sentinel that pins the accessor
9897        // doesn't perform a silent `Some("") → None` collapse on the
9898        // empty arm — validate rejects `Some("")` through
9899        // `RepositorioEmpty` but the accessor must ship the raw slot
9900        // verbatim so a validate-time gate regression surfaces at the
9901        // caixa-helm / caixa-flux emit boundary rather than being
9902        // silently absorbed into the per-renderer fallback),
9903        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
9904        // shorthand every existing manifest fixture across
9905        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
9906        // `Some("https://github.com/pleme-io/checkout")` (the canonical
9907        // `https://` URL the README quickstart uses),
9908        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
9909        // `Some("git://github.com/pleme-io/checkout.git")` /
9910        // `Some("git@github.com:pleme-io/checkout.git")` /
9911        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
9912        // github scheme the shared `is_git_repo_url` predicate
9913        // documents), and five past-the-guard sentinels for the
9914        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
9915        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
9916        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
9917        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
9918        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
9919        // sentinels pin the accessor doesn't silently absorb the
9920        // refusal cases into a fallback).
9921        //
9922        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
9923        // accessor pin on the substrate primitive — sibling of the peer
9924        // [`Caixa::licenca`] (6d5bc28) pin
9925        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
9926        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
9927        // projection pin pattern this pin folds on. Sibling in shape to
9928        // the peer per-`:placement`
9929        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
9930        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
9931        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
9932        // axes, extended onto the outer top-level [`Caixa`] universal-
9933        // axis surface. Pins against a future silent detour that
9934        // returned an owned `Option<String>` (which would type-check
9935        // but silently allocate on every accessor call, breaking the
9936        // zero-cost projection every peer sibling accessor carries), a
9937        // `Some("") → None` collapse (which would silently absorb the
9938        // `RepositorioEmpty` refusal case at the accessor boundary and
9939        // the caixa-helm `Chart.yaml` `home:` fold would silently
9940        // render a `home: null` / omitted field on a struct-literal
9941        // `Caixa { repositorio: Some(""), .. }`), or a
9942        // `None → Some(<default>)` collapse (which would silently reify
9943        // the per-renderer fallback at the accessor boundary and every
9944        // downstream consumer keying off the `Option::is_none()`
9945        // discriminator would lose the "author omitted the slot"
9946        // signal).
9947        for repositorio in [
9948            None,
9949            Some(""),
9950            Some("github:pleme-io/hello-rio"),
9951            Some("https://github.com/pleme-io/checkout"),
9952            Some("ssh://git@github.com/pleme-io/checkout.git"),
9953            Some("git://github.com/pleme-io/checkout.git"),
9954            Some("git@github.com:pleme-io/checkout.git"),
9955            Some("file:///opt/mirrors/pleme-io/checkout"),
9956            Some("pleme-io/checkout"),
9957            Some("-upload-pack=evil"),
9958            Some("github:pleme-io/checkout?ref=main"),
9959            Some("github:pleme-io/checkout#main"),
9960            Some("github:pleme-io/{tpl}"),
9961        ] {
9962            let c = caixa_with_repositorio(repositorio);
9963            assert_eq!(
9964                c.repositorio(),
9965                repositorio,
9966                "Caixa::repositorio must return :repositorio verbatim \
9967                 (got {:?}, expected {repositorio:?})",
9968                c.repositorio(),
9969            );
9970            assert_eq!(
9971                c.repositorio(),
9972                c.repositorio.as_deref(),
9973                "Caixa::repositorio must byte-equal the raw \
9974                 `self.repositorio.as_deref()` field access across every \
9975                 value in the Option<&str> accept-set",
9976            );
9977        }
9978    }
9979
9980    #[test]
9981    fn validate_repositorio_empty_arm_routes_through_accessor() {
9982        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
9983        // gate must key off [`Caixa::repositorio`], not the raw
9984        // `self.repositorio.as_deref()` field access. Structurally: a
9985        // `Caixa { repositorio: Some(""), .. }` must surface the
9986        // `RepositorioEmpty` refusal exactly, and a
9987        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
9988        // (the canonical `github:` shorthand form) must pass validate.
9989        // The pair jointly pins the accessor + validate-gate
9990        // composition: any future silent detour that had the accessor
9991        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
9992        // collapse) would silently absorb the `RepositorioEmpty` refusal
9993        // at the accessor boundary and the validate gate would accept a
9994        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
9995        // composition pin catches that at caixa-core build time.
9996        //
9997        // Peer of the [`Caixa::licenca`] (6d5bc28)
9998        // `validate_licenca_empty_arm_routes_through_accessor`
9999        // composition pin on the sibling outer top-level [`Caixa`]
10000        // `Option<&str>` universal-axis surface — same "the validate /
10001        // shape-gate predicate must route through the substrate-
10002        // primitive typed dispatch" discipline extended onto the second
10003        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10004        // composition surface.
10005        let c = caixa_with_repositorio(Some(""));
10006        assert!(
10007            matches!(
10008                c.validate_repositorio(),
10009                Err(ManifestError::RepositorioEmpty),
10010            ),
10011            "validate_repositorio must reject repositorio == Some(\"\") \
10012             with RepositorioEmpty — the accessor and the validate gate \
10013             must route through the same substrate-primitive typed \
10014             dispatch on the :repositorio empty arm",
10015        );
10016        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
10017        assert!(
10018            c.validate_repositorio().is_ok(),
10019            "validate_repositorio must accept repositorio == \
10020             Some(\"github:pleme-io/hello-rio\") (the canonical \
10021             `github:` shorthand git-repo-URL shape)",
10022        );
10023    }
10024
10025    #[test]
10026    fn repositorio_projects_option_str_by_borrow() {
10027        // The by-borrow pin: [`Caixa::repositorio`] returns
10028        // `Option<&str>` by borrow — the `&str` borrows the underlying
10029        // `String` storage of the `Option<String>` slot and the
10030        // accessor must not allocate a fresh `String` on every call.
10031        // Peer of the per-`:placement`
10032        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
10033        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
10034        // `Option<&str>`-return axes, extended onto the second outer
10035        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
10036        // the accessor's returned `&str` must borrow from `&self` (the
10037        // returned reference's lifetime is tied to `&self`), and
10038        // calling the accessor twice on the same [`Caixa`] must yield
10039        // the same `Option<&str>` verbatim (idempotent, no side effects
10040        // on `&self`).
10041        //
10042        // Pins against a future silent detour that returned an owned
10043        // `Option<String>` (which would type-check but silently
10044        // allocate on every call, breaking the zero-cost projection
10045        // every peer sibling accessor carries), or a one-arm-only
10046        // accessor that returned a saturating value on some sentinel
10047        // input (breaking the pass-through invariant the sibling
10048        // required-scalar accessors carry).
10049        for repositorio in [
10050            None,
10051            Some(""),
10052            Some("github:pleme-io/hello-rio"),
10053            Some("https://github.com/pleme-io/checkout"),
10054        ] {
10055            let c = caixa_with_repositorio(repositorio);
10056            let first = c.repositorio();
10057            let second = c.repositorio();
10058            assert_eq!(
10059                first, second,
10060                "Caixa::repositorio must be idempotent — two successive \
10061                 calls on the same &self must return the same \
10062                 Option<&str>",
10063            );
10064            assert_eq!(
10065                first, repositorio,
10066                "Caixa::repositorio must return :repositorio verbatim by \
10067                 borrow — got {first:?}, expected {repositorio:?}",
10068            );
10069        }
10070    }
10071
10072    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
10073
10074    #[test]
10075    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
10076        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
10077        // pin: [`Caixa::descricao`] must return the `:descricao` typed
10078        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10079        // raw `self.descricao.as_deref()` access across every
10080        // representative value in the accept-set — `None` (the "omit
10081        // the slot to defer to the per-renderer `caixa.nome`-derived
10082        // fallback" arm every existing fixture without a `:descricao`
10083        // line carries), `Some("")` (a past-the-guard sentinel that
10084        // pins the accessor doesn't perform a silent `Some("") → None`
10085        // collapse on the empty arm — validate rejects `Some("")`
10086        // through `DescricaoEmpty` but the accessor must ship the raw
10087        // slot verbatim so a validate-time gate regression surfaces at
10088        // the caixa-helm / caixa-feira emit boundary rather than being
10089        // silently absorbed into the per-renderer `caixa.nome`-derived
10090        // fallback), `Some("Checkout flow.")` (the canonical one-line
10091        // prose descriptor the peer
10092        // `validate_descricao_accepts_canonical_value` positive sweep
10093        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
10094        // Servico.")` (the multi-byte Unicode continuation-byte shape
10095        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
10096        // multi-glyph Unicode shape the peer
10097        // `is_chart_description_shape` predicate accepts), and five
10098        // past-the-guard sentinels for the `DescricaoInvalid` refusal
10099        // cases (`Some(" Checkout flow.")` leading-whitespace,
10100        // `Some("Checkout flow. ")` trailing-whitespace,
10101        // `Some("Checkout\nflow.")` embedded-LF,
10102        // `Some("Checkout\tflow.")` embedded-TAB, and
10103        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
10104        // the accessor doesn't silently absorb the refusal cases into
10105        // a fallback).
10106        //
10107        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
10108        // accessor pin on the substrate primitive — sibling of the peer
10109        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
10110        // (cc7332d) pins that opened the "outer [`Caixa`]
10111        // `Option<&str>` scalar" projection pin pattern this pin folds
10112        // on. Sibling in shape to the peer per-`:placement`
10113        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10114        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10115        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10116        // axes, extended onto the outer top-level [`Caixa`] universal-
10117        // axis surface. Pins against a future silent detour that
10118        // returned an owned `Option<String>` (which would type-check
10119        // but silently allocate on every accessor call, breaking the
10120        // zero-cost projection every peer sibling accessor carries), a
10121        // `Some("") → None` collapse (which would silently absorb the
10122        // `DescricaoEmpty` refusal case at the accessor boundary and
10123        // the caixa-helm `Chart.yaml` `description:` fold would
10124        // silently render a `caixa.nome`-derived fallback on a
10125        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
10126        // `None → Some(<default>)` collapse (which would silently
10127        // reify the per-renderer `caixa.nome`-derived fallback at the
10128        // accessor boundary and every downstream consumer keying off
10129        // the `Option::is_none()` discriminator would lose the "author
10130        // omitted the slot" signal).
10131        for descricao in [
10132            None,
10133            Some(""),
10134            Some("Checkout flow."),
10135            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
10136            Some("→ — · ✓"),
10137            Some(" Checkout flow."),
10138            Some("Checkout flow. "),
10139            Some("Checkout\nflow."),
10140            Some("Checkout\tflow."),
10141            Some("Checkout\x00flow."),
10142        ] {
10143            let c = caixa_with_descricao(descricao);
10144            assert_eq!(
10145                c.descricao(),
10146                descricao,
10147                "Caixa::descricao must return :descricao verbatim (got \
10148                 {:?}, expected {descricao:?})",
10149                c.descricao(),
10150            );
10151            assert_eq!(
10152                c.descricao(),
10153                c.descricao.as_deref(),
10154                "Caixa::descricao must byte-equal the raw \
10155                 `self.descricao.as_deref()` field access across every \
10156                 value in the Option<&str> accept-set",
10157            );
10158        }
10159    }
10160
10161    #[test]
10162    fn validate_descricao_empty_arm_routes_through_accessor() {
10163        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
10164        // gate must key off [`Caixa::descricao`], not the raw
10165        // `self.descricao.as_deref()` field access. Structurally: a
10166        // `Caixa { descricao: Some(""), .. }` must surface the
10167        // `DescricaoEmpty` refusal exactly, and a
10168        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
10169        // canonical one-line-prose form) must pass validate. The pair
10170        // jointly pins the accessor + validate-gate composition: any
10171        // future silent detour that had the accessor return `None` on
10172        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10173        // silently absorb the `DescricaoEmpty` refusal at the accessor
10174        // boundary and the validate gate would accept a struct-literal
10175        // `Caixa { descricao: Some(""), .. }` — the composition pin
10176        // catches that at caixa-core build time.
10177        //
10178        // Peer of the [`Caixa::licenca`] (6d5bc28)
10179        // `validate_licenca_empty_arm_routes_through_accessor` and
10180        // [`Caixa::repositorio`] (cc7332d)
10181        // `validate_repositorio_empty_arm_routes_through_accessor`
10182        // composition pins on the sibling outer top-level [`Caixa`]
10183        // `Option<&str>` universal-axis surface — same "the validate /
10184        // shape-gate predicate must route through the substrate-
10185        // primitive typed dispatch" discipline extended onto the third
10186        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10187        // composition surface.
10188        let c = caixa_with_descricao(Some(""));
10189        assert!(
10190            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
10191            "validate_descricao must reject descricao == Some(\"\") \
10192             with DescricaoEmpty — the accessor and the validate gate \
10193             must route through the same substrate-primitive typed \
10194             dispatch on the :descricao empty arm",
10195        );
10196        let c = caixa_with_descricao(Some("Checkout flow."));
10197        assert!(
10198            c.validate_descricao().is_ok(),
10199            "validate_descricao must accept descricao == \
10200             Some(\"Checkout flow.\") (the canonical one-line-prose \
10201             chart-description shape)",
10202        );
10203    }
10204
10205    #[test]
10206    fn descricao_projects_option_str_by_borrow() {
10207        // The by-borrow pin: [`Caixa::descricao`] returns
10208        // `Option<&str>` by borrow — the `&str` borrows the underlying
10209        // `String` storage of the `Option<String>` slot and the
10210        // accessor must not allocate a fresh `String` on every call.
10211        // Peer of the [`Caixa::licenca`] (6d5bc28) and
10212        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
10213        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
10214        // the per-`:placement`
10215        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10216        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
10217        // return axis, extended onto the third outer top-level
10218        // [`Caixa`] universal-axis `Option<&str>` shape — the
10219        // accessor's returned `&str` must borrow from `&self` (the
10220        // returned reference's lifetime is tied to `&self`), and
10221        // calling the accessor twice on the same [`Caixa`] must yield
10222        // the same `Option<&str>` verbatim (idempotent, no side
10223        // effects on `&self`).
10224        //
10225        // Pins against a future silent detour that returned an owned
10226        // `Option<String>` (which would type-check but silently
10227        // allocate on every call, breaking the zero-cost projection
10228        // every peer sibling accessor carries), or a one-arm-only
10229        // accessor that returned a saturating value on some sentinel
10230        // input (breaking the pass-through invariant the sibling
10231        // required-scalar accessors carry).
10232        for descricao in [
10233            None,
10234            Some(""),
10235            Some("Checkout flow."),
10236            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
10237        ] {
10238            let c = caixa_with_descricao(descricao);
10239            let first = c.descricao();
10240            let second = c.descricao();
10241            assert_eq!(
10242                first, second,
10243                "Caixa::descricao must be idempotent — two successive \
10244                 calls on the same &self must return the same \
10245                 Option<&str>",
10246            );
10247            assert_eq!(
10248                first, descricao,
10249                "Caixa::descricao must return :descricao verbatim by \
10250                 borrow — got {first:?}, expected {descricao:?}",
10251            );
10252        }
10253    }
10254
10255    // ── validate_edicao — universal-axis language-edition shape ──
10256
10257    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
10258        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10259        c.edicao = edicao.map(String::from);
10260        c
10261    }
10262
10263    #[test]
10264    fn validate_edicao_accepts_none() {
10265        // The omit-the-slot identity: `:edicao` is optional. The
10266        // gate is a no-op when the author didn't declare a value —
10267        // every caixa without an `:edicao` line trivially passes,
10268        // and the substrate-side build pipeline falls back to the
10269        // documented default edition. Mirrors the peer
10270        // `validate_licenca_accepts_none` posture on the sibling
10271        // `Option<String>` Caixa slot.
10272        let c = caixa_with_edicao(None);
10273        c.validate_edicao().unwrap();
10274    }
10275
10276    #[test]
10277    fn validate_edicao_accepts_canonical_value() {
10278        // Positive control: the canonical `"2026"` edition every
10279        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
10280        // `caixa-mesh`) carries by construction passes the gate.
10281        // Future-introduced sibling editions (`"2027"`, `"2030"`,
10282        // `"2049"`) that match the same 4-digit ASCII decimal year
10283        // shape must also trivially pass — the structural shape
10284        // predicate accepts every well-formed year regardless of
10285        // whether the substrate yet understands the specific value
10286        // (a future known-edition allowlist tightens that).
10287        for ed in ["2026", "2027", "2030", "2049"] {
10288            let c = caixa_with_edicao(Some(ed));
10289            c.validate_edicao()
10290                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
10291        }
10292    }
10293
10294    #[test]
10295    fn validate_edicao_rejects_empty_some() {
10296        // Canonical paste-from-blank-doc footgun. Without this gate
10297        // the empty `Some("")` silently lands as `(:edicao "")` in
10298        // the rendered caixa.lisp and a future renderer-side
10299        // consumer's `Option::unwrap_or_else` (which only fires on
10300        // `None`) skips its fallback. Mirrors the peer
10301        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
10302        // `Option<String>` Caixa slot.
10303        let c = caixa_with_edicao(Some(""));
10304        let err = c.validate_edicao().unwrap_err();
10305        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
10306    }
10307
10308    #[test]
10309    fn validate_edicao_rejects_free_form_non_year() {
10310        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
10311        // `"nightly"` shapes carry no operational meaning on the
10312        // substrate's build-time edition selector. Until this gate
10313        // landed the bare empty-arm check let every such value
10314        // through and broke far from the source caixa.lisp. Peer
10315        // with the shape-predicate cascade
10316        // `validate_repositorio_rejects_missing_colon_separator`
10317        // establishes past its own empty arm.
10318        for ed in ["x", "latest", "nightly", "stable"] {
10319            let c = caixa_with_edicao(Some(ed));
10320            let err = c.validate_edicao().unwrap_err();
10321            assert!(
10322                matches!(err, ManifestError::EdicaoInvalid { .. }),
10323                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10324            );
10325        }
10326    }
10327
10328    #[test]
10329    fn validate_edicao_rejects_trailing_whitespace() {
10330        // Paste-from-doc whitespace footgun. A trailing space in
10331        // the `:edicao` value would silently break the substrate's
10332        // build-time edition match-table lookup at the rendered
10333        // artifact's edition-selector consumer. The shape predicate
10334        // refuses every whitespace byte by construction (any byte
10335        // outside `0-9` fails `is_ascii_digit`). Peer with
10336        // `validate_repositorio_rejects_whitespace`.
10337        let c = caixa_with_edicao(Some("2026 "));
10338        let err = c.validate_edicao().unwrap_err();
10339        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
10340            panic!("expected EdicaoInvalid, got {err:?}");
10341        };
10342        assert_eq!(edicao, "2026 ");
10343    }
10344
10345    #[test]
10346    fn validate_edicao_rejects_leading_whitespace() {
10347        // Symmetric paste-from-doc whitespace footgun on the leading
10348        // boundary — the gate refuses every shape with a non-digit
10349        // byte by construction.
10350        let c = caixa_with_edicao(Some(" 2026"));
10351        let err = c.validate_edicao().unwrap_err();
10352        assert!(
10353            matches!(err, ManifestError::EdicaoInvalid { .. }),
10354            "got {err:?}",
10355        );
10356    }
10357
10358    #[test]
10359    fn validate_edicao_rejects_control_char() {
10360        // Paste-from-multiline-doc CRLF footgun — control characters
10361        // at the value boundary break the substrate's build-time
10362        // edition-selector parser. Peer with
10363        // `validate_repositorio_rejects_control_char`.
10364        let c = caixa_with_edicao(Some("2026\n"));
10365        let err = c.validate_edicao().unwrap_err();
10366        assert!(
10367            matches!(err, ManifestError::EdicaoInvalid { .. }),
10368            "got {err:?}",
10369        );
10370    }
10371
10372    #[test]
10373    fn validate_edicao_rejects_non_ascii_lookalike() {
10374        // Fullwidth-keyboard look-alike footgun — `"2026"` is
10375        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
10376        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
10377        // edition selector wants an ASCII year, and the gate
10378        // refuses every non-ASCII shape by construction (length in
10379        // bytes is 12 ≠ 4, *and* every byte falls outside
10380        // `is_ascii_digit`'s `0-9` range).
10381        let c = caixa_with_edicao(Some("2026"));
10382        let err = c.validate_edicao().unwrap_err();
10383        assert!(
10384            matches!(err, ManifestError::EdicaoInvalid { .. }),
10385            "got {err:?}",
10386        );
10387    }
10388
10389    #[test]
10390    fn validate_edicao_rejects_version_tag_prefix() {
10391        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
10392        // / `"r2026"` are familiar shapes from git-tag / Rust
10393        // edition / release-tag conventions that don't apply to
10394        // the year-shaped edition axis. The shape predicate refuses
10395        // every leading non-digit prefix.
10396        for ed in ["v2026", "e2026", "r2026"] {
10397            let c = caixa_with_edicao(Some(ed));
10398            let err = c.validate_edicao().unwrap_err();
10399            assert!(
10400                matches!(err, ManifestError::EdicaoInvalid { .. }),
10401                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10402            );
10403        }
10404    }
10405
10406    #[test]
10407    fn validate_edicao_rejects_decimal_shape() {
10408        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
10409        // `"2026.0"` are familiar shapes from semver / float
10410        // conventions that don't apply to the year-shaped edition
10411        // axis. The shape predicate refuses every non-digit byte
10412        // (`.` falls outside `is_ascii_digit`).
10413        for ed in ["2026.1", "2026.0", "2026.0.1"] {
10414            let c = caixa_with_edicao(Some(ed));
10415            let err = c.validate_edicao().unwrap_err();
10416            assert!(
10417                matches!(err, ManifestError::EdicaoInvalid { .. }),
10418                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10419            );
10420        }
10421    }
10422
10423    #[test]
10424    fn validate_edicao_rejects_wrong_length_numeric() {
10425        // Wrong-length numeric footgun — `"26"` (truncated) /
10426        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
10427        // (zero-padded too wide) all parse as integers but don't
10428        // name a 4-digit year. The shape predicate refuses every
10429        // value whose length isn't exactly 4 bytes.
10430        for ed in ["26", "202", "20260", "00026", "9"] {
10431            let c = caixa_with_edicao(Some(ed));
10432            let err = c.validate_edicao().unwrap_err();
10433            assert!(
10434                matches!(err, ManifestError::EdicaoInvalid { .. }),
10435                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10436            );
10437        }
10438    }
10439
10440    #[test]
10441    fn validate_edicao_empty_takes_precedence_over_shape() {
10442        // Empty-first cascade pin: the empty `Some("")` surfaces
10443        // the narrower `EdicaoEmpty` not the shape-predicate-
10444        // wrapped `EdicaoInvalid`, mirroring the peer
10445        // `validate_repositorio_empty_takes_precedence_over_shape`
10446        // (`RepositorioEmpty` → `RepositorioInvalid`),
10447        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
10448        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
10449        // cascades. The shape predicate also refuses the empty
10450        // input (defensively — `s.len() != 4`), but the
10451        // manifest-layer empty arm runs first to surface the
10452        // narrower diagnostic verbatim.
10453        let c = caixa_with_edicao(Some(""));
10454        let err = c.validate_edicao().unwrap_err();
10455        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
10456    }
10457
10458    #[test]
10459    fn validate_edicao_template_passes() {
10460        // Round-trip pin: the bare `Caixa::template` shape (which
10461        // carries `:edicao "2026"` verbatim) passes the gate by
10462        // construction. A future template-shape change that
10463        // introduced `(:edicao "")` or a non-year value would
10464        // surface here as a regression. Mirrors the peer
10465        // `validate_licenca_template_passes` pin.
10466        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10467        c.validate_edicao().unwrap();
10468    }
10469
10470    #[test]
10471    fn validate_edicao_diagnostic_names_offending_slot() {
10472        // Diagnostic-shape pin (peer with
10473        // `validate_licenca_diagnostic_names_offending_slot`): the
10474        // error's Display surfaces the `:edicao` slot name verbatim,
10475        // so a `feira lint` run can render the diagnostic without
10476        // re-parsing and the author can grep their caixa.lisp for
10477        // the offending `:edicao` line.
10478        let c = caixa_with_edicao(Some(""));
10479        let rendered = c.validate_edicao().unwrap_err().to_string();
10480        assert!(
10481            rendered.contains(":edicao"),
10482            "diagnostic must name the offending slot: {rendered}",
10483        );
10484    }
10485
10486    #[test]
10487    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
10488        // Diagnostic-shape pin on the shape-predicate arm (peer
10489        // with `validate_repositorio_diagnostic_carries_offending_value`):
10490        // the error's Display surfaces the offending value + slot
10491        // name verbatim, so a `feira lint` run can render the
10492        // diagnostic without re-parsing and the author can grep
10493        // their caixa.lisp for the offending `:edicao` value.
10494        let c = caixa_with_edicao(Some("v2026"));
10495        let rendered = c.validate_edicao().unwrap_err().to_string();
10496        assert!(
10497            rendered.contains(":edicao"),
10498            "diagnostic must name the offending slot: {rendered}",
10499        );
10500        assert!(
10501            rendered.contains("v2026"),
10502            "diagnostic must quote the offending value: {rendered}",
10503        );
10504    }
10505
10506    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
10507
10508    #[test]
10509    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
10510        // The canonical per-`Caixa` `:edicao` language-edition scalar
10511        // pin: [`Caixa::edicao`] must return the `:edicao` typed
10512        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10513        // raw `self.edicao.as_deref()` access across every representative
10514        // value in the accept-set — `None` (the "omit the slot to defer
10515        // to the substrate's default edition" arm every existing
10516        // [`caixa-resolver`] fixture without an `:edicao` line carries),
10517        // `Some("")` (a past-the-guard sentinel that pins the accessor
10518        // doesn't perform a silent `Some("") → None` collapse on the
10519        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
10520        // but the accessor must ship the raw slot verbatim so a
10521        // validate-time gate regression surfaces at any future edition-
10522        // aware consumer's boundary rather than being silently absorbed
10523        // into the substrate's default edition), `Some("2026")` (the
10524        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
10525        // template scaffolds via [`Caixa::template`] and every
10526        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
10527        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
10528        // carries by construction), `Some("2018")` / `Some("2021")` /
10529        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
10530        // peer with Cargo's `[package] edition` grammar every future-
10531        // introduced sibling to `"2026"` will follow), and eight
10532        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
10533        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
10534        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
10535        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
10536        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
10537        // length-numeric, `Some("latest")` free-form-non-year — the
10538        // sentinels pin the accessor doesn't silently absorb the
10539        // refusal cases into a substrate-default-edition fallback).
10540        //
10541        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
10542        // return scalar accessor pin on the substrate primitive —
10543        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
10544        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
10545        // (3f16e2f) pins that opened the "outer [`Caixa`]
10546        // `Option<&str>` scalar" projection pin pattern this pin folds
10547        // on. Sibling in shape to the peer per-`:placement`
10548        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10549        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10550        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10551        // axes, extended onto the outer top-level [`Caixa`] universal-
10552        // axis surface's last unlifted `Option<String>` slot. Pins
10553        // against a future silent detour that returned an owned
10554        // `Option<String>` (which would type-check but silently
10555        // allocate on every accessor call, breaking the zero-cost
10556        // projection every peer sibling accessor carries), a
10557        // `Some("") → None` collapse (which would silently absorb the
10558        // `EdicaoEmpty` refusal case at the accessor boundary and any
10559        // future edition-aware consumer would silently fall back to
10560        // the substrate's default edition on a struct-literal
10561        // `Caixa { edicao: Some(""), .. }`), or a
10562        // `None → Some("2026")` collapse (which would silently reify
10563        // the substrate's default edition at the accessor boundary
10564        // and every downstream consumer keying off the
10565        // `Option::is_none()` discriminator would lose the "author
10566        // omitted the slot" signal).
10567        for edicao in [
10568            None,
10569            Some(""),
10570            Some("2026"),
10571            Some("2018"),
10572            Some("2021"),
10573            Some("2024"),
10574            Some("2026 "),
10575            Some(" 2026"),
10576            Some("2026\n"),
10577            Some("2026"),
10578            Some("v2026"),
10579            Some("2026.1"),
10580            Some("26"),
10581            Some("latest"),
10582        ] {
10583            let c = caixa_with_edicao(edicao);
10584            assert_eq!(
10585                c.edicao(),
10586                edicao,
10587                "Caixa::edicao must return :edicao verbatim (got {:?}, \
10588                 expected {edicao:?})",
10589                c.edicao(),
10590            );
10591            assert_eq!(
10592                c.edicao(),
10593                c.edicao.as_deref(),
10594                "Caixa::edicao must byte-equal the raw \
10595                 `self.edicao.as_deref()` field access across every \
10596                 value in the Option<&str> accept-set",
10597            );
10598        }
10599    }
10600
10601    #[test]
10602    fn validate_edicao_empty_arm_routes_through_accessor() {
10603        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
10604        // must key off [`Caixa::edicao`], not the raw
10605        // `self.edicao.as_deref()` field access. Structurally: a
10606        // `Caixa { edicao: Some(""), .. }` must surface the
10607        // `EdicaoEmpty` refusal exactly, and a
10608        // `Caixa { edicao: Some("2026"), .. }` (the canonical
10609        // 4-digit-ASCII-decimal-year form) must pass validate. The
10610        // pair jointly pins the accessor + validate-gate composition:
10611        // any future silent detour that had the accessor return `None`
10612        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
10613        // would silently absorb the `EdicaoEmpty` refusal at the
10614        // accessor boundary and the validate gate would accept a
10615        // struct-literal `Caixa { edicao: Some(""), .. }` — the
10616        // composition pin catches that at caixa-core build time.
10617        //
10618        // Peer of the [`Caixa::licenca`] (6d5bc28)
10619        // `validate_licenca_empty_arm_routes_through_accessor`,
10620        // [`Caixa::repositorio`] (cc7332d)
10621        // `validate_repositorio_empty_arm_routes_through_accessor`,
10622        // and [`Caixa::descricao`] (3f16e2f)
10623        // `validate_descricao_empty_arm_routes_through_accessor`
10624        // composition pins on the sibling outer top-level [`Caixa`]
10625        // `Option<&str>` universal-axis surface — same "the validate /
10626        // shape-gate predicate must route through the substrate-
10627        // primitive typed dispatch" discipline extended onto the
10628        // fourth and final outer top-level [`Caixa`] universal-axis
10629        // `Option<&str>`-composition surface, closing the accessor-
10630        // composition family.
10631        let c = caixa_with_edicao(Some(""));
10632        assert!(
10633            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
10634            "validate_edicao must reject edicao == Some(\"\") with \
10635             EdicaoEmpty — the accessor and the validate gate must \
10636             route through the same substrate-primitive typed dispatch \
10637             on the :edicao empty arm",
10638        );
10639        let c = caixa_with_edicao(Some("2026"));
10640        assert!(
10641            c.validate_edicao().is_ok(),
10642            "validate_edicao must accept edicao == Some(\"2026\") \
10643             (the canonical 4-digit-ASCII-decimal-year shape)",
10644        );
10645    }
10646
10647    #[test]
10648    fn edicao_projects_option_str_by_borrow() {
10649        // The by-borrow pin: [`Caixa::edicao`] returns
10650        // `Option<&str>` by borrow — the `&str` borrows the underlying
10651        // `String` storage of the `Option<String>` slot and the
10652        // accessor must not allocate a fresh `String` on every call.
10653        // Peer of the [`Caixa::licenca`] (6d5bc28),
10654        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
10655        // (3f16e2f) by-borrow pins on the peer outer top-level
10656        // [`Caixa`] `Option<&str>`-return axes, and of the
10657        // per-`:placement`
10658        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10659        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
10660        // return axis, extended onto the fourth and final outer top-
10661        // level [`Caixa`] universal-axis `Option<&str>` shape — the
10662        // accessor's returned `&str` must borrow from `&self` (the
10663        // returned reference's lifetime is tied to `&self`), and
10664        // calling the accessor twice on the same [`Caixa`] must yield
10665        // the same `Option<&str>` verbatim (idempotent, no side
10666        // effects on `&self`).
10667        //
10668        // Pins against a future silent detour that returned an owned
10669        // `Option<String>` (which would type-check but silently
10670        // allocate on every call, breaking the zero-cost projection
10671        // every peer sibling accessor carries), or a one-arm-only
10672        // accessor that returned a saturating value on some sentinel
10673        // input (breaking the pass-through invariant the sibling
10674        // required-scalar accessors carry).
10675        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
10676            let c = caixa_with_edicao(edicao);
10677            let first = c.edicao();
10678            let second = c.edicao();
10679            assert_eq!(
10680                first, second,
10681                "Caixa::edicao must be idempotent — two successive \
10682                 calls on the same &self must return the same \
10683                 Option<&str>",
10684            );
10685            assert_eq!(
10686                first, edicao,
10687                "Caixa::edicao must return :edicao verbatim by \
10688                 borrow — got {first:?}, expected {edicao:?}",
10689            );
10690        }
10691    }
10692
10693    #[test]
10694    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
10695        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
10696        // label caixa-identity scalar pin: [`Caixa::nome`] must return
10697        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
10698        // the raw field access across every representative value in
10699        // the accept-set — the canonical `"demo"` template baseline
10700        // (the same `feira init`-scaffolded default the sibling
10701        // `validate_nome_accepts_canonical_template` positive-control
10702        // gate pins), plus every sibling per-typed-slot atom accessor's
10703        // canonical positive-arm byte-string (`"catalog"` per
10704        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
10705        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
10706        // `caixa-helm`/`caixa-flux` cross-crate integration-test
10707        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
10708        // canonical example), plus every past-the-guard sentinel for
10709        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
10710        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
10711        // the bare DNS-1123 63-byte cap but overflows the joint
10712        // `lareira-<nome>` chart-name budget the sibling
10713        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
10714        //
10715        // The past-the-guard sentinels pin the accessor doesn't
10716        // silently absorb the refusal cases into a template-derived
10717        // fallback (a future `.nome().is_empty().then(|| "demo")`
10718        // collapse would silently absorb the `NomeEmpty` refusal at
10719        // the accessor boundary and the validate gate would accept a
10720        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
10721        // catches that at caixa-core build time).
10722        //
10723        // First outer top-level [`Caixa`] `&str`-return required-
10724        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
10725        // required-scalar" projection pattern the sibling per-`Caixa`
10726        // `:versao` future lift folds on. Sibling in shape to the peer
10727        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
10728        // required-`String`-carry accessor pin on the sibling per-
10729        // sub-struct required-axis, extended onto the outer top-level
10730        // [`Caixa`] universal-axis required-`String`-carry axis.
10731        for nome in [
10732            "demo",
10733            "catalog",
10734            "cart",
10735            "hello-rio",
10736            "checkout",
10737            "",
10738            "Bad_Name",
10739            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
10740        ] {
10741            let c = caixa_with_nome(nome);
10742            assert_eq!(
10743                c.nome(),
10744                nome,
10745                "Caixa::nome must return :nome verbatim (got {}, \
10746                 expected {nome})",
10747                c.nome(),
10748            );
10749            assert_eq!(
10750                c.nome(),
10751                c.nome.as_str(),
10752                "Caixa::nome must byte-equal the raw .nome field \
10753                 access across every value in the String accept-set",
10754            );
10755        }
10756    }
10757
10758    #[test]
10759    fn validate_nome_empty_arm_routes_through_accessor() {
10760        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
10761        // key off [`Caixa::nome`], not the raw `.nome` field access.
10762        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
10763        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
10764        // template baseline (the peer positive-arm the sibling
10765        // `validate_nome_accepts_canonical_template` gate carves out)
10766        // must pass validate. The pair jointly pins the accessor +
10767        // validate-gate composition: any future silent detour that
10768        // had the accessor return a fresh `"demo"` on the empty arm
10769        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
10770        // would silently absorb the `NomeEmpty` refusal at the
10771        // accessor boundary and the validate gate would accept a
10772        // struct-literal `Caixa { nome: "".into(), .. }` — the
10773        // composition pin catches that at caixa-core build time.
10774        //
10775        // Peer of the sibling per-`Caixa`
10776        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
10777        // / `validate_repositorio_empty_arm_routes_through_accessor`
10778        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
10779        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
10780        // (2641cbd) composition pins on the sibling outer top-level
10781        // [`Caixa`] `Option<&str>` axes — same "the validate /
10782        // shape-gate predicate must route through the substrate-
10783        // primitive typed dispatch" discipline extended onto the peer
10784        // outer top-level [`Caixa`] required-`&str` composition axis.
10785        let c = caixa_with_nome("");
10786        assert!(
10787            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
10788            "validate_nome must reject nome == \"\" with NomeEmpty — \
10789             the accessor and the validate gate must route through the \
10790             same substrate-primitive typed dispatch on the :nome \
10791             empty-arm",
10792        );
10793        let c = caixa_with_nome("demo");
10794        assert!(
10795            c.validate_nome().is_ok(),
10796            "validate_nome must accept nome == \"demo\" (the canonical \
10797             DNS-1123-label template baseline)",
10798        );
10799    }
10800
10801    #[test]
10802    fn nome_projects_str_by_borrow() {
10803        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
10804        // — the `&str` borrows the underlying `String` storage of the
10805        // required `nome` slot and the accessor must not allocate a
10806        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
10807        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
10808        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
10809        // by-borrow pins on the peer outer top-level [`Caixa`]
10810        // `Option<&str>`-return axes, extended onto the first outer
10811        // top-level [`Caixa`] required-`&str`-return axis — the
10812        // accessor's returned `&str` must borrow from `&self` (the
10813        // returned reference's lifetime is tied to `&self`), and
10814        // calling the accessor twice on the same [`Caixa`] must yield
10815        // the same `&str` verbatim (idempotent, no side effects on
10816        // `&self`).
10817        //
10818        // Pins against a future silent detour that returned an owned
10819        // `String` (which would type-check but silently allocate on
10820        // every call, breaking the zero-cost projection every peer
10821        // sibling accessor carries), an accidental
10822        // `.nome.to_lowercase()` detour that returned a fresh
10823        // allocation through an already-DNS-1123-lowercase-only
10824        // string (breaking a future `const fn` regression), or a
10825        // one-arm-only accessor that returned a canonicalized value
10826        // on some sentinel input (breaking the pass-through invariant
10827        // the sibling required-scalar accessors carry).
10828        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
10829            let c = caixa_with_nome(nome);
10830            let first = c.nome();
10831            let second = c.nome();
10832            assert_eq!(
10833                first, second,
10834                "Caixa::nome must be idempotent — two successive calls \
10835                 on the same &self must return the same &str",
10836            );
10837            assert_eq!(
10838                first, nome,
10839                "Caixa::nome must return :nome verbatim by borrow — \
10840                 got {first}, expected {nome}",
10841            );
10842        }
10843    }
10844
10845    #[test]
10846    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
10847        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
10848        // pinned-version scalar pin: [`Caixa::versao`] must return the
10849        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
10850        // raw `.versao` field access across every representative value
10851        // in the accept-set — the canonical `"0.1.0"` template baseline
10852        // (the same `feira init`-scaffolded default the sibling
10853        // `validate_versao_accepts_canonical_template` positive-control
10854        // gate pins), plus every canonical SemVer-2 shape the sibling
10855        // `validate_versao_accepts_canonical_forms` positive-arm sweep
10856        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
10857        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
10858        // `"10.20.30"`), plus every past-the-guard sentinel for the
10859        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
10860        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
10861        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
10862        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
10863        // `"latest"` the docker-tag-shape footgun — the sentinels pin
10864        // the accessor doesn't silently absorb the refusal cases into a
10865        // template-derived fallback like `"0.1.0"`).
10866        //
10867        // The past-the-guard sentinels pin the accessor doesn't silently
10868        // absorb the refusal cases into a template-derived fallback (a
10869        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
10870        // silently absorb the `VersaoEmpty` refusal at the accessor
10871        // boundary and the validate gate would accept a struct-literal
10872        // `Caixa { versao: "".into(), .. }` — the pin catches that at
10873        // caixa-core build time).
10874        //
10875        // Second outer top-level [`Caixa`] `&str`-return required-scalar
10876        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
10877        // scalar" projection pattern the sibling per-`Caixa`
10878        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
10879        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
10880        // (4127bb6) / per-`:children`
10881        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
10882        // / per-`:upgrade-from`
10883        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
10884        // struct `:versao`-shaped `&str`-return accessor pins on the
10885        // sibling per-typed-slot version-carrier axes, extended onto the
10886        // second outer top-level [`Caixa`] universal-axis required-
10887        // `String`-carry axis so the two universal-axis identity-
10888        // carrying scalars every `defcaixa` form supplies (`:nome` +
10889        // `:versao`) share the same "one typed dispatch per axis" pin
10890        // discipline.
10891        for versao in [
10892            "0.1.0",
10893            "0.0.0",
10894            "1.0.0",
10895            "0.2.0-rc.1",
10896            "1.0.0-alpha.0",
10897            "1.0.0+build.42",
10898            "1.0.0-rc.1+build.42",
10899            "10.20.30",
10900            "",
10901            "v0.1.0",
10902            "0.1",
10903            "^0.1",
10904            "0.1.0.0",
10905            "latest",
10906        ] {
10907            let c = caixa_with_versao(versao);
10908            assert_eq!(
10909                c.versao(),
10910                versao,
10911                "Caixa::versao must return :versao verbatim (got {}, \
10912                 expected {versao})",
10913                c.versao(),
10914            );
10915            assert_eq!(
10916                c.versao(),
10917                c.versao.as_str(),
10918                "Caixa::versao must byte-equal the raw .versao field \
10919                 access across every value in the String accept-set",
10920            );
10921        }
10922    }
10923
10924    #[test]
10925    fn validate_versao_empty_arm_routes_through_accessor() {
10926        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
10927        // must key off [`Caixa::versao`], not the raw `.versao` field
10928        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
10929        // surface the `VersaoEmpty` refusal exactly, and the canonical
10930        // `"0.1.0"` template baseline (the peer positive-arm the sibling
10931        // `validate_versao_accepts_canonical_template` gate carves out)
10932        // must pass validate. The pair jointly pins the accessor +
10933        // validate-gate composition: any future silent detour that had
10934        // the accessor return a fresh `"0.1.0"` on the empty arm
10935        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
10936        // would silently absorb the `VersaoEmpty` refusal at the
10937        // accessor boundary and the validate gate would accept a
10938        // struct-literal `Caixa { versao: "".into(), .. }` — the
10939        // composition pin catches that at caixa-core build time.
10940        //
10941        // Peer of the sibling per-`Caixa`
10942        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
10943        // composition pin on the sibling outer top-level [`Caixa`]
10944        // required-`&str` universal-axis surface — same "the validate /
10945        // shape-gate predicate must route through the substrate-
10946        // primitive typed dispatch" discipline extended onto the peer
10947        // outer top-level [`Caixa`] required-`&str` universal-axis
10948        // pinned-version composition axis, closing the second
10949        // coordinate of the "one canonical typed dispatch per per-Caixa
10950        // required-`&str` universal-axis" discipline.
10951        let c = caixa_with_versao("");
10952        assert!(
10953            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
10954            "validate_versao must reject versao == \"\" with VersaoEmpty — \
10955             the accessor and the validate gate must route through the \
10956             same substrate-primitive typed dispatch on the :versao \
10957             empty-arm",
10958        );
10959        let c = caixa_with_versao("0.1.0");
10960        assert!(
10961            c.validate_versao().is_ok(),
10962            "validate_versao must accept versao == \"0.1.0\" (the \
10963             canonical SemVer-2 template baseline)",
10964        );
10965    }
10966
10967    #[test]
10968    fn versao_projects_str_by_borrow() {
10969        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
10970        // — the `&str` borrows the underlying `String` storage of the
10971        // required `versao` slot and the accessor must not allocate a
10972        // fresh `String` on every call. Peer of the [`Caixa::nome`]
10973        // (e6b7d97) by-borrow pin on the sibling outer top-level
10974        // [`Caixa`] required-`&str`-return axis, extended onto the
10975        // second outer top-level [`Caixa`] required-`&str`-return
10976        // universal-axis pinned-version surface — the accessor's
10977        // returned `&str` must borrow from `&self` (the returned
10978        // reference's lifetime is tied to `&self`), and calling the
10979        // accessor twice on the same [`Caixa`] must yield the same
10980        // `&str` verbatim (idempotent, no side effects on `&self`).
10981        //
10982        // Pins against a future silent detour that returned an owned
10983        // `String` (which would type-check but silently allocate on
10984        // every call, breaking the zero-cost projection every peer
10985        // sibling accessor carries), an accidental
10986        // `semver::Version::parse(&self.versao).unwrap().to_string()`
10987        // detour that returned a canonicalized fresh allocation through
10988        // an already-canonical byte-string (breaking a future `const fn`
10989        // regression and silently absorbing the `VersaoInvalid` refusal
10990        // at the accessor boundary), or a one-arm-only accessor that
10991        // returned a canonicalized value on some sentinel input
10992        // (breaking the pass-through invariant the sibling required-
10993        // scalar accessors carry).
10994        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
10995            let c = caixa_with_versao(versao);
10996            let first = c.versao();
10997            let second = c.versao();
10998            assert_eq!(
10999                first, second,
11000                "Caixa::versao must be idempotent — two successive \
11001                 calls on the same &self must return the same &str",
11002            );
11003            assert_eq!(
11004                first, versao,
11005                "Caixa::versao must return :versao verbatim by borrow \
11006                 — got {first}, expected {versao}",
11007            );
11008        }
11009    }
11010
11011    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
11012        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11013        c.kind = kind;
11014        c
11015    }
11016
11017    #[test]
11018    fn kind_returns_kind_variant_verbatim_across_permutations() {
11019        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
11020        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
11021        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
11022        // the raw `.kind` field access across every variant in the
11023        // closed accept-set (`Biblioteca` — the library kind that
11024        // exports lisp forms; `Binario` — the nix-built executable kind
11025        // under `exe/`; `Servico` — the wasm-component daemon kind
11026        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
11027        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
11028        // composition kind).
11029        //
11030        // Pins against a future silent detour that re-derived the kind
11031        // from a peer axis (an accidental fallback to
11032        // `if !servicos.is_empty() { Servico } else if
11033        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
11034        // collapse that read the code-surface / mesh-slot columns into
11035        // the kind discriminator), a variant remap the operator
11036        // authors on one consumer without the other, or a stale-derive
11037        // detour that substituted [`CaixaKind::Biblioteca`] as the
11038        // default when the field held any other variant (which would
11039        // silently collapse the distinction between "author explicitly
11040        // declared `:kind Servico`" and "author declared any other
11041        // kind" every downstream renderer-dispatch site depends on).
11042        //
11043        // First outer top-level [`Caixa`] `Copy`-return required-enum-
11044        // discriminant accessor pin — opens the "outer [`Caixa`]
11045        // `Copy`-return required-discriminant" projection pattern.
11046        // Sibling in shape to the peer per-`:supervisor`
11047        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
11048        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
11049        // (921fe1b), and per-`:children`
11050        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
11051        // `Copy`-return closed-set-enum discriminant accessor pins on
11052        // the sibling nested-spec typed-slot discriminator axes,
11053        // extended here to the outer top-level [`Caixa`] universal-
11054        // axis surface.
11055        for kind in [
11056            CaixaKind::Biblioteca,
11057            CaixaKind::Binario,
11058            CaixaKind::Servico,
11059            CaixaKind::Supervisor,
11060            CaixaKind::Aplicacao,
11061        ] {
11062            let c = caixa_with_kind(kind);
11063            assert_eq!(
11064                c.kind(),
11065                kind,
11066                "Caixa::kind must return :kind verbatim (got {:?}, \
11067                 expected {kind:?})",
11068                c.kind(),
11069            );
11070            assert_eq!(
11071                c.kind(),
11072                c.kind,
11073                "Caixa::kind accessor and .kind field access must \
11074                 byte-equal — the accessor is the substrate-primitive \
11075                 typed dispatch every downstream kind-gate consumer \
11076                 must route through",
11077            );
11078        }
11079    }
11080
11081    #[test]
11082    fn require_kind_reads_through_lifted_kind_accessor() {
11083        // Two-consumer coherence pin: the [`crate::render::require_kind`]
11084        // entry-gate predicate (the canonical two-line
11085        // `require_kind(caixa, Servico)?` prelude every per-Servico /
11086        // per-Aplicacao renderer runs at its entry-point) and the
11087        // sibling [`crate::render::KindMismatch`] error carrier's
11088        // `actual:` field (which names the offending caixa's variant
11089        // in the diagnostic) must both key off the lifted accessor, so
11090        // any future rebrand on the typed slot's reader shape lands at
11091        // exactly one place. Pins the two-site coherence by exercising
11092        // every off-diagonal `(actual, expected)` pair across the
11093        // closed accept-set — the `KindMismatch { actual, expected }`
11094        // surfaced on the mismatch arm must byte-equal the pair the
11095        // accessor returns for each side.
11096        //
11097        // Peer of the sibling per-`:placement`
11098        // `validate_placement_reads_through_lifted_estrategia_accessor`
11099        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
11100        // `Copy`-return discriminant axis — same "the entry-gate
11101        // predicate and the error carrier's `actual:` field must route
11102        // through the substrate-primitive typed dispatch" discipline
11103        // extended onto the outer top-level [`Caixa`] universal-axis
11104        // discriminant surface.
11105        for expected in [
11106            CaixaKind::Biblioteca,
11107            CaixaKind::Binario,
11108            CaixaKind::Servico,
11109            CaixaKind::Supervisor,
11110            CaixaKind::Aplicacao,
11111        ] {
11112            for actual in [
11113                CaixaKind::Biblioteca,
11114                CaixaKind::Binario,
11115                CaixaKind::Servico,
11116                CaixaKind::Supervisor,
11117                CaixaKind::Aplicacao,
11118            ] {
11119                let c = caixa_with_kind(actual);
11120                let result = crate::render::require_kind(&c, expected);
11121                if expected == actual {
11122                    assert!(
11123                        result.is_ok(),
11124                        "require_kind must accept when actual == expected \
11125                         (actual={actual:?}, expected={expected:?})",
11126                    );
11127                } else {
11128                    let err = result.expect_err("require_kind must reject when actual != expected");
11129                    assert_eq!(
11130                        err.actual,
11131                        c.kind(),
11132                        "KindMismatch.actual must byte-equal Caixa::kind() \
11133                         — the error carrier's `actual:` field reads \
11134                         through the lifted accessor",
11135                    );
11136                    assert_eq!(
11137                        err.expected, expected,
11138                        "KindMismatch.expected must byte-equal the \
11139                         expected variant passed to require_kind",
11140                    );
11141                }
11142            }
11143        }
11144    }
11145
11146    #[test]
11147    fn aplicacao_view_kind_gate_routes_through_accessor() {
11148        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
11149        // must key off [`Caixa::kind`], not the raw `.kind` field
11150        // access. Structurally: a `Caixa { kind: X, .. }` for any
11151        // non-`Aplicacao` variant must fold to `None` on the
11152        // `aplicacao_view` composer (the "kind mismatch → no typed
11153        // view" contract every downstream Aplicacao consumer keys off
11154        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
11155        // `Some(_)`. The pair jointly pins the accessor + view-gate
11156        // composition: any future silent detour that had the accessor
11157        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
11158        // input would silently absorb the kind-mismatch case at the
11159        // accessor boundary and every per-Aplicacao renderer would
11160        // silently render a non-Aplicacao caixa's mesh slots — the
11161        // composition pin catches that at caixa-core build time.
11162        //
11163        // Peer of the sibling per-`Caixa`
11164        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
11165        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
11166        // composition pins on the sibling outer top-level [`Caixa`]
11167        // required-`&str` universal-axis surfaces — same "the
11168        // composer / validate gate must route through the substrate-
11169        // primitive typed dispatch" discipline extended onto the
11170        // outer top-level [`Caixa`] `Copy`-return required-
11171        // discriminant composition axis.
11172        for kind in [
11173            CaixaKind::Biblioteca,
11174            CaixaKind::Binario,
11175            CaixaKind::Servico,
11176            CaixaKind::Supervisor,
11177        ] {
11178            let c = caixa_with_kind(kind);
11179            assert!(
11180                c.aplicacao_view().is_none(),
11181                "aplicacao_view must return None on non-Aplicacao \
11182                 kind {kind:?} — the composer's kind-gate must route \
11183                 through Caixa::kind()",
11184            );
11185        }
11186        let c = caixa_with_kind(CaixaKind::Aplicacao);
11187        assert!(
11188            c.aplicacao_view().is_some(),
11189            "aplicacao_view must return Some on kind Aplicacao — \
11190             the composer's kind-gate must accept the matching arm \
11191             through Caixa::kind()",
11192        );
11193    }
11194
11195    #[test]
11196    fn supervisor_view_kind_gate_routes_through_accessor() {
11197        // Composition pin (mirror of the sibling
11198        // `aplicacao_view_kind_gate_routes_through_accessor` on the
11199        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
11200        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
11201        // field access. A `Caixa { kind: X, .. }` for any non-
11202        // `Supervisor` variant must fold to `None` on the
11203        // `supervisor_view` composer, and a `Caixa { kind:
11204        // Supervisor, .. }` must fold to `Some(_)`. Same peer
11205        // composition pin discipline on the second `_view` composer
11206        // axis.
11207        for kind in [
11208            CaixaKind::Biblioteca,
11209            CaixaKind::Binario,
11210            CaixaKind::Servico,
11211            CaixaKind::Aplicacao,
11212        ] {
11213            let c = caixa_with_kind(kind);
11214            assert!(
11215                c.supervisor_view().is_none(),
11216                "supervisor_view must return None on non-Supervisor \
11217                 kind {kind:?} — the composer's kind-gate must route \
11218                 through Caixa::kind()",
11219            );
11220        }
11221        let mut c = caixa_with_kind(CaixaKind::Supervisor);
11222        // A Supervisor caixa needs a strategy + at least one child to
11223        // fold to a Some(_) that also validates; the composer itself
11224        // requires only the kind arm, so bare kind flip is enough to
11225        // pin the `Some(_)` return, but we populate the minimum
11226        // supervisor shape so a future strengthening of the composer
11227        // to reject an empty spec doesn't false-positive this pin.
11228        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
11229        c.children = vec![crate::supervisor::ChildSpec {
11230            caixa: "child".into(),
11231            versao: "^0.1".into(),
11232            restart: crate::supervisor::RestartPolicy::Permanent,
11233        }];
11234        assert!(
11235            c.supervisor_view().is_some(),
11236            "supervisor_view must return Some on kind Supervisor — \
11237             the composer's kind-gate must accept the matching arm \
11238             through Caixa::kind()",
11239        );
11240    }
11241
11242    #[test]
11243    fn kind_projects_by_copy() {
11244        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
11245        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
11246        // `&self` (the returned value is owned, `Copy`-projected from
11247        // the underlying [`CaixaKind`] storage; two calls on the same
11248        // [`Caixa`] must yield byte-equal values). Peer of the peer
11249        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
11250        // `SupervisorSpec::estrategia` / per-`:children`
11251        // `ChildSpec::restart` `Copy`-return discriminant accessor
11252        // pins on the sibling nested-spec typed-slot discriminator
11253        // axes, extended onto the first outer top-level [`Caixa`]
11254        // required-`Copy`-return axis — pins against a future silent
11255        // detour that returned `&CaixaKind` (which would type-check
11256        // but silently constrain every consumer's callsite to a
11257        // borrow-shaped dispatch, breaking the zero-cost `Copy`
11258        // projection every peer sibling accessor carries).
11259        for kind in [
11260            CaixaKind::Biblioteca,
11261            CaixaKind::Binario,
11262            CaixaKind::Servico,
11263            CaixaKind::Supervisor,
11264            CaixaKind::Aplicacao,
11265        ] {
11266            let c = caixa_with_kind(kind);
11267            let first: CaixaKind = c.kind();
11268            let second: CaixaKind = c.kind();
11269            assert_eq!(
11270                first, second,
11271                "Caixa::kind must be idempotent — two successive \
11272                 calls on the same &self must return the same \
11273                 CaixaKind variant",
11274            );
11275            assert_eq!(
11276                first, kind,
11277                "Caixa::kind must return :kind verbatim by Copy — \
11278                 got {first:?}, expected {kind:?}",
11279            );
11280        }
11281    }
11282
11283    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
11284
11285    #[test]
11286    fn autores_returns_autores_slice_verbatim_across_permutations() {
11287        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
11288        // name-list slice pin: [`Caixa::autores`] must return the
11289        // `:autores` typed [`Vec<String>`] list verbatim as a
11290        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
11291        // access across every representative value in the accept-set —
11292        // `[]` (the "no maintainers declared" arm every existing
11293        // fixture without an `:autores` line carries), `[""]` (a past-
11294        // the-guard sentinel that pins the accessor doesn't perform a
11295        // silent `[""] → []` collapse on the empty-entry arm — validate
11296        // rejects `[""]` through `AutorEmpty` but the accessor must
11297        // ship the raw slot verbatim so a validate-time gate regression
11298        // surfaces at the caixa-helm emit boundary rather than being
11299        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
11300        // canonical single-maintainer form every `feira init` template
11301        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
11302        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
11303        // (the canonical RFC-5322 `<name> <email>` form the
11304        // `is_chart_maintainer_name_shape` predicate accepts), and
11305        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
11306        // sentinel — validate rejects through `AutorDuplicate` but the
11307        // accessor must ship the raw slot verbatim).
11308        //
11309        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
11310        // pin on the substrate primitive — opens the "outer [`Caixa`]
11311        // `&[T]` slice" projection pattern the sibling per-`Caixa`
11312        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
11313        // / `:servicos` / `:upgrade-from` / `:children` future lifts
11314        // fold on. Sibling in shape to the peer per-`:supervisor`
11315        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
11316        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
11317        // (a6e18d7), per-`:membros`
11318        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
11319        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
11320        // (0dcc926), and per-`:upgrade-from :instructions`
11321        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
11322        // `&[T]`-return slice accessor pins on the sibling per-M2 /
11323        // per-M3 typed-slot list axes, extended onto the outer top-
11324        // level [`Caixa`] universal-axis surface. Pins against a future
11325        // silent detour that returned an owned `Vec<String>` (which
11326        // would type-check but silently clone on every accessor call,
11327        // breaking the zero-cost projection every peer sibling slice
11328        // accessor carries), a `[""] → []` collapse (which would
11329        // silently absorb the `AutorEmpty` refusal case at the accessor
11330        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
11331        // would silently absorb the `AutorDuplicate` refusal case at
11332        // the accessor boundary and the caixa-helm `maintainers:` fold
11333        // would silently render a dedupped list on a struct-literal
11334        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
11335        for autores in [
11336            vec![],
11337            vec![""],
11338            vec!["pleme-io"],
11339            vec!["alice", "bob"],
11340            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
11341            vec!["pleme-io", "pleme-io"],
11342        ] {
11343            let c = caixa_with_autores(autores.clone());
11344            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
11345            assert_eq!(
11346                c.autores(),
11347                expected.as_slice(),
11348                "Caixa::autores must return :autores verbatim (got {:?}, \
11349                 expected {expected:?})",
11350                c.autores(),
11351            );
11352            assert_eq!(
11353                c.autores(),
11354                c.autores.as_slice(),
11355                "Caixa::autores must byte-equal the raw \
11356                 `self.autores.as_slice()` field access across every \
11357                 value in the Vec<String> accept-set",
11358            );
11359        }
11360    }
11361
11362    #[test]
11363    fn validate_autores_empty_entry_arm_routes_through_accessor() {
11364        // Composition pin: [`Caixa::validate_autores`]'s per-entry
11365        // empty-arm gate must key off [`Caixa::autores`], not the raw
11366        // `&self.autores` field-borrow walk. Structurally: a
11367        // `Caixa { autores: vec!["".into()], .. }` must surface the
11368        // `AutorEmpty` refusal exactly, and a
11369        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
11370        // canonical single-maintainer form) must pass validate. The
11371        // pair jointly pins the accessor + validate-gate composition:
11372        // any future silent detour that had the accessor return an
11373        // empty slice on the `[""]` arm (a
11374        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
11375        // would silently absorb the `AutorEmpty` refusal at the
11376        // accessor boundary and the validate gate would accept a
11377        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
11378        // the composition pin catches that at caixa-core build time.
11379        //
11380        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
11381        // accessor-composition pin
11382        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
11383        // sibling `Option<&str>`-composition axis and the
11384        // per-`:politicas :circuit-breaker`
11385        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
11386        // accessor-composition pin
11387        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
11388        // on the sibling required-`u32`-composition axis — same "the
11389        // validate / shape-gate predicate must route through the
11390        // substrate-primitive typed dispatch" discipline extended onto
11391        // the outer top-level [`Caixa`] universal-axis `&[T]`-
11392        // composition surface.
11393        let c = caixa_with_autores(vec![""]);
11394        assert!(
11395            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
11396            "validate_autores must reject autores == vec![\"\"] with \
11397             AutorEmpty — the accessor and the validate gate must \
11398             route through the same substrate-primitive typed dispatch \
11399             on the :autores per-entry empty arm",
11400        );
11401        let c = caixa_with_autores(vec!["pleme-io"]);
11402        assert!(
11403            c.validate_autores().is_ok(),
11404            "validate_autores must accept autores == vec![\"pleme-io\"] \
11405             (the canonical single-maintainer shape every `feira init` \
11406             template scaffolds)",
11407        );
11408    }
11409
11410    #[test]
11411    fn autores_projects_slice_by_borrow() {
11412        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
11413        // borrow — the returned slice borrows the underlying
11414        // `Vec<String>` storage of the `:autores` slot and the
11415        // accessor must not clone the backing `Vec` on every call.
11416        // Peer of the per-`:membros`
11417        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
11418        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
11419        // (0dcc926) / per-`:placement`
11420        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
11421        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
11422        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
11423        // typed-slot `&[T]`-return axes, extended onto the outer top-
11424        // level [`Caixa`] universal-axis `&[String]` shape — the
11425        // accessor's returned slice must borrow from `&self` (the
11426        // returned reference's lifetime is tied to `&self`), and
11427        // calling the accessor twice on the same [`Caixa`] must yield
11428        // slices that are pointer-equal (the underlying byte-buffer is
11429        // the storage `Vec`'s allocation, not a fresh copy) as well as
11430        // value-equal (idempotent, no side effects on `&self`).
11431        //
11432        // Pins against a future silent detour that returned an owned
11433        // `Vec<String>` (which would type-check but silently clone on
11434        // every call, breaking the zero-cost projection every peer
11435        // sibling slice accessor carries), a `&Vec<String>` return
11436        // (which would leak the backing `Vec`'s grow/push/reserve
11437        // surface no downstream consumer reaches for), or a one-arm-
11438        // only accessor that returned a saturating value on some
11439        // sentinel input (breaking the pass-through invariant the
11440        // sibling slice accessors carry).
11441        for autores in [
11442            vec![],
11443            vec!["pleme-io"],
11444            vec!["alice", "bob"],
11445            vec!["pleme-io", "pleme-io"],
11446        ] {
11447            let c = caixa_with_autores(autores.clone());
11448            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
11449            let first = c.autores();
11450            let second = c.autores();
11451            assert_eq!(
11452                first, second,
11453                "Caixa::autores must be idempotent — two successive \
11454                 calls on the same &self must return the same \
11455                 &[String]",
11456            );
11457            assert_eq!(
11458                first.as_ptr(),
11459                second.as_ptr(),
11460                "Caixa::autores must borrow the underlying Vec<String> \
11461                 storage — two successive calls must return slices \
11462                 with the same backing pointer (a fresh Vec<String> \
11463                 clone would change the pointer on every call)",
11464            );
11465            assert_eq!(
11466                first,
11467                expected.as_slice(),
11468                "Caixa::autores must return :autores verbatim by \
11469                 borrow — got {first:?}, expected {expected:?}",
11470            );
11471        }
11472    }
11473
11474    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
11475
11476    #[test]
11477    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
11478        // The canonical per-`Caixa` `:etiquetas` universal-axis
11479        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
11480        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
11481        // as a `&[String]`, byte-equal to the raw
11482        // `self.etiquetas.as_slice()` access across every representative
11483        // value in the accept-set — `[]` (the "no tags declared" arm
11484        // every existing fixture without an `:etiquetas` line carries),
11485        // `[""]` (a past-the-guard sentinel that pins the accessor
11486        // doesn't perform a silent `[""] → []` collapse on the empty-
11487        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
11488        // but the accessor must ship the raw slot verbatim so a
11489        // validate-time gate regression surfaces at the caixa-helm emit
11490        // boundary rather than being silently absorbed into a keyword-
11491        // drop), `["demo"]` (the canonical single-tag form every
11492        // `feira init` template scaffolds), `["example", "aplicacao",
11493        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
11494        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
11495        // (a past-the-guard duplicate sentinel — validate rejects
11496        // through `EtiquetaDuplicate` but the accessor must ship the
11497        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
11498        // at chart-render time isn't silently promoted into the
11499        // accessor boundary and struct-literal
11500        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
11501        // fixtures continue to expose the duplicate at the accessor).
11502        //
11503        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
11504        // pin on the substrate primitive — folds on the "outer
11505        // [`Caixa`] `&[T]` slice" projection pattern
11506        // `autores_returns_autores_slice_verbatim_across_permutations`
11507        // (b5d813f) opened, sibling in shape and idiom. Pins against a
11508        // future silent detour that returned an owned `Vec<String>`
11509        // (which would type-check but silently clone on every accessor
11510        // call, breaking the zero-cost projection every peer sibling
11511        // slice accessor carries), a `[""] → []` collapse (which would
11512        // silently absorb the `EtiquetaEmpty` refusal case at the
11513        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
11514        // (which would silently absorb the `EtiquetaDuplicate` refusal
11515        // case at the accessor boundary — the caixa-helm chart-render
11516        // `BTreeSet::collect` dedup is downstream of the accessor and
11517        // must not be silently promoted into it).
11518        for etiquetas in [
11519            vec![],
11520            vec![""],
11521            vec!["demo"],
11522            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
11523            vec!["demo", "demo"],
11524        ] {
11525            let c = caixa_with_etiquetas(etiquetas.clone());
11526            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
11527            assert_eq!(
11528                c.etiquetas(),
11529                expected.as_slice(),
11530                "Caixa::etiquetas must return :etiquetas verbatim (got \
11531                 {:?}, expected {expected:?})",
11532                c.etiquetas(),
11533            );
11534            assert_eq!(
11535                c.etiquetas(),
11536                c.etiquetas.as_slice(),
11537                "Caixa::etiquetas must byte-equal the raw \
11538                 `self.etiquetas.as_slice()` field access across every \
11539                 value in the Vec<String> accept-set",
11540            );
11541        }
11542    }
11543
11544    #[test]
11545    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
11546        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
11547        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
11548        // `&self.etiquetas` field-borrow walk. Structurally: a
11549        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
11550        // `EtiquetaEmpty` refusal exactly, and a
11551        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
11552        // single-tag form) must pass validate. The pair jointly pins
11553        // the accessor + validate-gate composition: any future silent
11554        // detour that had the accessor return an empty slice on the
11555        // `[""]` arm (a
11556        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
11557        // silently absorb the `EtiquetaEmpty` refusal at the accessor
11558        // boundary and the validate gate would accept a struct-literal
11559        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
11560        // pin catches that at caixa-core build time.
11561        //
11562        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
11563        // through_accessor` (b5d813f) accessor-composition pin on the
11564        // sibling `&[T]`-composition axis — same "the validate / shape-
11565        // gate predicate must route through the substrate-primitive
11566        // typed dispatch" discipline extended onto the sibling outer
11567        // top-level [`Caixa`] `&[T]`-composition surface.
11568        let c = caixa_with_etiquetas(vec![""]);
11569        assert!(
11570            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
11571            "validate_etiquetas must reject etiquetas == vec![\"\"] \
11572             with EtiquetaEmpty — the accessor and the validate gate \
11573             must route through the same substrate-primitive typed \
11574             dispatch on the :etiquetas per-entry empty arm",
11575        );
11576        let c = caixa_with_etiquetas(vec!["demo"]);
11577        assert!(
11578            c.validate_etiquetas().is_ok(),
11579            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
11580             (the canonical single-tag shape every `feira init` \
11581             template scaffolds)",
11582        );
11583    }
11584
11585    #[test]
11586    fn etiquetas_projects_slice_by_borrow() {
11587        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
11588        // by borrow — the returned slice borrows the underlying
11589        // `Vec<String>` storage of the `:etiquetas` slot and the
11590        // accessor must not clone the backing `Vec` on every call.
11591        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
11592        // (b5d813f) by-borrow pin on the sibling outer top-level
11593        // [`Caixa`] `&[String]`-return axis — the accessor's returned
11594        // slice must borrow from `&self` (the returned reference's
11595        // lifetime is tied to `&self`), and calling the accessor twice
11596        // on the same [`Caixa`] must yield slices that are pointer-
11597        // equal (the underlying byte-buffer is the storage `Vec`'s
11598        // allocation, not a fresh copy) as well as value-equal
11599        // (idempotent, no side effects on `&self`).
11600        //
11601        // Pins against a future silent detour that returned an owned
11602        // `Vec<String>` (which would type-check but silently clone on
11603        // every call, breaking the zero-cost projection every peer
11604        // sibling slice accessor carries), a `&Vec<String>` return
11605        // (which would leak the backing `Vec`'s grow/push/reserve
11606        // surface no downstream consumer reaches for), or a one-arm-
11607        // only accessor that returned a saturating value on some
11608        // sentinel input (breaking the pass-through invariant the
11609        // sibling slice accessors carry).
11610        for etiquetas in [
11611            vec![],
11612            vec!["demo"],
11613            vec!["example", "aplicacao", "mesh"],
11614            vec!["demo", "demo"],
11615        ] {
11616            let c = caixa_with_etiquetas(etiquetas.clone());
11617            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
11618            let first = c.etiquetas();
11619            let second = c.etiquetas();
11620            assert_eq!(
11621                first, second,
11622                "Caixa::etiquetas must be idempotent — two successive \
11623                 calls on the same &self must return the same \
11624                 &[String]",
11625            );
11626            assert_eq!(
11627                first.as_ptr(),
11628                second.as_ptr(),
11629                "Caixa::etiquetas must borrow the underlying \
11630                 Vec<String> storage — two successive calls must \
11631                 return slices with the same backing pointer (a fresh \
11632                 Vec<String> clone would change the pointer on every \
11633                 call)",
11634            );
11635            assert_eq!(
11636                first,
11637                expected.as_slice(),
11638                "Caixa::etiquetas must return :etiquetas verbatim by \
11639                 borrow — got {first:?}, expected {expected:?}",
11640            );
11641        }
11642    }
11643
11644    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
11645
11646    #[test]
11647    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
11648        // The canonical per-`Caixa` `:bibliotecas` universal-axis
11649        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
11650        // must return the `:bibliotecas` typed [`Vec<String>`] list
11651        // verbatim as a `&[String]`, byte-equal to the raw
11652        // `self.bibliotecas.as_slice()` access across every
11653        // representative value in the accept-set — `[]` (the "no
11654        // libraries declared" arm every `:kind` other than `Biblioteca`
11655        // + every `Biblioteca` relying on the canonical
11656        // `lib/<nome>.lisp` implicit-default path carries; the
11657        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
11658        // fires exactly on this empty-slot + `Biblioteca`-kind
11659        // combination), `[""]` (a past-the-guard sentinel that pins
11660        // the accessor doesn't perform a silent `[""] → []` collapse
11661        // on the empty-entry arm — validate rejects `[""]` through
11662        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
11663        // must ship the raw slot verbatim so a validate-time gate
11664        // regression surfaces at the `feira build` phase-1 parse
11665        // boundary rather than being silently absorbed into a
11666        // library-drop), `["lib/demo.lisp"]` (the canonical single-
11667        // entry form `Caixa::template` scaffolds and every `feira init`
11668        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
11669        // (the canonical multi-library form the
11670        // `validate_code_paths_accepts_explicit_relative_paths_on_
11671        // every_slot` fixture emits), and `["lib/foo.lisp",
11672        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
11673        // validate rejects through `CodePathDuplicate { slot:
11674        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
11675        // but the accessor must ship the raw slot verbatim so the
11676        // `feira build` `for entry in caixa.bibliotecas()` parse walk
11677        // sees the duplicate at the accessor boundary and struct-
11678        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
11679        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
11680        // the duplicate at the accessor).
11681        //
11682        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
11683        // pin on the substrate primitive — folds on the "outer
11684        // [`Caixa`] `&[T]` slice" projection pattern
11685        // `autores_returns_autores_slice_verbatim_across_permutations`
11686        // (b5d813f) opened and
11687        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
11688        // (78c7d3c) folded on, sibling in shape and idiom. Pins
11689        // against a future silent detour that returned an owned
11690        // `Vec<String>` (which would type-check but silently clone on
11691        // every accessor call, breaking the zero-cost projection
11692        // every peer sibling slice accessor carries), a `[""] → []`
11693        // collapse (which would silently absorb the `CodePathEmpty`
11694        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
11695        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
11696        // would silently absorb the `CodePathDuplicate` refusal case
11697        // at the accessor boundary — the per-slot set-not-multiset
11698        // gate is downstream of the accessor and must not be silently
11699        // promoted into it).
11700        for bibliotecas in [
11701            vec![],
11702            vec![""],
11703            vec!["lib/demo.lisp"],
11704            vec!["lib/demo.lisp", "lib/helpers.lisp"],
11705            vec!["lib/foo.lisp", "lib/foo.lisp"],
11706        ] {
11707            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
11708            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
11709            assert_eq!(
11710                c.bibliotecas(),
11711                expected.as_slice(),
11712                "Caixa::bibliotecas must return :bibliotecas verbatim \
11713                 (got {:?}, expected {expected:?})",
11714                c.bibliotecas(),
11715            );
11716            assert_eq!(
11717                c.bibliotecas(),
11718                c.bibliotecas.as_slice(),
11719                "Caixa::bibliotecas must byte-equal the raw \
11720                 `self.bibliotecas.as_slice()` field access across \
11721                 every value in the Vec<String> accept-set",
11722            );
11723        }
11724    }
11725
11726    #[test]
11727    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
11728        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
11729        // empty-arm gate on the `:bibliotecas` slot must key off
11730        // [`Caixa::bibliotecas`], not a divergent raw
11731        // `&self.bibliotecas` field-borrow walk. Structurally: a
11732        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
11733        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
11734        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
11735        // into()], .. }` (the canonical single-library form
11736        // `Caixa::template` scaffolds) must pass validate. The pair
11737        // jointly pins the accessor + validate-gate composition: any
11738        // future silent detour that had the accessor return an empty
11739        // slice on the `[""]` arm (a `.iter().filter(|s|
11740        // !s.is_empty()).collect()` collapse) would silently absorb
11741        // the `CodePathEmpty` refusal at the accessor boundary and
11742        // the validate gate would accept a struct-literal
11743        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
11744        // composition pin catches that at caixa-core build time.
11745        //
11746        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
11747        // through_accessor` (b5d813f) and
11748        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
11749        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
11750        // composition axes — same "the validate / shape-gate
11751        // predicate must route through the substrate-primitive typed
11752        // dispatch" discipline extended onto the sibling outer top-
11753        // level [`Caixa`] `&[T]`-composition surface. Nominally the
11754        // in-tree `validate_code_paths` production body still keys
11755        // off the internal `[(":bibliotecas", &self.bibliotecas,
11756        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
11757        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
11758        // (the tuple's homogeneous slice-typed shape blocks a per-
11759        // element accessor swap in isolation — a future companion
11760        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
11761        // `&[T]` slice-accessor axis closes that tuple onto the
11762        // triple of typed dispatches as a unit); the composition pin
11763        // catches any future accessor-side silent filter drop against
11764        // that eventual tuple-closure regardless of whether the
11765        // `:bibliotecas` slot is threaded through the accessor or the
11766        // raw field access at the tuple's construction site.
11767        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
11768        assert!(
11769            matches!(
11770                c.validate_code_paths(),
11771                Err(ManifestError::CodePathEmpty {
11772                    slot: ":bibliotecas"
11773                })
11774            ),
11775            "validate_code_paths must reject bibliotecas == vec![\"\"] \
11776             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
11777             accessor and the validate gate must route through the \
11778             same substrate-primitive typed dispatch on the \
11779             :bibliotecas per-entry empty arm",
11780        );
11781        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
11782        assert!(
11783            c.validate_code_paths().is_ok(),
11784            "validate_code_paths must accept bibliotecas == \
11785             vec![\"lib/demo.lisp\"] (the canonical single-library \
11786             shape every `feira init` template scaffolds)",
11787        );
11788    }
11789
11790    #[test]
11791    fn bibliotecas_projects_slice_by_borrow() {
11792        // The by-borrow pin: [`Caixa::bibliotecas`] returns
11793        // `&[String]` by borrow — the returned slice borrows the
11794        // underlying `Vec<String>` storage of the `:bibliotecas` slot
11795        // and the accessor must not clone the backing `Vec` on every
11796        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
11797        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
11798        // by-borrow pins on the sibling outer top-level [`Caixa`]
11799        // `&[String]`-return axes — the accessor's returned slice
11800        // must borrow from `&self` (the returned reference's lifetime
11801        // is tied to `&self`), and calling the accessor twice on the
11802        // same [`Caixa`] must yield slices that are pointer-equal
11803        // (the underlying byte-buffer is the storage `Vec`'s
11804        // allocation, not a fresh copy) as well as value-equal
11805        // (idempotent, no side effects on `&self`).
11806        //
11807        // Pins against a future silent detour that returned an owned
11808        // `Vec<String>` (which would type-check but silently clone on
11809        // every call, breaking the zero-cost projection every peer
11810        // sibling slice accessor carries), a `&Vec<String>` return
11811        // (which would leak the backing `Vec`'s grow/push/reserve
11812        // surface no downstream consumer reaches for), or a one-arm-
11813        // only accessor that returned a saturating value on some
11814        // sentinel input (breaking the pass-through invariant the
11815        // sibling slice accessors carry).
11816        for bibliotecas in [
11817            vec![],
11818            vec!["lib/demo.lisp"],
11819            vec!["lib/demo.lisp", "lib/helpers.lisp"],
11820            vec!["lib/foo.lisp", "lib/foo.lisp"],
11821        ] {
11822            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
11823            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
11824            let first = c.bibliotecas();
11825            let second = c.bibliotecas();
11826            assert_eq!(
11827                first, second,
11828                "Caixa::bibliotecas must be idempotent — two \
11829                 successive calls on the same &self must return the \
11830                 same &[String]",
11831            );
11832            assert_eq!(
11833                first.as_ptr(),
11834                second.as_ptr(),
11835                "Caixa::bibliotecas must borrow the underlying \
11836                 Vec<String> storage — two successive calls must \
11837                 return slices with the same backing pointer (a \
11838                 fresh Vec<String> clone would change the pointer on \
11839                 every call)",
11840            );
11841            assert_eq!(
11842                first,
11843                expected.as_slice(),
11844                "Caixa::bibliotecas must return :bibliotecas verbatim \
11845                 by borrow — got {first:?}, expected {expected:?}",
11846            );
11847        }
11848    }
11849
11850    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
11851
11852    #[test]
11853    fn exe_returns_exe_slice_verbatim_across_permutations() {
11854        // The canonical per-`Caixa` `:exe` universal-axis
11855        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
11856        // must return the `:exe` typed [`Vec<String>`] list verbatim as
11857        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
11858        // access across every representative value in the accept-set —
11859        // `[]` (the "no executable declared" arm every `:kind` other
11860        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
11861        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
11862        // + `Binario`-kind combination), `[""]` (a past-the-guard
11863        // sentinel that pins the accessor doesn't perform a silent
11864        // `[""] → []` collapse on the empty-entry arm — validate rejects
11865        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
11866        // accessor must ship the raw slot verbatim so a validate-time
11867        // gate regression surfaces at the layout / `feira nix` boundary
11868        // rather than being silently absorbed into an executable-drop),
11869        // `["exe/cli"]` (the canonical single-entry Binario form every
11870        // in-tree `caixa_with_code_paths` positive control uses),
11871        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
11872        // form the `validate_code_paths_accepts_explicit_relative_paths_
11873        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
11874        // (a past-the-guard duplicate sentinel — validate rejects
11875        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
11876        // set-not-multiset gate, but the accessor must ship the raw
11877        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
11878        // into(), "exe/cli".into()], .. }` fixtures continue to expose
11879        // the duplicate at the accessor).
11880        //
11881        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
11882        // pin on the substrate primitive — folds on the "outer
11883        // [`Caixa`] `&[T]` slice" projection pattern
11884        // `autores_returns_autores_slice_verbatim_across_permutations`
11885        // (b5d813f) opened,
11886        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
11887        // (78c7d3c) folded on, and
11888        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
11889        // (8a36c23) closed the universal-axis text-tag family of.
11890        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
11891        // the sibling `:servicos` future lift closes onto. Pins against
11892        // a future silent detour that returned an owned `Vec<String>`
11893        // (which would type-check but silently clone on every accessor
11894        // call, breaking the zero-cost projection every peer sibling
11895        // slice accessor carries), a `[""] → []` collapse (which would
11896        // silently absorb the `CodePathEmpty` refusal case at the
11897        // accessor boundary), or an `["exe/cli", "exe/cli"] →
11898        // ["exe/cli"]` dedup collapse (which would silently absorb the
11899        // `CodePathDuplicate` refusal case at the accessor boundary —
11900        // the per-slot set-not-multiset gate is downstream of the
11901        // accessor and must not be silently promoted into it).
11902        for exe in [
11903            vec![],
11904            vec![""],
11905            vec!["exe/cli"],
11906            vec!["exe/cli", "exe/serve"],
11907            vec!["exe/cli", "exe/cli"],
11908        ] {
11909            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
11910            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
11911            assert_eq!(
11912                c.exe(),
11913                expected.as_slice(),
11914                "Caixa::exe must return :exe verbatim (got {:?}, \
11915                 expected {expected:?})",
11916                c.exe(),
11917            );
11918            assert_eq!(
11919                c.exe(),
11920                c.exe.as_slice(),
11921                "Caixa::exe must byte-equal the raw \
11922                 `self.exe.as_slice()` field access across every value \
11923                 in the Vec<String> accept-set",
11924            );
11925        }
11926    }
11927
11928    #[test]
11929    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
11930        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
11931        // empty-arm gate on the `:exe` slot must key off
11932        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
11933        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
11934        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
11935        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
11936        // (the canonical single-executable form every in-tree
11937        // `caixa_with_code_paths` positive control uses) must pass
11938        // validate. The pair jointly pins the accessor + validate-gate
11939        // composition: any future silent detour that had the accessor
11940        // return an empty slice on the `[""]` arm (a
11941        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
11942        // silently absorb the `CodePathEmpty` refusal at the accessor
11943        // boundary and the validate gate would accept a struct-literal
11944        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
11945        // catches that at caixa-core build time.
11946        //
11947        // Peer of the per-`Caixa`
11948        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
11949        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
11950        // (b5d813f), and
11951        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
11952        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
11953        // composition axes — same "the validate / shape-gate predicate
11954        // must route through the substrate-primitive typed dispatch"
11955        // discipline extended onto the sibling outer top-level [`Caixa`]
11956        // `&[T]`-composition surface. Nominally the in-tree
11957        // `validate_code_paths` production body still keys off the
11958        // internal `[(":bibliotecas", &self.bibliotecas,
11959        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
11960        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
11961        // (the tuple's homogeneous slice-typed shape blocks a per-
11962        // element accessor swap in isolation — a future companion lift
11963        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
11964        // accessor axis closes that tuple onto the triple of typed
11965        // dispatches as a unit); the composition pin catches any future
11966        // accessor-side silent filter drop against that eventual tuple-
11967        // closure regardless of whether the `:exe` slot is threaded
11968        // through the accessor or the raw field access at the tuple's
11969        // construction site.
11970        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
11971        assert!(
11972            matches!(
11973                c.validate_code_paths(),
11974                Err(ManifestError::CodePathEmpty { slot: ":exe" })
11975            ),
11976            "validate_code_paths must reject exe == vec![\"\"] \
11977             with CodePathEmpty {{ slot: \":exe\" }} — the \
11978             accessor and the validate gate must route through the \
11979             same substrate-primitive typed dispatch on the \
11980             :exe per-entry empty arm",
11981        );
11982        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
11983        assert!(
11984            c.validate_code_paths().is_ok(),
11985            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
11986             (the canonical single-executable shape every in-tree \
11987             `caixa_with_code_paths` positive control uses)",
11988        );
11989    }
11990
11991    #[test]
11992    fn exe_projects_slice_by_borrow() {
11993        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
11994        // borrow — the returned slice borrows the underlying
11995        // `Vec<String>` storage of the `:exe` slot and the accessor
11996        // must not clone the backing `Vec` on every call. Peer of the
11997        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
11998        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
11999        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
12000        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
12001        // return axes — the accessor's returned slice must borrow from
12002        // `&self` (the returned reference's lifetime is tied to
12003        // `&self`), and calling the accessor twice on the same
12004        // [`Caixa`] must yield slices that are pointer-equal (the
12005        // underlying byte-buffer is the storage `Vec`'s allocation,
12006        // not a fresh copy) as well as value-equal (idempotent, no
12007        // side effects on `&self`).
12008        //
12009        // Pins against a future silent detour that returned an owned
12010        // `Vec<String>` (which would type-check but silently clone on
12011        // every call, breaking the zero-cost projection every peer
12012        // sibling slice accessor carries), a `&Vec<String>` return
12013        // (which would leak the backing `Vec`'s grow/push/reserve
12014        // surface no downstream consumer reaches for), or a one-arm-
12015        // only accessor that returned a saturating value on some
12016        // sentinel input (breaking the pass-through invariant the
12017        // sibling slice accessors carry).
12018        for exe in [
12019            vec![],
12020            vec!["exe/cli"],
12021            vec!["exe/cli", "exe/serve"],
12022            vec!["exe/cli", "exe/cli"],
12023        ] {
12024            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
12025            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
12026            let first = c.exe();
12027            let second = c.exe();
12028            assert_eq!(
12029                first, second,
12030                "Caixa::exe must be idempotent — two successive calls \
12031                 on the same &self must return the same &[String]",
12032            );
12033            assert_eq!(
12034                first.as_ptr(),
12035                second.as_ptr(),
12036                "Caixa::exe must borrow the underlying Vec<String> \
12037                 storage — two successive calls must return slices \
12038                 with the same backing pointer (a fresh Vec<String> \
12039                 clone would change the pointer on every call)",
12040            );
12041            assert_eq!(
12042                first,
12043                expected.as_slice(),
12044                "Caixa::exe must return :exe verbatim by borrow — \
12045                 got {first:?}, expected {expected:?}",
12046            );
12047        }
12048    }
12049
12050    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
12051
12052    #[test]
12053    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
12054        // The canonical per-`Caixa` `:servicos` universal-axis
12055        // ComputeUnit-CR-YAML-entry-path-list slice pin:
12056        // [`Caixa::servicos`] must return the `:servicos` typed
12057        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
12058        // the raw `self.servicos.as_slice()` access across every
12059        // representative value in the accept-set — `[]` (the "no
12060        // ComputeUnit-CR declared" arm every `:kind` other than
12061        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
12062        // `ServicoWithoutServicos` arm-gate fires exactly on this
12063        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
12064        // guard sentinel that pins the accessor doesn't perform a
12065        // silent `[""] → []` collapse on the empty-entry arm — validate
12066        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
12067        // but the accessor must ship the raw slot verbatim so a
12068        // validate-time gate regression surfaces at the layout /
12069        // per-Servico renderer boundary rather than being silently
12070        // absorbed into a component-drop),
12071        // `["servicos/demo.computeunit.yaml"]` (the canonical
12072        // singleton V0-shape every in-tree `caixa_with_code_paths`
12073        // positive control uses; the same shape
12074        // [`crate::require_single_servico`] admits),
12075        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
12076        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
12077        // singularity gate rejects through `ServicoCountMismatch
12078        // { count: 2 }` but the accessor must ship the raw slot
12079        // verbatim so struct-literal `Caixa { servicos: vec![...,
12080        // ...], .. }` fixtures continue to expose the count at the
12081        // accessor), and `["servicos/a.computeunit.yaml",
12082        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
12083        // sentinel — validate rejects through
12084        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
12085        // set-not-multiset gate, but the accessor must ship the raw
12086        // slot verbatim so struct-literal fixtures continue to expose
12087        // the duplicate at the accessor).
12088        //
12089        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
12090        // slice accessor pin on the substrate primitive — folds on the
12091        // "outer [`Caixa`] `&[T]` slice" projection pattern
12092        // `autores_returns_autores_slice_verbatim_across_permutations`
12093        // (b5d813f) opened,
12094        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12095        // (78c7d3c) folded on,
12096        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12097        // (8a36c23) closed the universal-axis text-tag family of, and
12098        // `exe_returns_exe_slice_verbatim_across_permutations`
12099        // (65d9527) opened the foreign-code-slot sub-family of. Closes
12100        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
12101        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
12102        // `:servicos`) now each carries a substrate-canonical slice
12103        // accessor. Pins against a future silent detour that returned
12104        // an owned `Vec<String>` (which would type-check but silently
12105        // clone on every accessor call, breaking the zero-cost
12106        // projection every peer sibling slice accessor carries), a
12107        // `[""] → []` collapse (which would silently absorb the
12108        // `CodePathEmpty` refusal case at the accessor boundary), an
12109        // `[a, a] → [a]` dedup collapse (which would silently absorb
12110        // the `CodePathDuplicate` refusal case at the accessor
12111        // boundary — the per-slot set-not-multiset gate is downstream
12112        // of the accessor and must not be silently promoted into it),
12113        // or a `[a, b] → [a]` singleton collapse (which would silently
12114        // absorb the V0 `ServicoCountMismatch` refusal case at the
12115        // accessor boundary — the V0 singularity gate is downstream of
12116        // the accessor and must not be silently promoted into it).
12117        for servicos in [
12118            vec![],
12119            vec![""],
12120            vec!["servicos/demo.computeunit.yaml"],
12121            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
12122            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
12123        ] {
12124            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
12125            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
12126            assert_eq!(
12127                c.servicos(),
12128                expected.as_slice(),
12129                "Caixa::servicos must return :servicos verbatim (got \
12130                 {:?}, expected {expected:?})",
12131                c.servicos(),
12132            );
12133            assert_eq!(
12134                c.servicos(),
12135                c.servicos.as_slice(),
12136                "Caixa::servicos must byte-equal the raw \
12137                 `self.servicos.as_slice()` field access across every \
12138                 value in the Vec<String> accept-set",
12139            );
12140        }
12141    }
12142
12143    #[test]
12144    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
12145        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12146        // empty-arm gate on the `:servicos` slot must key off
12147        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
12148        // field-borrow walk. Structurally: a `Caixa { servicos:
12149        // vec!["".into()], .. }` must surface the `CodePathEmpty
12150        // { slot: ":servicos" }` refusal exactly, and a `Caixa
12151        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
12152        // .. }` (the canonical singleton V0-shape every in-tree
12153        // `caixa_with_code_paths` positive control uses) must pass
12154        // validate. The pair jointly pins the accessor + validate-gate
12155        // composition: any future silent detour that had the accessor
12156        // return an empty slice on the `[""]` arm (a `.iter().filter
12157        // (|s| !s.is_empty()).collect()` collapse) would silently
12158        // absorb the `CodePathEmpty` refusal at the accessor boundary
12159        // and the validate gate would accept a struct-literal
12160        // `Caixa { servicos: vec!["".into()], .. }` — the composition
12161        // pin catches that at caixa-core build time.
12162        //
12163        // Peer of the per-`Caixa`
12164        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12165        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
12166        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
12167        // (b5d813f), and
12168        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12169        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12170        // composition axes — same "the validate / shape-gate predicate
12171        // must route through the substrate-primitive typed dispatch"
12172        // discipline extended onto the sibling outer top-level
12173        // [`Caixa`] `&[T]`-composition surface, closing the trio of
12174        // code-surface accessor-composition pins on the same axis.
12175        // Nominally the in-tree `validate_code_paths` production body
12176        // still keys off the internal
12177        // `[(":bibliotecas", &self.bibliotecas,
12178        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12179        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12180        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
12181        // per-element accessor swap in isolation — a future companion
12182        // lift promotes the tuple's element type to `&[String]` and
12183        // threads the triple of typed dispatches through as a unit);
12184        // the composition pin catches any future accessor-side silent
12185        // filter drop against that eventual tuple-closure regardless
12186        // of whether the `:servicos` slot is threaded through the
12187        // accessor or the raw field access at the tuple's construction
12188        // site.
12189        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
12190        assert!(
12191            matches!(
12192                c.validate_code_paths(),
12193                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
12194            ),
12195            "validate_code_paths must reject servicos == vec![\"\"] \
12196             with CodePathEmpty {{ slot: \":servicos\" }} — the \
12197             accessor and the validate gate must route through the \
12198             same substrate-primitive typed dispatch on the \
12199             :servicos per-entry empty arm",
12200        );
12201        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
12202        assert!(
12203            c.validate_code_paths().is_ok(),
12204            "validate_code_paths must accept servicos == \
12205             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
12206             singleton V0-shape every in-tree `caixa_with_code_paths` \
12207             positive control uses)",
12208        );
12209    }
12210
12211    #[test]
12212    fn servicos_projects_slice_by_borrow() {
12213        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
12214        // borrow — the returned slice borrows the underlying
12215        // `Vec<String>` storage of the `:servicos` slot and the
12216        // accessor must not clone the backing `Vec` on every call.
12217        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12218        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
12219        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
12220        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
12221        // the sibling outer top-level [`Caixa`] `&[String]`-return
12222        // axes — the accessor's returned slice must borrow from
12223        // `&self` (the returned reference's lifetime is tied to
12224        // `&self`), and calling the accessor twice on the same
12225        // [`Caixa`] must yield slices that are pointer-equal (the
12226        // underlying byte-buffer is the storage `Vec`'s allocation,
12227        // not a fresh copy) as well as value-equal (idempotent, no
12228        // side effects on `&self`).
12229        //
12230        // Pins against a future silent detour that returned an owned
12231        // `Vec<String>` (which would type-check but silently clone on
12232        // every call, breaking the zero-cost projection every peer
12233        // sibling slice accessor carries), a `&Vec<String>` return
12234        // (which would leak the backing `Vec`'s grow/push/reserve
12235        // surface no downstream consumer reaches for), or a one-arm-
12236        // only accessor that returned a saturating value on some
12237        // sentinel input (breaking the pass-through invariant the
12238        // sibling slice accessors carry).
12239        for servicos in [
12240            vec![],
12241            vec!["servicos/demo.computeunit.yaml"],
12242            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
12243            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
12244        ] {
12245            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
12246            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
12247            let first = c.servicos();
12248            let second = c.servicos();
12249            assert_eq!(
12250                first, second,
12251                "Caixa::servicos must be idempotent — two successive \
12252                 calls on the same &self must return the same &[String]",
12253            );
12254            assert_eq!(
12255                first.as_ptr(),
12256                second.as_ptr(),
12257                "Caixa::servicos must borrow the underlying \
12258                 Vec<String> storage — two successive calls must \
12259                 return slices with the same backing pointer (a fresh \
12260                 Vec<String> clone would change the pointer on every \
12261                 call)",
12262            );
12263            assert_eq!(
12264                first,
12265                expected.as_slice(),
12266                "Caixa::servicos must return :servicos verbatim by \
12267                 borrow — got {first:?}, expected {expected:?}",
12268            );
12269        }
12270    }
12271
12272    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
12273
12274    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
12275        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12276        c.deps = deps;
12277        c
12278    }
12279
12280    #[test]
12281    fn deps_returns_deps_slice_verbatim_across_permutations() {
12282        // The canonical per-`Caixa` `:deps` universal-axis runtime-
12283        // dependency-declaration-list slice pin: [`Caixa::deps`] must
12284        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
12285        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
12286        // access across every representative value in the accept-set —
12287        // `[]` (the "no runtime deps declared" arm every existing
12288        // fixture without a `:deps` line carries; the
12289        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
12290        // single-entry list (the shape most consumer caixas carry), a
12291        // canonical two-entry list (the multi-dep runtime closure), and
12292        // two past-the-guard sentinels — a `[""]`-`:nome` entry
12293        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
12294        // `NomeInvalid` but the accessor must ship the raw slot
12295        // verbatim) and a `[a, a]` duplicate (validate rejects through
12296        // `DuplicateNome { list: ":deps" }` but the accessor must ship
12297        // the raw slot verbatim so struct-literal fixtures continue to
12298        // expose the duplicate at the accessor).
12299        //
12300        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
12301        // pin on the substrate primitive — opens the outer-`Caixa`
12302        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
12303        // future lift closes on. Peer of the closed outer-`Caixa`
12304        // foreign-code-slot `&[String]` sub-family
12305        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12306        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
12307        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
12308        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
12309        // (`autores_returns_autores_slice_verbatim_across_permutations`
12310        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12311        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
12312        // projection pattern onto a novel element-type axis (`Dep`
12313        // composite vs the prior sibling family's `String` scalar).
12314        // Pins against a future silent detour that returned an owned
12315        // `Vec<Dep>` (which would type-check but silently clone on every
12316        // accessor call, breaking the zero-cost projection every peer
12317        // sibling slice accessor carries), a `[""] → []` collapse (which
12318        // would silently absorb the `NomeEmpty` refusal case at the
12319        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
12320        // would silently absorb the `DuplicateNome` refusal case at the
12321        // accessor boundary).
12322        for deps in [
12323            vec![],
12324            vec![Dep::simple("", "^0.1")],
12325            vec![Dep::simple("caixa-teia", "^0.1")],
12326            vec![
12327                Dep::simple("caixa-teia", "^0.1"),
12328                Dep::simple("caixa-core", "^0.1"),
12329            ],
12330            vec![
12331                Dep::simple("caixa-teia", "^0.1"),
12332                Dep::simple("caixa-teia", "^0.2"),
12333            ],
12334        ] {
12335            let c = caixa_with_deps(deps.clone());
12336            assert_eq!(
12337                c.deps(),
12338                deps.as_slice(),
12339                "Caixa::deps must return :deps verbatim (got {:?}, \
12340                 expected {deps:?})",
12341                c.deps(),
12342            );
12343            assert_eq!(
12344                c.deps(),
12345                c.deps.as_slice(),
12346                "Caixa::deps must element-equal the raw \
12347                 `self.deps.as_slice()` field access across every \
12348                 value in the Vec<Dep> accept-set",
12349            );
12350        }
12351    }
12352
12353    #[test]
12354    fn validate_deps_duplicate_arm_routes_through_accessor() {
12355        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
12356        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
12357        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
12358        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
12359        // "^0.2")], .. }` must surface the `DuplicateNome { list:
12360        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
12361        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
12362        // form) must pass validate. The pair jointly pins the accessor +
12363        // validate-gate composition: any future silent detour that had
12364        // the accessor return a dedupped slice on the `[a, a]` arm (a
12365        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
12366        // would silently absorb the `DuplicateNome` refusal at the
12367        // accessor boundary and the validate gate would accept a
12368        // struct-literal `Caixa` carrying the drift — the composition
12369        // pin catches that at caixa-core build time.
12370        //
12371        // Peer of the per-`Caixa`
12372        // `validate_autores_empty_entry_arm_routes_through_accessor`
12373        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12374        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12375        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
12376        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
12377        // (611f78b) accessor-composition pins on the sibling `&[T]`-
12378        // composition axes — same "the validate gate must route through
12379        // the substrate-primitive typed dispatch" discipline extended
12380        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
12381        // composition surface, opening the outer-`Caixa` dependency-slot
12382        // arm of the composition-pin family.
12383        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
12384        let err = c.validate_deps().unwrap_err();
12385        assert!(
12386            matches!(
12387                err,
12388                DepError::DuplicateNome { ref nome, list } if nome == "d"
12389                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
12390            ),
12391            "validate_deps must reject deps == \
12392             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
12393             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
12394             accessor and the validate gate must route through the \
12395             same substrate-primitive typed dispatch on the :deps \
12396             within-list duplicate arm (got {err:?})",
12397        );
12398        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
12399        assert!(
12400            c.validate_deps().is_ok(),
12401            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
12402             (the canonical single-entry form)",
12403        );
12404    }
12405
12406    #[test]
12407    fn deps_projects_slice_by_borrow() {
12408        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
12409        // — the returned slice borrows the underlying `Vec<Dep>` storage
12410        // of the `:deps` slot and the accessor must not clone the
12411        // backing `Vec` on every call. Peer of the per-`Caixa`
12412        // `autores_projects_slice_by_borrow` (b5d813f),
12413        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
12414        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
12415        // `exe_projects_slice_by_borrow` (65d9527), and
12416        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
12417        // on the sibling outer top-level [`Caixa`] `&[String]`-return
12418        // axes — the accessor's returned slice must borrow from `&self`
12419        // (the returned reference's lifetime is tied to `&self`), and
12420        // calling the accessor twice on the same [`Caixa`] must yield
12421        // slices that are pointer-equal (the underlying byte-buffer is
12422        // the storage `Vec`'s allocation, not a fresh copy) as well as
12423        // value-equal (idempotent, no side effects on `&self`).
12424        //
12425        // Pins against a future silent detour that returned an owned
12426        // `Vec<Dep>` (which would type-check but silently clone on
12427        // every call), a `&Vec<Dep>` return (which would leak the
12428        // backing `Vec`'s grow/push/reserve surface no downstream
12429        // consumer reaches for), or a one-arm-only accessor that
12430        // returned a saturating value on some sentinel input.
12431        for deps in [
12432            vec![],
12433            vec![Dep::simple("caixa-teia", "^0.1")],
12434            vec![
12435                Dep::simple("caixa-teia", "^0.1"),
12436                Dep::simple("caixa-core", "^0.1"),
12437            ],
12438        ] {
12439            let c = caixa_with_deps(deps.clone());
12440            let first = c.deps();
12441            let second = c.deps();
12442            assert_eq!(
12443                first, second,
12444                "Caixa::deps must be idempotent — two successive calls \
12445                 on the same &self must return the same &[Dep]",
12446            );
12447            assert_eq!(
12448                first.as_ptr(),
12449                second.as_ptr(),
12450                "Caixa::deps must borrow the underlying Vec<Dep> \
12451                 storage — two successive calls must return slices \
12452                 with the same backing pointer (a fresh Vec<Dep> clone \
12453                 would change the pointer on every call)",
12454            );
12455            assert_eq!(
12456                first,
12457                deps.as_slice(),
12458                "Caixa::deps must return :deps verbatim by borrow — \
12459                 got {first:?}, expected {deps:?}",
12460            );
12461        }
12462    }
12463
12464    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
12465
12466    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
12467        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12468        c.deps_dev = deps_dev;
12469        c
12470    }
12471
12472    #[test]
12473    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
12474        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
12475        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
12476        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
12477        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
12478        // access across every representative value in the accept-set —
12479        // `[]` (the "no dev deps declared" arm every existing fixture
12480        // without a `:deps-dev` line carries; the [`Caixa::template`]
12481        // scaffold emits `:deps-dev ()`), a canonical single-entry list
12482        // (the shape most consumer caixas carry — a `tatara-check` dev
12483        // pin), a canonical two-entry list (the multi-dev-dep closure),
12484        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
12485        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
12486        // `NomeInvalid` but the accessor must ship the raw slot
12487        // verbatim) and a `[a, a]` duplicate (validate rejects through
12488        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
12489        // ship the raw slot verbatim so struct-literal fixtures continue
12490        // to expose the duplicate at the accessor).
12491        //
12492        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
12493        // pin on the substrate primitive — closes the outer-`Caixa`
12494        // dependency-slot `&[Dep]` sub-family the sibling
12495        // `deps_returns_deps_slice_verbatim_across_permutations`
12496        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
12497        // slice" projection pattern onto the sibling dev-dep axis —
12498        // pins against a future silent detour that returned an owned
12499        // `Vec<Dep>` (which would type-check but silently clone on every
12500        // accessor call, breaking the zero-cost projection every peer
12501        // sibling slice accessor carries), a `[""] → []` collapse (which
12502        // would silently absorb the `NomeEmpty` refusal case at the
12503        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
12504        // would silently absorb the `DuplicateNome` refusal case at the
12505        // accessor boundary).
12506        for deps_dev in [
12507            vec![],
12508            vec![Dep::simple("", "^0.1")],
12509            vec![Dep::simple("tatara-check", "^0.1")],
12510            vec![
12511                Dep::simple("tatara-check", "^0.1"),
12512                Dep::simple("caixa-lint", "^0.1"),
12513            ],
12514            vec![
12515                Dep::simple("tatara-check", "^0.1"),
12516                Dep::simple("tatara-check", "^0.2"),
12517            ],
12518        ] {
12519            let c = caixa_with_deps_dev(deps_dev.clone());
12520            assert_eq!(
12521                c.deps_dev(),
12522                deps_dev.as_slice(),
12523                "Caixa::deps_dev must return :deps-dev verbatim (got \
12524                 {:?}, expected {deps_dev:?})",
12525                c.deps_dev(),
12526            );
12527            assert_eq!(
12528                c.deps_dev(),
12529                c.deps_dev.as_slice(),
12530                "Caixa::deps_dev must element-equal the raw \
12531                 `self.deps_dev.as_slice()` field access across every \
12532                 value in the Vec<Dep> accept-set",
12533            );
12534        }
12535    }
12536
12537    #[test]
12538    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
12539        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
12540        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
12541        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
12542        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
12543        // Dep::simple("d", "^0.2")], .. }` must surface the
12544        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
12545        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
12546        // canonical single-entry form) must pass validate. The pair
12547        // jointly pins the accessor + validate-gate composition: any
12548        // future silent detour that had the accessor return a dedupped
12549        // slice on the `[a, a]` arm (a
12550        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
12551        // would silently absorb the `DuplicateNome` refusal at the
12552        // accessor boundary and the validate gate would accept a
12553        // struct-literal `Caixa` carrying the drift — the composition
12554        // pin catches that at caixa-core build time.
12555        //
12556        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
12557        // (ad34b4e) on the sibling `:deps` axis — same "the validate
12558        // gate must route through the substrate-primitive typed
12559        // dispatch" discipline folded onto the sibling `:deps-dev`
12560        // axis, closing the two-list dep-graph composition-pin family.
12561        // The `:deps-dev` diagnostic must carry the
12562        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
12563        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
12564        // offending list unambiguously.
12565        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
12566        let err = c.validate_deps().unwrap_err();
12567        assert!(
12568            matches!(
12569                err,
12570                DepError::DuplicateNome { ref nome, list } if nome == "d"
12571                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
12572            ),
12573            "validate_deps must reject deps_dev == \
12574             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
12575             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
12576             accessor and the validate gate must route through the \
12577             same substrate-primitive typed dispatch on the :deps-dev \
12578             within-list duplicate arm (got {err:?})",
12579        );
12580        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
12581        assert!(
12582            c.validate_deps().is_ok(),
12583            "validate_deps must accept deps_dev == \
12584             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
12585        );
12586    }
12587
12588    #[test]
12589    fn deps_dev_projects_slice_by_borrow() {
12590        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
12591        // borrow — the returned slice borrows the underlying `Vec<Dep>`
12592        // storage of the `:deps-dev` slot and the accessor must not
12593        // clone the backing `Vec` on every call. Peer of
12594        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
12595        // `:deps` axis, and of the per-`Caixa`
12596        // `autores_projects_slice_by_borrow` (b5d813f),
12597        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
12598        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
12599        // `exe_projects_slice_by_borrow` (65d9527), and
12600        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
12601        // on the sibling outer top-level [`Caixa`] `&[String]`-return
12602        // axes — the accessor's returned slice must borrow from `&self`
12603        // (the returned reference's lifetime is tied to `&self`), and
12604        // calling the accessor twice on the same [`Caixa`] must yield
12605        // slices that are pointer-equal (the underlying byte-buffer is
12606        // the storage `Vec`'s allocation, not a fresh copy) as well as
12607        // value-equal (idempotent, no side effects on `&self`).
12608        //
12609        // Pins against a future silent detour that returned an owned
12610        // `Vec<Dep>` (which would type-check but silently clone on
12611        // every call), a `&Vec<Dep>` return (which would leak the
12612        // backing `Vec`'s grow/push/reserve surface no downstream
12613        // consumer reaches for), or a one-arm-only accessor that
12614        // returned a saturating value on some sentinel input.
12615        for deps_dev in [
12616            vec![],
12617            vec![Dep::simple("tatara-check", "^0.1")],
12618            vec![
12619                Dep::simple("tatara-check", "^0.1"),
12620                Dep::simple("caixa-lint", "^0.1"),
12621            ],
12622        ] {
12623            let c = caixa_with_deps_dev(deps_dev.clone());
12624            let first = c.deps_dev();
12625            let second = c.deps_dev();
12626            assert_eq!(
12627                first, second,
12628                "Caixa::deps_dev must be idempotent — two successive \
12629                 calls on the same &self must return the same &[Dep]",
12630            );
12631            assert_eq!(
12632                first.as_ptr(),
12633                second.as_ptr(),
12634                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
12635                 storage — two successive calls must return slices \
12636                 with the same backing pointer (a fresh Vec<Dep> clone \
12637                 would change the pointer on every call)",
12638            );
12639            assert_eq!(
12640                first,
12641                deps_dev.as_slice(),
12642                "Caixa::deps_dev must return :deps-dev verbatim by \
12643                 borrow — got {first:?}, expected {deps_dev:?}",
12644            );
12645        }
12646    }
12647
12648    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
12649
12650    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
12651        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12652        c.limits = limits;
12653        c
12654    }
12655
12656    #[test]
12657    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
12658        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
12659        // composite optional-composite-reference-shape pin:
12660        // [`Caixa::limits`] must return the `:limits` typed
12661        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
12662        // reference over the same backing storage the raw
12663        // `self.limits.as_ref()` field access borrows from, byte-equal
12664        // across every representative fixture in the accept-set — the
12665        // author-omitted `None` shape (the "engine-default applies"
12666        // partition every downstream Servico M2 overlay emitter treats
12667        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
12668        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
12669        // per-axis cap is `None`, so the peer M2 overlay emitter's
12670        // `.is_empty()`-gated projection still emits nothing but the
12671        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
12672        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
12673        // fixture (only `:memory` set — the canonical shape most
12674        // memory-heavy Servicos carry), and a fully-populated composite
12675        // (every per-axis cap set — the canonical shape a
12676        // sandboxed-by-default Servico carries).
12677        //
12678        // Pins against a future silent detour that returned a fresh-
12679        // cloned [`LimitsSpec`] copy (which would type-check via the
12680        // `Clone` impl but silently break every downstream caller that
12681        // relied on the reference sharing the composite's backing
12682        // identity), a reference to an operator-resolved overlay (the
12683        // future per-cluster `:limits-overrides` slot — its resolution
12684        // must land at exactly this accessor body, not silently divert
12685        // the raw slot away from a second consumer), a
12686        // `None` → `Some(LimitsSpec::default)` cluster-default
12687        // projection (which would collapse the load-bearing
12688        // "author-omitted `:limits` ⇒ engine-default applies" partition
12689        // the peer [`crate::render::servico_m2_overlay`] emitter and
12690        // the peer [`Caixa::declared_servico_slots`] enumerator both
12691        // read), or an axis-shuffled projection (a future detour that
12692        // swapped `memory` and `fuel` through the accessor would
12693        // silently split the paired [`crate::StandardLayout::verify`]
12694        // per-`:limits` shape gate's traversal input from the peer
12695        // `servico_m2_overlay` emitter's projection input).
12696        //
12697        // First outer top-level [`Caixa`] `Option<&Composite>`-return
12698        // composite-reference accessor pin on the substrate primitive
12699        // — opens the outer-`Caixa` `Option<&Composite>` composite-
12700        // reference projection pattern the sibling `:behavior`
12701        // [`crate::BehaviorSpec`] / `:politicas`
12702        // [`crate::aplicacao::MeshPolicy`] / `:placement`
12703        // [`crate::aplicacao::Placement`] / `:entrada`
12704        // [`crate::aplicacao::Entrada`] future outer-composite lifts
12705        // fold on. Peer of the closed M3 outer-composite family the
12706        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
12707        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
12708        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
12709        // reference accessor pins already carry on the outer
12710        // [`crate::AplicacaoSpec`] altitude — extends the outer-
12711        // accessor byte-equal-projection discipline onto the outer
12712        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
12713        use crate::LimitsSpec;
12714        use std::time::Duration;
12715        let fixtures: Vec<Option<LimitsSpec>> = vec![
12716            None,
12717            Some(LimitsSpec::default()),
12718            Some(LimitsSpec {
12719                memory: Some(64 * 1024 * 1024),
12720                ..Default::default()
12721            }),
12722            Some(LimitsSpec {
12723                memory: Some(64 * 1024 * 1024),
12724                fuel: Some(1_000_000),
12725                wall_clock: Some(Duration::from_secs(30)),
12726                cpu: Some(500),
12727            }),
12728        ];
12729        for limits in fixtures {
12730            let c = caixa_with_limits(limits.clone());
12731            assert_eq!(
12732                c.limits(),
12733                limits.as_ref(),
12734                "Caixa::limits must return :limits verbatim (got {:?}, \
12735                 expected {:?})",
12736                c.limits(),
12737                limits.as_ref(),
12738            );
12739            match (c.limits(), c.limits.as_ref()) {
12740                (Some(a), Some(b)) => assert!(
12741                    std::ptr::eq(a, b),
12742                    "Caixa::limits accessor and self.limits.as_ref() \
12743                     field access must borrow the same backing storage \
12744                     — the accessor is the substrate-primitive typed \
12745                     dispatch every downstream Servico-M2-overlay \
12746                     composite consumer must route through, and a \
12747                     reference-identity split would silently break \
12748                     every consumer that relied on the borrow sharing \
12749                     the composite's storage",
12750                ),
12751                (None, None) => {}
12752                _ => panic!(
12753                    "Caixa::limits presence bit must byte-equal \
12754                     self.limits.is_some() — a presence-bit drift would \
12755                     silently split the paired StandardLayout::verify \
12756                     per-`:limits` shape gate's traversal head from \
12757                     the peer render::servico_m2_overlay M2 overlay \
12758                     emitter's traversal head from the peer \
12759                     Caixa::declared_servico_slots M2 declared-slot \
12760                     enumerator's presence probe",
12761                ),
12762            }
12763            assert_eq!(
12764                c.limits().is_some(),
12765                c.limits.is_some(),
12766                "Caixa::limits().is_some() must byte-equal \
12767                 self.limits.is_some() — a presence-bit drift would \
12768                 silently split every downstream Option<&LimitsSpec> \
12769                 consumer's partition on the engine-default arm",
12770            );
12771        }
12772    }
12773
12774    #[test]
12775    fn declared_servico_slots_limits_arm_routes_through_accessor() {
12776        // Composition pin: [`Caixa::declared_servico_slots`]'s
12777        // `:limits` presence-probe arm must key off [`Caixa::limits`],
12778        // not the raw `self.limits.is_some()` field-probe. Structurally:
12779        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
12780        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
12781        // (the presence bit is `Some`, so the M2 kind-coherence gate
12782        // must surface the slot as "declared" even when every per-axis
12783        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
12784        // push the label (the "author omitted the slot entirely"
12785        // partition). The pair jointly pins the accessor + declared-
12786        // slot enumerator composition: any future silent detour that
12787        // had the accessor collapse `Some(LimitsSpec::default())` to
12788        // `None` (a `.filter(|l| !l.is_empty())` projection) would
12789        // silently absorb the "declared but empty" arm at the
12790        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
12791        // kind-coherence gate would silently accept a
12792        // struct-literal `Caixa` carrying the drift.
12793        //
12794        // Peer of the sibling per-`Caixa`
12795        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
12796        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
12797        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
12798        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
12799        // enumerator gate must route through the substrate-primitive
12800        // typed dispatch" discipline extended onto the outer top-level
12801        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
12802        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
12803        // composition-pin family.
12804        use crate::LimitsSpec;
12805        let c = caixa_with_limits(Some(LimitsSpec::default()));
12806        let slots = c.declared_servico_slots();
12807        assert!(
12808            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
12809            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
12810             when `:limits` is Some (even for LimitsSpec::default()) \
12811             — the accessor and the enumerator gate must route through \
12812             the same substrate-primitive typed dispatch on the outer \
12813             :limits presence bit (got slots={slots:?})",
12814        );
12815        let c = caixa_with_limits(None);
12816        let slots = c.declared_servico_slots();
12817        assert!(
12818            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
12819            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
12820             when `:limits` is None — the author-omitted arm must \
12821             route through the accessor's None-return unchanged (got \
12822             slots={slots:?})",
12823        );
12824    }
12825
12826    #[test]
12827    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
12828        // Composition pin: [`crate::render::servico_m2_overlay`]'s
12829        // per-`:limits` M2 overlay emit arm must key off
12830        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
12831        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
12832        // Some(64 MiB), .. default }), .. }` must surface the
12833        // `M2_KEY_LIMITS` key with the per-axis
12834        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
12835        // limits: Some(LimitsSpec::default()), .. }` must omit the
12836        // key entirely (the `.is_empty()`-gated inner arm elides an
12837        // empty composite even when the outer presence bit is `Some`),
12838        // and a `Caixa { limits: None, .. }` must also omit the key
12839        // (the "author omitted the slot entirely" partition). The
12840        // three-fixture family jointly pins the accessor + M2 overlay
12841        // emitter composition: any future silent detour that had the
12842        // accessor return a fresh-cloned copy on the `Some` arm (a
12843        // `LimitsSpec::clone()` projection) would silently break the
12844        // reference-identity pin the peer per-axis
12845        // `serde_yaml::to_value(limits)` projection reads from.
12846        use crate::LimitsSpec;
12847        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
12848        let c = caixa_with_limits(Some(LimitsSpec {
12849            memory: Some(64 * 1024 * 1024),
12850            ..Default::default()
12851        }));
12852        let overlay = servico_m2_overlay(&c).unwrap();
12853        assert!(
12854            overlay.contains_key(M2_KEY_LIMITS),
12855            "servico_m2_overlay must surface M2_KEY_LIMITS when \
12856             `:limits` carries a non-empty composite — the accessor \
12857             and the M2 overlay emitter must route through the same \
12858             substrate-primitive typed dispatch on the outer :limits \
12859             composite (got overlay={overlay:?})",
12860        );
12861        let c = caixa_with_limits(Some(LimitsSpec::default()));
12862        let overlay = servico_m2_overlay(&c).unwrap();
12863        assert!(
12864            !overlay.contains_key(M2_KEY_LIMITS),
12865            "servico_m2_overlay must omit M2_KEY_LIMITS when \
12866             `:limits` is Some(LimitsSpec::default()) — the empty \
12867             composite's `.is_empty()`-gated inner arm must elide \
12868             the key regardless of the outer presence bit (got \
12869             overlay={overlay:?})",
12870        );
12871        let c = caixa_with_limits(None);
12872        let overlay = servico_m2_overlay(&c).unwrap();
12873        assert!(
12874            !overlay.contains_key(M2_KEY_LIMITS),
12875            "servico_m2_overlay must omit M2_KEY_LIMITS when \
12876             `:limits` is None — the author-omitted arm must route \
12877             through the accessor's None-return unchanged (got \
12878             overlay={overlay:?})",
12879        );
12880    }
12881
12882    #[test]
12883    fn limits_projects_option_ref_by_borrow() {
12884        // The by-borrow pin: [`Caixa::limits`] returns
12885        // `Option<&LimitsSpec>` by borrow — the returned reference
12886        // borrows the underlying `Option<LimitsSpec>` storage of the
12887        // `:limits` slot and the accessor must not clone the backing
12888        // composite on every call. Peer of the sibling
12889        // `deps_projects_slice_by_borrow` (ad34b4e) /
12890        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
12891        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
12892        // extended here to the outer [`Caixa`] `Option<&Composite>`-
12893        // return axis: the accessor's returned reference must borrow
12894        // from `&self` (the returned reference's lifetime is tied to
12895        // `&self`), and calling the accessor twice on the same
12896        // [`Caixa`] must yield references that are pointer-equal (the
12897        // underlying byte-buffer is the storage `LimitsSpec`'s
12898        // allocation, not a fresh copy) as well as value-equal
12899        // (idempotent, no side effects on `&self`).
12900        //
12901        // Pins against a future silent detour that returned an owned
12902        // `LimitsSpec` (which would type-check via the `Clone` impl
12903        // but silently clone on every call), a `&LimitsSpec` panic-
12904        // return on the `None` arm (which would collapse the load-
12905        // bearing `Option` presence-bit into a runtime panic), or a
12906        // one-arm-only accessor that returned a saturating composite
12907        // on some sentinel input.
12908        use crate::LimitsSpec;
12909        use std::time::Duration;
12910        for limits in [
12911            Some(LimitsSpec::default()),
12912            Some(LimitsSpec {
12913                memory: Some(64 * 1024 * 1024),
12914                fuel: Some(1_000_000),
12915                wall_clock: Some(Duration::from_secs(30)),
12916                cpu: Some(500),
12917            }),
12918        ] {
12919            let c = caixa_with_limits(limits.clone());
12920            let first = c.limits().unwrap();
12921            let second = c.limits().unwrap();
12922            assert_eq!(
12923                first, second,
12924                "Caixa::limits must be idempotent — two successive \
12925                 calls on the same &self must return the same \
12926                 &LimitsSpec",
12927            );
12928            assert!(
12929                std::ptr::eq(first, second),
12930                "Caixa::limits must borrow the underlying \
12931                 Option<LimitsSpec> storage — two successive calls \
12932                 must return references with the same backing pointer \
12933                 (a fresh LimitsSpec clone would change the pointer \
12934                 on every call)",
12935            );
12936            assert_eq!(
12937                Some(first),
12938                limits.as_ref(),
12939                "Caixa::limits must return :limits verbatim by borrow \
12940                 — got {first:?}, expected {:?}",
12941                limits.as_ref(),
12942            );
12943        }
12944        let c = caixa_with_limits(None);
12945        assert!(
12946            c.limits().is_none(),
12947            "Caixa::limits must return None when :limits is absent — \
12948             the author-omitted arm must project through the \
12949             accessor's Option::None unchanged",
12950        );
12951    }
12952
12953    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
12954
12955    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
12956        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12957        c.behavior = behavior;
12958        c
12959    }
12960
12961    #[test]
12962    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
12963        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
12964        // composite optional-composite-reference-shape pin:
12965        // [`Caixa::behavior`] must return the `:behavior` typed
12966        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
12967        // reference over the same backing storage the raw
12968        // `self.behavior.as_ref()` field access borrows from, byte-equal
12969        // across every representative fixture in the accept-set — the
12970        // author-omitted `None` shape (the "runtime-default applies"
12971        // partition every downstream Servico M2 overlay emitter treats
12972        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
12973        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
12974        // every per-callback path is `None`, so the peer M2 overlay
12975        // emitter's `.is_empty()`-gated projection still emits nothing
12976        // but the outer presence-bit is `Some`, so
12977        // [`Caixa::declared_servico_slots`] still pushes the
12978        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
12979        // (only `:on-state-change` set — the canonical shape a caixa
12980        // that only wires the hot-upgrade migration path carries), and
12981        // a fully-populated composite (every per-callback path set —
12982        // the canonical shape a fully-instrumented gen_server-shaped
12983        // Servico carries).
12984        //
12985        // Peer of the sibling
12986        // `limits_returns_limits_option_ref_verbatim_across_permutations`
12987        // (b2bd9d7) opening fixture-family + reference-identity +
12988        // presence-bit tetrad pin on the outer top-level [`Caixa`]
12989        // `Option<&Composite>`-return sub-family — extended here to the
12990        // second axis of that sub-family so both of the currently-lifted
12991        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
12992        // `:behavior`) carry the same "byte-equal, borrow-shared,
12993        // presence-bit-preserved" outer-accessor discipline.
12994        //
12995        // Pins against a future silent detour that returned a fresh-
12996        // cloned [`crate::BehaviorSpec`] copy (which would type-check
12997        // via the `Clone` impl but silently break every downstream
12998        // caller that relied on the reference sharing the composite's
12999        // backing identity), a reference to an operator-resolved
13000        // overlay (a future per-cluster `:behavior-overrides` slot —
13001        // its resolution must land at exactly this accessor body, not
13002        // silently divert the raw slot away from a second consumer), a
13003        // `None` → `Some(BehaviorSpec::default)` cluster-default
13004        // projection (which would collapse the load-bearing
13005        // "author-omitted `:behavior` ⇒ runtime-default applies"
13006        // partition the peer [`crate::render::servico_m2_overlay`]
13007        // emitter, the peer [`Caixa::declared_servico_slots`]
13008        // enumerator, and the cross-slot
13009        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
13010        // gate all read), or a callback-shuffled projection (a future
13011        // detour that swapped `on_init` and `on_terminate` through the
13012        // accessor would silently split the paired
13013        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
13014        // traversal input from the peer `servico_m2_overlay` emitter's
13015        // projection input from the cross-slot `:state-change`
13016        // composition gate's traversal input).
13017        use crate::BehaviorSpec;
13018        use std::path::PathBuf;
13019        let fixtures: Vec<Option<BehaviorSpec>> = vec![
13020            None,
13021            Some(BehaviorSpec::default()),
13022            Some(BehaviorSpec {
13023                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13024                ..Default::default()
13025            }),
13026            Some(BehaviorSpec {
13027                on_init: Some(PathBuf::from("lib/init.lisp")),
13028                on_call: Some(PathBuf::from("lib/handlers.lisp")),
13029                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
13030                on_info: Some(PathBuf::from("lib/handlers.lisp")),
13031                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13032                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
13033            }),
13034        ];
13035        for behavior in fixtures {
13036            let c = caixa_with_behavior(behavior.clone());
13037            assert_eq!(
13038                c.behavior(),
13039                behavior.as_ref(),
13040                "Caixa::behavior must return :behavior verbatim (got \
13041                 {:?}, expected {:?})",
13042                c.behavior(),
13043                behavior.as_ref(),
13044            );
13045            match (c.behavior(), c.behavior.as_ref()) {
13046                (Some(a), Some(b)) => assert!(
13047                    std::ptr::eq(a, b),
13048                    "Caixa::behavior accessor and self.behavior.as_ref() \
13049                     field access must borrow the same backing storage \
13050                     — the accessor is the substrate-primitive typed \
13051                     dispatch every downstream Servico-M2-overlay \
13052                     composite consumer must route through, and a \
13053                     reference-identity split would silently break \
13054                     every consumer that relied on the borrow sharing \
13055                     the composite's storage",
13056                ),
13057                (None, None) => {}
13058                _ => panic!(
13059                    "Caixa::behavior presence bit must byte-equal \
13060                     self.behavior.is_some() — a presence-bit drift \
13061                     would silently split the paired \
13062                     StandardLayout::verify per-`:behavior` shape \
13063                     gate's traversal head from the peer \
13064                     render::servico_m2_overlay M2 overlay emitter's \
13065                     traversal head from the cross-slot \
13066                     validate_upgrade_from_against_behavior \
13067                     composition gate's traversal head from the peer \
13068                     Caixa::declared_servico_slots M2 declared-slot \
13069                     enumerator's presence probe",
13070                ),
13071            }
13072            assert_eq!(
13073                c.behavior().is_some(),
13074                c.behavior.is_some(),
13075                "Caixa::behavior().is_some() must byte-equal \
13076                 self.behavior.is_some() — a presence-bit drift would \
13077                 silently split every downstream Option<&BehaviorSpec> \
13078                 consumer's partition on the runtime-default arm",
13079            );
13080        }
13081    }
13082
13083    #[test]
13084    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
13085        // Composition pin: [`Caixa::declared_servico_slots`]'s
13086        // `:behavior` presence-probe arm must key off
13087        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
13088        // field-probe. Structurally: a `Caixa { behavior:
13089        // Some(BehaviorSpec::default()), .. }` must still push
13090        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
13091        // presence bit is `Some`, so the M2 kind-coherence gate must
13092        // surface the slot as "declared" even when every per-callback
13093        // path is unset), and a `Caixa { behavior: None, .. }` must
13094        // NOT push the label (the "author omitted the slot entirely"
13095        // partition). The pair jointly pins the accessor + declared-
13096        // slot enumerator composition: any future silent detour that
13097        // had the accessor collapse `Some(BehaviorSpec::default())`
13098        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
13099        // silently absorb the "declared but empty" arm at the
13100        // accessor boundary and the
13101        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
13102        // kind-coherence gate would silently accept a struct-literal
13103        // `Caixa` carrying the drift.
13104        //
13105        // Peer of the sibling
13106        // `declared_servico_slots_limits_arm_routes_through_accessor`
13107        // (b2bd9d7) composition pin on the sibling `:limits` outer-
13108        // `Option<&LimitsSpec>` arm of the same
13109        // [`Caixa::declared_servico_slots`] M2 declared-slot
13110        // enumerator's traversal — same "the enumerator gate must
13111        // route through the substrate-primitive typed dispatch"
13112        // discipline extended onto the outer top-level [`Caixa`]
13113        // `Option<&BehaviorSpec>`-composition surface.
13114        use crate::BehaviorSpec;
13115        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
13116        let slots = c.declared_servico_slots();
13117        assert!(
13118            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
13119            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
13120             when `:behavior` is Some (even for BehaviorSpec::default()) \
13121             — the accessor and the enumerator gate must route through \
13122             the same substrate-primitive typed dispatch on the outer \
13123             :behavior presence bit (got slots={slots:?})",
13124        );
13125        let c = caixa_with_behavior(None);
13126        let slots = c.declared_servico_slots();
13127        assert!(
13128            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
13129            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
13130             when `:behavior` is None — the author-omitted arm must \
13131             route through the accessor's None-return unchanged (got \
13132             slots={slots:?})",
13133        );
13134    }
13135
13136    #[test]
13137    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
13138        // Composition pin: [`crate::render::servico_m2_overlay`]'s
13139        // per-`:behavior` M2 overlay emit arm must key off
13140        // [`Caixa::behavior`], not the raw `&caixa.behavior`
13141        // field-borrow. Structurally: a `Caixa { behavior:
13142        // Some(BehaviorSpec { on_state_change: Some(...), .. default
13143        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
13144        // per-callback `onStateChange` sub-mapping in the overlay, a
13145        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
13146        // must omit the key entirely (the `.is_empty()`-gated inner
13147        // arm elides an empty composite even when the outer presence
13148        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
13149        // also omit the key (the "author omitted the slot entirely"
13150        // partition). The three-fixture family jointly pins the
13151        // accessor + M2 overlay emitter composition: any future
13152        // silent detour that had the accessor return a fresh-cloned
13153        // copy on the `Some` arm (a `BehaviorSpec::clone()`
13154        // projection) would silently break the reference-identity
13155        // pin the peer per-callback `serde_yaml::to_value(behavior)`
13156        // projection reads from.
13157        //
13158        // Peer of the sibling
13159        // `servico_m2_overlay_limits_arm_routes_through_accessor`
13160        // (b2bd9d7) composition pin on the sibling `:limits` outer-
13161        // `Option<&LimitsSpec>` arm of the same
13162        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
13163        // traversal — same "the emitter must route through the
13164        // substrate-primitive typed dispatch on the outer composite"
13165        // discipline extended onto the outer top-level [`Caixa`]
13166        // `Option<&BehaviorSpec>`-composition surface.
13167        use crate::BehaviorSpec;
13168        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
13169        use std::path::PathBuf;
13170        let c = caixa_with_behavior(Some(BehaviorSpec {
13171            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13172            ..Default::default()
13173        }));
13174        let overlay = servico_m2_overlay(&c).unwrap();
13175        assert!(
13176            overlay.contains_key(M2_KEY_BEHAVIOR),
13177            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
13178             `:behavior` carries a non-empty composite — the accessor \
13179             and the M2 overlay emitter must route through the same \
13180             substrate-primitive typed dispatch on the outer :behavior \
13181             composite (got overlay={overlay:?})",
13182        );
13183        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
13184        let overlay = servico_m2_overlay(&c).unwrap();
13185        assert!(
13186            !overlay.contains_key(M2_KEY_BEHAVIOR),
13187            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
13188             `:behavior` is Some(BehaviorSpec::default()) — the empty \
13189             composite's `.is_empty()`-gated inner arm must elide the \
13190             key regardless of the outer presence bit (got \
13191             overlay={overlay:?})",
13192        );
13193        let c = caixa_with_behavior(None);
13194        let overlay = servico_m2_overlay(&c).unwrap();
13195        assert!(
13196            !overlay.contains_key(M2_KEY_BEHAVIOR),
13197            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
13198             `:behavior` is None — the author-omitted arm must route \
13199             through the accessor's None-return unchanged (got \
13200             overlay={overlay:?})",
13201        );
13202    }
13203
13204    #[test]
13205    fn behavior_projects_option_ref_by_borrow() {
13206        // The by-borrow pin: [`Caixa::behavior`] returns
13207        // `Option<&BehaviorSpec>` by borrow — the returned reference
13208        // borrows the underlying `Option<BehaviorSpec>` storage of the
13209        // `:behavior` slot and the accessor must not clone the backing
13210        // composite on every call. Peer of the sibling
13211        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
13212        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
13213        // return sub-family — extended here to the second axis of the
13214        // same sub-family: the accessor's returned reference must
13215        // borrow from `&self` (the returned reference's lifetime is
13216        // tied to `&self`), and calling the accessor twice on the same
13217        // [`Caixa`] must yield references that are pointer-equal (the
13218        // underlying byte-buffer is the storage `BehaviorSpec`'s
13219        // allocation, not a fresh copy) as well as value-equal
13220        // (idempotent, no side effects on `&self`).
13221        //
13222        // Pins against a future silent detour that returned an owned
13223        // `BehaviorSpec` (which would type-check via the `Clone` impl
13224        // but silently clone on every call), a `&BehaviorSpec` panic-
13225        // return on the `None` arm (which would collapse the load-
13226        // bearing `Option` presence-bit into a runtime panic), or a
13227        // one-arm-only accessor that returned a saturating composite
13228        // on some sentinel input.
13229        use crate::BehaviorSpec;
13230        use std::path::PathBuf;
13231        for behavior in [
13232            Some(BehaviorSpec::default()),
13233            Some(BehaviorSpec {
13234                on_init: Some(PathBuf::from("lib/init.lisp")),
13235                on_call: Some(PathBuf::from("lib/handlers.lisp")),
13236                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
13237                on_info: Some(PathBuf::from("lib/handlers.lisp")),
13238                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13239                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
13240            }),
13241        ] {
13242            let c = caixa_with_behavior(behavior.clone());
13243            let first = c.behavior().unwrap();
13244            let second = c.behavior().unwrap();
13245            assert_eq!(
13246                first, second,
13247                "Caixa::behavior must be idempotent — two successive \
13248                 calls on the same &self must return the same \
13249                 &BehaviorSpec",
13250            );
13251            assert!(
13252                std::ptr::eq(first, second),
13253                "Caixa::behavior must borrow the underlying \
13254                 Option<BehaviorSpec> storage — two successive calls \
13255                 must return references with the same backing pointer \
13256                 (a fresh BehaviorSpec clone would change the pointer \
13257                 on every call)",
13258            );
13259            assert_eq!(
13260                Some(first),
13261                behavior.as_ref(),
13262                "Caixa::behavior must return :behavior verbatim by \
13263                 borrow — got {first:?}, expected {:?}",
13264                behavior.as_ref(),
13265            );
13266        }
13267        let c = caixa_with_behavior(None);
13268        assert!(
13269            c.behavior().is_none(),
13270            "Caixa::behavior must return None when :behavior is absent \
13271             — the author-omitted arm must project through the \
13272             accessor's Option::None unchanged",
13273        );
13274    }
13275
13276    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
13277
13278    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
13279        use crate::aplicacao::{Membro, WitContract};
13280        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13281        c.kind = CaixaKind::Aplicacao;
13282        c.membros = vec![Membro {
13283            caixa: "a".into(),
13284            versao: "^0.1".into(),
13285        }];
13286        c.contratos = vec![WitContract {
13287            de: "a".into(),
13288            para: "a".into(),
13289            wit: "wasi:http/proxy".into(),
13290            endpoint: Some("/x".into()),
13291            subject: None,
13292            slot: None,
13293        }];
13294        c.politicas = politicas;
13295        c
13296    }
13297
13298    #[test]
13299    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
13300        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
13301        // composite optional-composite-reference-shape pin:
13302        // [`Caixa::politicas`] must return the `:politicas` typed
13303        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
13304        // reference over the same backing storage the raw
13305        // `self.politicas.as_ref()` field access borrows from,
13306        // byte-equal across every representative fixture in the
13307        // accept-set — the author-omitted `None` shape (the "cluster-
13308        // default applies" partition every downstream mesh-artifact
13309        // emitter treats as "emit no `:politicas` overlay"), the
13310        // empty-composite `Some(MeshPolicy { .. default })` shape
13311        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
13312        // per-axis mesh-policy scalar is `None`, so the peer inner
13313        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
13314        // caixa-mesh overlay elides every per-axis emit but the outer
13315        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
13316        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
13317        // single-axis fixture (only `:timeout` set — the canonical
13318        // shape a latency-sensitive Aplicacao carries), and a
13319        // fully-populated composite (every per-axis mesh-policy
13320        // scalar set — the canonical shape a fully-governed
13321        // Aplicacao carries).
13322        //
13323        // Pins against a future silent detour that returned a fresh-
13324        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
13325        // type-check via the `Clone` impl but silently break every
13326        // downstream caller that relied on the reference sharing the
13327        // composite's backing identity), a reference to an operator-
13328        // resolved overlay (the future per-cluster
13329        // `:politicas-overrides` slot — its resolution must land at
13330        // exactly this accessor body, not silently divert the raw
13331        // slot away from the peer [`Caixa::declared_mesh_slots`]
13332        // enumerator's presence probe), a
13333        // `None` → `Some(MeshPolicy::default)` cluster-default
13334        // projection (which would collapse the load-bearing
13335        // "author-omitted `:politicas` ⇒ cluster-default applies"
13336        // partition the peer [`Caixa::declared_mesh_slots`]
13337        // enumerator and the peer [`Caixa::aplicacao_view`]
13338        // Aplicacao-composition seed both read), or an axis-shuffled
13339        // projection (a future detour that swapped `timeout` and
13340        // `retries` through the accessor would silently split the
13341        // paired [`Caixa::aplicacao_view`] seed's fold input from the
13342        // sibling M3 mesh-artifact emitter's projection input).
13343        //
13344        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
13345        // composite-reference accessor pin on the substrate primitive
13346        // — peer of the sibling
13347        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13348        // (b2bd9d7) and
13349        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
13350        // (35d8b52) opening tetrad pins on the outer top-level
13351        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
13352        // here to the first of the three M3 mesh-slot axes so the
13353        // opening third of the outer `Option<&Composite>` sub-family
13354        // carries the same "byte-equal, borrow-shared, presence-bit-
13355        // preserved" outer-accessor discipline.
13356        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
13357        use std::time::Duration;
13358        let fixtures: Vec<Option<MeshPolicy>> = vec![
13359            None,
13360            Some(MeshPolicy::default()),
13361            Some(MeshPolicy {
13362                timeout: Some(Duration::from_secs(30)),
13363                ..Default::default()
13364            }),
13365            Some(MeshPolicy {
13366                timeout: Some(Duration::from_secs(30)),
13367                retries: Some(3),
13368                circuit_breaker: Some(CircuitBreaker {
13369                    max_failures: 5,
13370                    window: Duration::from_secs(60),
13371                }),
13372                mtls_required: Some(true),
13373                rate_limit: Some(RateLimit {
13374                    rate: 100,
13375                    window: Duration::from_secs(1),
13376                }),
13377            }),
13378        ];
13379        for politicas in fixtures {
13380            let c = caixa_aplicacao_with_politicas(politicas.clone());
13381            assert_eq!(
13382                c.politicas(),
13383                politicas.as_ref(),
13384                "Caixa::politicas must return :politicas verbatim (got \
13385                 {:?}, expected {:?})",
13386                c.politicas(),
13387                politicas.as_ref(),
13388            );
13389            match (c.politicas(), c.politicas.as_ref()) {
13390                (Some(a), Some(b)) => assert!(
13391                    std::ptr::eq(a, b),
13392                    "Caixa::politicas accessor and self.politicas.as_ref() \
13393                     field access must borrow the same backing storage \
13394                     — the accessor is the substrate-primitive typed \
13395                     dispatch every downstream Aplicacao-mesh-overlay \
13396                     composite consumer must route through, and a \
13397                     reference-identity split would silently break \
13398                     every consumer that relied on the borrow sharing \
13399                     the composite's storage",
13400                ),
13401                (None, None) => {}
13402                _ => panic!(
13403                    "Caixa::politicas presence bit must byte-equal \
13404                     self.politicas.is_some() — a presence-bit drift \
13405                     would silently split the paired \
13406                     Caixa::aplicacao_view Aplicacao-composition seed's \
13407                     traversal head from the peer \
13408                     Caixa::declared_mesh_slots M3 declared-slot \
13409                     enumerator's presence probe",
13410                ),
13411            }
13412            assert_eq!(
13413                c.politicas().is_some(),
13414                c.politicas.is_some(),
13415                "Caixa::politicas().is_some() must byte-equal \
13416                 self.politicas.is_some() — a presence-bit drift would \
13417                 silently split every downstream Option<&MeshPolicy> \
13418                 consumer's partition on the cluster-default arm",
13419            );
13420        }
13421    }
13422
13423    #[test]
13424    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
13425        // Composition pin: [`Caixa::declared_mesh_slots`]'s
13426        // `:politicas` presence-probe arm must key off
13427        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
13428        // field-probe. Structurally: a `Caixa { politicas:
13429        // Some(MeshPolicy::default()), .. }` must still push
13430        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
13431        // presence bit is `Some`, so the M3 kind-coherence gate must
13432        // surface the slot as "declared" even when every per-axis
13433        // scalar is unset), and a `Caixa { politicas: None, .. }` must
13434        // NOT push the label (the "author omitted the slot entirely"
13435        // partition). The pair jointly pins the accessor + declared-
13436        // slot enumerator composition: any future silent detour that
13437        // had the accessor collapse `Some(MeshPolicy::default())` to
13438        // `None` (a `.filter(|p| !p.is_empty())` projection) would
13439        // silently absorb the "declared but empty" arm at the
13440        // accessor boundary and the
13441        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
13442        // coherence gate would silently accept a struct-literal
13443        // `Caixa` carrying the drift.
13444        //
13445        // Peer of the sibling
13446        // `declared_servico_slots_limits_arm_routes_through_accessor`
13447        // (b2bd9d7) and
13448        // `declared_servico_slots_behavior_arm_routes_through_accessor`
13449        // (35d8b52) composition pins on the sibling `:limits` /
13450        // `:behavior` outer-`Option<&Composite>` arms of the peer
13451        // [`Caixa::declared_servico_slots`] M2 declared-slot
13452        // enumerator's traversal — same "the enumerator gate must
13453        // route through the substrate-primitive typed dispatch"
13454        // discipline extended onto the outer top-level [`Caixa`] M3
13455        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
13456        // enumerator carries the same routing invariant as its M2
13457        // sibling.
13458        use crate::aplicacao::MeshPolicy;
13459        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
13460        let slots = c.declared_mesh_slots();
13461        assert!(
13462            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
13463            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
13464             when `:politicas` is Some (even for MeshPolicy::default()) \
13465             — the accessor and the enumerator gate must route through \
13466             the same substrate-primitive typed dispatch on the outer \
13467             :politicas presence bit (got slots={slots:?})",
13468        );
13469        let c = caixa_aplicacao_with_politicas(None);
13470        let slots = c.declared_mesh_slots();
13471        assert!(
13472            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
13473            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
13474             when `:politicas` is None — the author-omitted arm must \
13475             route through the accessor's None-return unchanged (got \
13476             slots={slots:?})",
13477        );
13478    }
13479
13480    #[test]
13481    fn aplicacao_view_politicas_arm_folds_through_accessor() {
13482        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
13483        // Aplicacao-composition seed must fold through
13484        // [`Caixa::politicas`], not the raw
13485        // `self.politicas.clone().unwrap_or_default()` field-borrow.
13486        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
13487        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
13488        // must surface a projected [`crate::AplicacaoSpec`] whose
13489        // `politicas().timeout()` field byte-equals the outer
13490        // composite's `timeout` scalar (the fold must project the
13491        // authored composite verbatim), a `Caixa { politicas:
13492        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
13493        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
13494        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
13495        // fold's empty-composite arm collapses to the same default the
13496        // author-omitted arm does), and a `Caixa { politicas: None,
13497        // kind: Aplicacao, .. }` must surface an
13498        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
13499        // [`crate::aplicacao::MeshPolicy::default`] (the "author
13500        // omitted the slot entirely" arm folds through the
13501        // `unwrap_or_default` onto the cluster-default). The triad
13502        // jointly pins the accessor + Aplicacao-composition seed
13503        // composition: any future silent detour that had the accessor
13504        // divert the raw slot away from the seed's fold (an operator-
13505        // resolved overlay's default-fold arm silently differing from
13506        // the raw slot's default-fold arm) would silently split the
13507        // build-time mesh-artifact emission gate from the caixa-mesh
13508        // renderer's Aplicacao-view input at the composition boundary.
13509        use crate::aplicacao::MeshPolicy;
13510        use std::time::Duration;
13511        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
13512            timeout: Some(Duration::from_secs(30)),
13513            ..Default::default()
13514        }));
13515        let view = c.aplicacao_view().unwrap();
13516        assert_eq!(
13517            view.politicas().timeout(),
13518            Some(Duration::from_secs(30)),
13519            "Caixa::aplicacao_view must fold the authored :politicas \
13520             :timeout scalar through the accessor verbatim onto the \
13521             projected AplicacaoSpec — a future silent detour at the \
13522             seed's fold arm would surface here as a projected-scalar \
13523             drift (got {:?})",
13524            view.politicas().timeout(),
13525        );
13526        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
13527        let view = c.aplicacao_view().unwrap();
13528        assert_eq!(
13529            view.politicas(),
13530            &MeshPolicy::default(),
13531            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
13532             through the accessor onto MeshPolicy::default — the empty- \
13533             composite arm collapses to the same default the author- \
13534             omitted arm does (got {:?})",
13535            view.politicas(),
13536        );
13537        let c = caixa_aplicacao_with_politicas(None);
13538        let view = c.aplicacao_view().unwrap();
13539        assert_eq!(
13540            view.politicas(),
13541            &MeshPolicy::default(),
13542            "Caixa::aplicacao_view must fold None through the accessor's \
13543             unwrap_or_default onto MeshPolicy::default — the author- \
13544             omitted arm must route through the accessor's None-return \
13545             unchanged (got {:?})",
13546            view.politicas(),
13547        );
13548    }
13549
13550    #[test]
13551    fn politicas_projects_option_ref_by_borrow() {
13552        // The by-borrow pin: [`Caixa::politicas`] returns
13553        // `Option<&MeshPolicy>` by borrow — the returned reference
13554        // borrows the underlying `Option<MeshPolicy>` storage of the
13555        // `:politicas` slot and the accessor must not clone the
13556        // backing composite on every call. Peer of the sibling
13557        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
13558        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
13559        // pins on the outer top-level [`Caixa`]
13560        // `Option<&Composite>`-return sub-family — extended here to
13561        // the third axis of the same sub-family: the accessor's
13562        // returned reference must borrow from `&self` (the returned
13563        // reference's lifetime is tied to `&self`), and calling the
13564        // accessor twice on the same [`Caixa`] must yield references
13565        // that are pointer-equal (the underlying byte-buffer is the
13566        // storage `MeshPolicy`'s allocation, not a fresh copy) as
13567        // well as value-equal (idempotent, no side effects on
13568        // `&self`).
13569        //
13570        // Pins against a future silent detour that returned an owned
13571        // `MeshPolicy` (which would type-check via the `Clone` impl
13572        // but silently clone on every call), a `&MeshPolicy` panic-
13573        // return on the `None` arm (which would collapse the load-
13574        // bearing `Option` presence-bit into a runtime panic), or a
13575        // one-arm-only accessor that returned a saturating composite
13576        // on some sentinel input.
13577        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
13578        use std::time::Duration;
13579        for politicas in [
13580            Some(MeshPolicy::default()),
13581            Some(MeshPolicy {
13582                timeout: Some(Duration::from_secs(30)),
13583                retries: Some(3),
13584                circuit_breaker: Some(CircuitBreaker {
13585                    max_failures: 5,
13586                    window: Duration::from_secs(60),
13587                }),
13588                mtls_required: Some(true),
13589                rate_limit: Some(RateLimit {
13590                    rate: 100,
13591                    window: Duration::from_secs(1),
13592                }),
13593            }),
13594        ] {
13595            let c = caixa_aplicacao_with_politicas(politicas.clone());
13596            let first = c.politicas().unwrap();
13597            let second = c.politicas().unwrap();
13598            assert_eq!(
13599                first, second,
13600                "Caixa::politicas must be idempotent — two successive \
13601                 calls on the same &self must return the same \
13602                 &MeshPolicy",
13603            );
13604            assert!(
13605                std::ptr::eq(first, second),
13606                "Caixa::politicas must borrow the underlying \
13607                 Option<MeshPolicy> storage — two successive calls \
13608                 must return references with the same backing pointer \
13609                 (a fresh MeshPolicy clone would change the pointer on \
13610                 every call)",
13611            );
13612            assert_eq!(
13613                Some(first),
13614                politicas.as_ref(),
13615                "Caixa::politicas must return :politicas verbatim by \
13616                 borrow — got {first:?}, expected {:?}",
13617                politicas.as_ref(),
13618            );
13619        }
13620        let c = caixa_aplicacao_with_politicas(None);
13621        assert!(
13622            c.politicas().is_none(),
13623            "Caixa::politicas must return None when :politicas is \
13624             absent — the author-omitted arm must project through the \
13625             accessor's Option::None unchanged",
13626        );
13627    }
13628
13629    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
13630
13631    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
13632        use crate::aplicacao::{Membro, WitContract};
13633        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13634        c.kind = CaixaKind::Aplicacao;
13635        c.membros = vec![Membro {
13636            caixa: "a".into(),
13637            versao: "^0.1".into(),
13638        }];
13639        c.contratos = vec![WitContract {
13640            de: "a".into(),
13641            para: "a".into(),
13642            wit: "wasi:http/proxy".into(),
13643            endpoint: Some("/x".into()),
13644            subject: None,
13645            slot: None,
13646        }];
13647        c.placement = placement;
13648        c
13649    }
13650
13651    #[test]
13652    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
13653        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
13654        // composite optional-composite-reference-shape pin:
13655        // [`Caixa::placement`] must return the `:placement` typed
13656        // `Option<Placement>` verbatim as an `Option<&Placement>`
13657        // reference over the same backing storage the raw
13658        // `self.placement.as_ref()` field access borrows from,
13659        // byte-equal across every representative fixture in the
13660        // accept-set — the author-omitted `None` shape (the
13661        // "cluster-default applies" partition every downstream mesh-
13662        // artifact emitter treats as "emit no `:placement` overlay"),
13663        // the empty-composite `Some(Placement { .. default })` shape
13664        // (`estrategia: SingleNode`, empty clusters, no shard-key /
13665        // affinity — the outer presence-bit is `Some` so
13666        // [`Caixa::declared_mesh_slots`] still pushes the
13667        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
13668        // `Replicated`-on-two-clusters fixture (the canonical shape a
13669        // stateless HTTP Aplicacao carries), and a fully-populated
13670        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
13671        // shape a stateful Akka-style cluster-sharding Aplicacao
13672        // carries).
13673        //
13674        // Pins against a future silent detour that returned a fresh-
13675        // cloned [`crate::aplicacao::Placement`] copy (which would
13676        // type-check via the `Clone` impl but silently break every
13677        // downstream caller that relied on the reference sharing the
13678        // composite's backing identity), a reference to an operator-
13679        // resolved overlay (the future per-cluster
13680        // `:placement-overrides` slot — its resolution must land at
13681        // exactly this accessor body, not silently divert the raw
13682        // slot away from the peer [`Caixa::declared_mesh_slots`]
13683        // enumerator's presence probe), a `None` →
13684        // `Some(Placement::default)` cluster-default projection (which
13685        // would collapse the load-bearing "author-omitted `:placement`
13686        // ⇒ cluster-default applies" partition the peer
13687        // [`Caixa::declared_mesh_slots`] enumerator and the peer
13688        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
13689        // read), or an axis-shuffled projection (a future detour that
13690        // swapped `clusters` and `affinity` through the accessor would
13691        // silently split the paired [`Caixa::aplicacao_view`] seed's
13692        // fold input from the sibling M3 mesh-artifact emitter's
13693        // projection input).
13694        //
13695        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
13696        // composite-reference accessor pin on the substrate primitive
13697        // — peer of the sibling
13698        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13699        // (b2bd9d7),
13700        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
13701        // (35d8b52), and
13702        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
13703        // (5d23d29) opening triad pins on the outer top-level
13704        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
13705        // here to the second of the three M3 mesh-slot axes so the
13706        // opening four-fifths of the outer `Option<&Composite>` sub-
13707        // family carries the same "byte-equal, borrow-shared,
13708        // presence-bit-preserved" outer-accessor discipline.
13709        use crate::aplicacao::{Placement, PlacementStrategy};
13710        let fixtures: Vec<Option<Placement>> = vec![
13711            None,
13712            Some(Placement::default()),
13713            Some(Placement {
13714                estrategia: PlacementStrategy::Replicated,
13715                clusters: vec!["rio".into(), "sao-paulo".into()],
13716                affinity: None,
13717                shard_key: None,
13718            }),
13719            Some(Placement {
13720                estrategia: PlacementStrategy::Sharded,
13721                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
13722                affinity: Some("data-locality".into()),
13723                shard_key: Some("$tenantId".into()),
13724            }),
13725        ];
13726        for placement in fixtures {
13727            let c = caixa_aplicacao_with_placement(placement.clone());
13728            assert_eq!(
13729                c.placement(),
13730                placement.as_ref(),
13731                "Caixa::placement must return :placement verbatim (got \
13732                 {:?}, expected {:?})",
13733                c.placement(),
13734                placement.as_ref(),
13735            );
13736            match (c.placement(), c.placement.as_ref()) {
13737                (Some(a), Some(b)) => assert!(
13738                    std::ptr::eq(a, b),
13739                    "Caixa::placement accessor and self.placement.as_ref() \
13740                     field access must borrow the same backing storage \
13741                     — the accessor is the substrate-primitive typed \
13742                     dispatch every downstream Aplicacao-distribution- \
13743                     overlay composite consumer must route through, and \
13744                     a reference-identity split would silently break \
13745                     every consumer that relied on the borrow sharing \
13746                     the composite's storage",
13747                ),
13748                (None, None) => {}
13749                _ => panic!(
13750                    "Caixa::placement presence bit must byte-equal \
13751                     self.placement.is_some() — a presence-bit drift \
13752                     would silently split the paired \
13753                     Caixa::aplicacao_view Aplicacao-composition seed's \
13754                     traversal head from the peer \
13755                     Caixa::declared_mesh_slots M3 declared-slot \
13756                     enumerator's presence probe",
13757                ),
13758            }
13759            assert_eq!(
13760                c.placement().is_some(),
13761                c.placement.is_some(),
13762                "Caixa::placement().is_some() must byte-equal \
13763                 self.placement.is_some() — a presence-bit drift would \
13764                 silently split every downstream Option<&Placement> \
13765                 consumer's partition on the cluster-default arm",
13766            );
13767        }
13768    }
13769
13770    #[test]
13771    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
13772        // Composition pin: [`Caixa::declared_mesh_slots`]'s
13773        // `:placement` presence-probe arm must key off
13774        // [`Caixa::placement`], not the raw `self.placement.is_some()`
13775        // field-probe. Structurally: a `Caixa { placement:
13776        // Some(Placement::default()), .. }` must still push
13777        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
13778        // presence bit is `Some`, so the M3 kind-coherence gate must
13779        // surface the slot as "declared" even when every per-axis
13780        // scalar defers to the cluster-default arm), and a `Caixa {
13781        // placement: None, .. }` must NOT push the label (the "author
13782        // omitted the slot entirely" partition). The pair jointly pins
13783        // the accessor + declared-slot enumerator composition: any
13784        // future silent detour that had the accessor collapse
13785        // `Some(Placement::default())` to `None` (a `.filter(|p|
13786        // p.clusters().is_empty().not())` projection) would silently
13787        // absorb the "declared but empty" arm at the accessor boundary
13788        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
13789        // kind-coherence gate would silently accept a struct-literal
13790        // `Caixa` carrying the drift.
13791        //
13792        // Peer of the sibling
13793        // `declared_servico_slots_limits_arm_routes_through_accessor`
13794        // (b2bd9d7),
13795        // `declared_servico_slots_behavior_arm_routes_through_accessor`
13796        // (35d8b52), and
13797        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
13798        // (5d23d29) composition pins on the sibling `:limits` /
13799        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
13800        // — same "the enumerator gate must route through the
13801        // substrate-primitive typed dispatch" discipline extended onto
13802        // the second of the three M3 mesh-slot axes so the
13803        // [`Caixa::declared_mesh_slots`] enumerator carries the same
13804        // routing invariant on the `:placement` arm as the peer
13805        // `:politicas` arm.
13806        use crate::aplicacao::Placement;
13807        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
13808        let slots = c.declared_mesh_slots();
13809        assert!(
13810            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
13811            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
13812             when `:placement` is Some (even for Placement::default()) \
13813             — the accessor and the enumerator gate must route through \
13814             the same substrate-primitive typed dispatch on the outer \
13815             :placement presence bit (got slots={slots:?})",
13816        );
13817        let c = caixa_aplicacao_with_placement(None);
13818        let slots = c.declared_mesh_slots();
13819        assert!(
13820            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
13821            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
13822             when `:placement` is None — the author-omitted arm must \
13823             route through the accessor's None-return unchanged (got \
13824             slots={slots:?})",
13825        );
13826    }
13827
13828    #[test]
13829    fn aplicacao_view_placement_arm_folds_through_accessor() {
13830        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
13831        // Aplicacao-composition seed must fold through
13832        // [`Caixa::placement`], not the raw
13833        // `self.placement.clone().unwrap_or_default()` field-borrow.
13834        // Structurally: a `Caixa { placement: Some(Placement {
13835        // estrategia: Replicated, clusters: ["rio"], .. default }),
13836        // kind: Aplicacao, .. }` must surface a projected
13837        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
13838        // `placement().clusters()` byte-equal the outer composite's
13839        // authored values (the fold must project the authored
13840        // composite verbatim), a `Caixa { placement:
13841        // Some(Placement::default()), kind: Aplicacao, .. }` must
13842        // surface an [`crate::AplicacaoSpec`] whose `placement()`
13843        // byte-equals [`crate::aplicacao::Placement::default`] (the
13844        // fold's empty-composite arm collapses to the same default
13845        // the author-omitted arm does), and a `Caixa { placement:
13846        // None, kind: Aplicacao, .. }` must surface an
13847        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
13848        // [`crate::aplicacao::Placement::default`] (the "author
13849        // omitted the slot entirely" arm folds through the
13850        // `unwrap_or_default` onto the cluster-default). The triad
13851        // jointly pins the accessor + Aplicacao-composition seed
13852        // composition: any future silent detour that had the accessor
13853        // divert the raw slot away from the seed's fold (an operator-
13854        // resolved overlay's default-fold arm silently differing from
13855        // the raw slot's default-fold arm) would silently split the
13856        // build-time distribution-artifact emission gate from the
13857        // caixa-mesh renderer's Aplicacao-view input at the
13858        // composition boundary.
13859        use crate::aplicacao::{Placement, PlacementStrategy};
13860        let c = caixa_aplicacao_with_placement(Some(Placement {
13861            estrategia: PlacementStrategy::Replicated,
13862            clusters: vec!["rio".into()],
13863            affinity: None,
13864            shard_key: None,
13865        }));
13866        let view = c.aplicacao_view().unwrap();
13867        assert_eq!(
13868            view.placement().estrategia(),
13869            PlacementStrategy::Replicated,
13870            "Caixa::aplicacao_view must fold the authored :placement \
13871             :estrategia scalar through the accessor verbatim onto the \
13872             projected AplicacaoSpec — a future silent detour at the \
13873             seed's fold arm would surface here as a projected-scalar \
13874             drift (got {:?})",
13875            view.placement().estrategia(),
13876        );
13877        assert_eq!(
13878            view.placement().clusters(),
13879            &["rio"],
13880            "Caixa::aplicacao_view must fold the authored :placement \
13881             :clusters list through the accessor verbatim onto the \
13882             projected AplicacaoSpec — a future silent detour at the \
13883             seed's fold arm would surface here as a projected-list \
13884             drift (got {:?})",
13885            view.placement().clusters(),
13886        );
13887        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
13888        let view = c.aplicacao_view().unwrap();
13889        assert_eq!(
13890            view.placement(),
13891            &Placement::default(),
13892            "Caixa::aplicacao_view must fold Some(Placement::default()) \
13893             through the accessor onto Placement::default — the empty- \
13894             composite arm collapses to the same default the author- \
13895             omitted arm does (got {:?})",
13896            view.placement(),
13897        );
13898        let c = caixa_aplicacao_with_placement(None);
13899        let view = c.aplicacao_view().unwrap();
13900        assert_eq!(
13901            view.placement(),
13902            &Placement::default(),
13903            "Caixa::aplicacao_view must fold None through the accessor's \
13904             unwrap_or_default onto Placement::default — the author- \
13905             omitted arm must route through the accessor's None-return \
13906             unchanged (got {:?})",
13907            view.placement(),
13908        );
13909    }
13910
13911    #[test]
13912    fn placement_projects_option_ref_by_borrow() {
13913        // The by-borrow pin: [`Caixa::placement`] returns
13914        // `Option<&Placement>` by borrow — the returned reference
13915        // borrows the underlying `Option<Placement>` storage of the
13916        // `:placement` slot and the accessor must not clone the
13917        // backing composite on every call. Peer of the sibling
13918        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
13919        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
13920        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
13921        // pins on the outer top-level [`Caixa`]
13922        // `Option<&Composite>`-return sub-family — extended here to
13923        // the fourth axis of the same sub-family: the accessor's
13924        // returned reference must borrow from `&self` (the returned
13925        // reference's lifetime is tied to `&self`), and calling the
13926        // accessor twice on the same [`Caixa`] must yield references
13927        // that are pointer-equal (the underlying byte-buffer is the
13928        // storage `Placement`'s allocation, not a fresh copy) as well
13929        // as value-equal (idempotent, no side effects on `&self`).
13930        //
13931        // Pins against a future silent detour that returned an owned
13932        // `Placement` (which would type-check via the `Clone` impl
13933        // but silently clone on every call), a `&Placement` panic-
13934        // return on the `None` arm (which would collapse the load-
13935        // bearing `Option` presence-bit into a runtime panic), or a
13936        // one-arm-only accessor that returned a saturating composite
13937        // on some sentinel input.
13938        use crate::aplicacao::{Placement, PlacementStrategy};
13939        for placement in [
13940            Some(Placement::default()),
13941            Some(Placement {
13942                estrategia: PlacementStrategy::Sharded,
13943                clusters: vec!["rio".into(), "sao-paulo".into()],
13944                affinity: Some("data-locality".into()),
13945                shard_key: Some("$tenantId".into()),
13946            }),
13947        ] {
13948            let c = caixa_aplicacao_with_placement(placement.clone());
13949            let first = c.placement().unwrap();
13950            let second = c.placement().unwrap();
13951            assert_eq!(
13952                first, second,
13953                "Caixa::placement must be idempotent — two successive \
13954                 calls on the same &self must return the same \
13955                 &Placement",
13956            );
13957            assert!(
13958                std::ptr::eq(first, second),
13959                "Caixa::placement must borrow the underlying \
13960                 Option<Placement> storage — two successive calls \
13961                 must return references with the same backing pointer \
13962                 (a fresh Placement clone would change the pointer on \
13963                 every call)",
13964            );
13965            assert_eq!(
13966                Some(first),
13967                placement.as_ref(),
13968                "Caixa::placement must return :placement verbatim by \
13969                 borrow — got {first:?}, expected {:?}",
13970                placement.as_ref(),
13971            );
13972        }
13973        let c = caixa_aplicacao_with_placement(None);
13974        assert!(
13975            c.placement().is_none(),
13976            "Caixa::placement must return None when :placement is \
13977             absent — the author-omitted arm must project through the \
13978             accessor's Option::None unchanged",
13979        );
13980    }
13981
13982    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
13983
13984    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
13985        use crate::aplicacao::{Membro, WitContract};
13986        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13987        c.kind = CaixaKind::Aplicacao;
13988        c.membros = vec![Membro {
13989            caixa: "a".into(),
13990            versao: "^0.1".into(),
13991        }];
13992        c.contratos = vec![WitContract {
13993            de: "a".into(),
13994            para: "a".into(),
13995            wit: "wasi:http/proxy".into(),
13996            endpoint: Some("/x".into()),
13997            subject: None,
13998            slot: None,
13999        }];
14000        c.entrada = entrada;
14001        c
14002    }
14003
14004    #[test]
14005    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
14006        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
14007        // composite optional-composite-reference-shape pin:
14008        // [`Caixa::entrada`] must return the `:entrada` typed
14009        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
14010        // reference over the same backing storage the raw
14011        // `self.entrada.as_ref()` field access borrows from,
14012        // byte-equal across every representative fixture in the
14013        // accept-set — the author-omitted `None` shape (the
14014        // "cluster-internal Aplicacao" partition every downstream
14015        // Gateway-API emitter treats as "emit no listener + no
14016        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
14017        // (empty `paths` — the resolved-paths fallback the peer
14018        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
14019        // onto the substrate catch-all), and a fully-populated
14020        // multi-path-with-non-default-port fixture (the canonical
14021        // shape a public HTTP Aplicacao carries).
14022        //
14023        // Pins against a future silent detour that returned a fresh-
14024        // cloned [`crate::aplicacao::Entrada`] copy (which would
14025        // type-check via the `Clone` impl but silently break every
14026        // downstream caller that relied on the reference sharing the
14027        // composite's backing identity), a reference to an operator-
14028        // resolved overlay (the future per-cluster
14029        // `:entrada-overrides` slot — its resolution must land at
14030        // exactly this accessor body, not silently divert the raw
14031        // slot away from the peer [`Caixa::declared_mesh_slots`]
14032        // enumerator's presence probe), or an axis-shuffled projection
14033        // (a future detour that swapped `host` and `para` through the
14034        // accessor would silently split the paired
14035        // [`Caixa::aplicacao_view`] seed's forward input from the
14036        // sibling M3 gateway-artifact emitter's projection input).
14037        //
14038        // Fifth and final outer top-level [`Caixa`]
14039        // `Option<&Composite>`-return composite-reference accessor pin
14040        // on the substrate primitive — peer of the sibling
14041        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14042        // (b2bd9d7),
14043        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14044        // (35d8b52),
14045        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
14046        // (5d23d29), and
14047        // `placement_returns_placement_option_ref_verbatim_across_permutations`
14048        // (4fb8074) opening tetrad pins on the outer top-level
14049        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14050        // here to the third and final M3 mesh-slot axis so the closed
14051        // outer `Option<&Composite>` sub-family carries the same
14052        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
14053        // accessor discipline across all five arms.
14054        use crate::aplicacao::Entrada;
14055        let fixtures: Vec<Option<Entrada>> = vec![
14056            None,
14057            Some(Entrada {
14058                host: "checkout.quero.cloud".into(),
14059                para: "gateway".into(),
14060                paths: Vec::new(),
14061                port: crate::DEFAULT_SERVICO_PORT,
14062            }),
14063            Some(Entrada {
14064                host: "api.pleme.io".into(),
14065                para: "public-api".into(),
14066                paths: vec!["/v1".into(), "/v2".into()],
14067                port: 8080,
14068            }),
14069        ];
14070        for entrada in fixtures {
14071            let c = caixa_aplicacao_with_entrada(entrada.clone());
14072            assert_eq!(
14073                c.entrada(),
14074                entrada.as_ref(),
14075                "Caixa::entrada must return :entrada verbatim (got \
14076                 {:?}, expected {:?})",
14077                c.entrada(),
14078                entrada.as_ref(),
14079            );
14080            match (c.entrada(), c.entrada.as_ref()) {
14081                (Some(a), Some(b)) => assert!(
14082                    std::ptr::eq(a, b),
14083                    "Caixa::entrada accessor and self.entrada.as_ref() \
14084                     field access must borrow the same backing storage \
14085                     — the accessor is the substrate-primitive typed \
14086                     dispatch every downstream Aplicacao-external- \
14087                     gateway composite consumer must route through, and \
14088                     a reference-identity split would silently break \
14089                     every consumer that relied on the borrow sharing \
14090                     the composite's storage",
14091                ),
14092                (None, None) => {}
14093                _ => panic!(
14094                    "Caixa::entrada presence bit must byte-equal \
14095                     self.entrada.is_some() — a presence-bit drift \
14096                     would silently split the paired \
14097                     Caixa::aplicacao_view Aplicacao-composition seed's \
14098                     traversal head from the peer \
14099                     Caixa::declared_mesh_slots M3 declared-slot \
14100                     enumerator's presence probe",
14101                ),
14102            }
14103            assert_eq!(
14104                c.entrada().is_some(),
14105                c.entrada.is_some(),
14106                "Caixa::entrada().is_some() must byte-equal \
14107                 self.entrada.is_some() — a presence-bit drift would \
14108                 silently split every downstream Option<&Entrada> \
14109                 consumer's partition on the cluster-internal arm",
14110            );
14111        }
14112    }
14113
14114    #[test]
14115    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
14116        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
14117        // presence-probe arm must key off [`Caixa::entrada`], not the
14118        // raw `self.entrada.is_some()` field-probe. Structurally: a
14119        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
14120        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
14121        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
14122        // presence bit is `Some`, so the M3 kind-coherence gate must
14123        // surface the slot as "declared" even when every per-axis
14124        // scalar defers to the substrate catch-all / default port),
14125        // and a `Caixa { entrada: None, .. }` must NOT push the label
14126        // (the "author omitted the slot entirely" partition). The pair
14127        // jointly pins the accessor + declared-slot enumerator
14128        // composition: any future silent detour that had the accessor
14129        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
14130        // `.filter(|e| !e.paths.is_empty())` projection) would silently
14131        // absorb the "declared but empty-paths" arm at the accessor
14132        // boundary and the
14133        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
14134        // coherence gate would silently accept a struct-literal
14135        // `Caixa` carrying the drift.
14136        //
14137        // Peer of the sibling
14138        // `declared_servico_slots_limits_arm_routes_through_accessor`
14139        // (b2bd9d7),
14140        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14141        // (35d8b52),
14142        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
14143        // (5d23d29), and
14144        // `declared_mesh_slots_placement_arm_routes_through_accessor`
14145        // (4fb8074) composition pins on the sibling `:limits` /
14146        // `:behavior` / `:politicas` / `:placement` outer-
14147        // `Option<&Composite>` arms — same "the enumerator gate must
14148        // route through the substrate-primitive typed dispatch"
14149        // discipline extended onto the third and final M3 mesh-slot
14150        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
14151        // carries the routing invariant on every M3 mesh-slot arm.
14152        use crate::aplicacao::Entrada;
14153        let c = caixa_aplicacao_with_entrada(Some(Entrada {
14154            host: "checkout.quero.cloud".into(),
14155            para: "gateway".into(),
14156            paths: Vec::new(),
14157            port: crate::DEFAULT_SERVICO_PORT,
14158        }));
14159        let slots = c.declared_mesh_slots();
14160        assert!(
14161            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
14162            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
14163             `:entrada` is Some (even for empty-paths / default-port) \
14164             — the accessor and the enumerator gate must route through \
14165             the same substrate-primitive typed dispatch on the outer \
14166             :entrada presence bit (got slots={slots:?})",
14167        );
14168        let c = caixa_aplicacao_with_entrada(None);
14169        let slots = c.declared_mesh_slots();
14170        assert!(
14171            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
14172            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
14173             when `:entrada` is None — the author-omitted arm must \
14174             route through the accessor's None-return unchanged (got \
14175             slots={slots:?})",
14176        );
14177    }
14178
14179    #[test]
14180    fn aplicacao_view_entrada_arm_folds_through_accessor() {
14181        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
14182        // Aplicacao-composition seed must fold through
14183        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
14184        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
14185        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
14186        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
14187        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
14188        // equals the outer composite's authored value (the fold must
14189        // project the authored composite verbatim), and a `Caixa {
14190        // entrada: None, kind: Aplicacao, .. }` must surface an
14191        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
14192        // "author omitted the slot entirely" arm folds through the
14193        // accessor's `Option::cloned` onto the same `None` presence
14194        // bit — unlike the peer `:politicas` / `:placement` arms
14195        // `:entrada` has no cluster-default fold, the omitted arm
14196        // stays omitted). The pair jointly pins the accessor +
14197        // Aplicacao-composition seed composition: any future silent
14198        // detour that had the accessor divert the raw slot away from
14199        // the seed's fold (an operator-resolved overlay's forward arm
14200        // silently differing from the raw slot's forward arm) would
14201        // silently split the build-time gateway-artifact emission gate
14202        // from the caixa-mesh renderer's Aplicacao-view input at the
14203        // composition boundary.
14204        use crate::aplicacao::Entrada;
14205        let authored = Entrada {
14206            host: "api.pleme.io".into(),
14207            para: "public-api".into(),
14208            paths: vec!["/v1".into()],
14209            port: 8080,
14210        };
14211        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
14212        let view = c.aplicacao_view().unwrap();
14213        assert_eq!(
14214            view.entrada(),
14215            Some(&authored),
14216            "Caixa::aplicacao_view must fold the authored :entrada \
14217             composite through the accessor verbatim onto the \
14218             projected AplicacaoSpec — a future silent detour at the \
14219             seed's fold arm would surface here as a projected- \
14220             composite drift (got {:?})",
14221            view.entrada(),
14222        );
14223        let c = caixa_aplicacao_with_entrada(None);
14224        let view = c.aplicacao_view().unwrap();
14225        assert!(
14226            view.entrada().is_none(),
14227            "Caixa::aplicacao_view must fold None through the \
14228             accessor's Option::cloned onto None — the author- \
14229             omitted arm must route through the accessor's None-return \
14230             unchanged (got {:?})",
14231            view.entrada(),
14232        );
14233    }
14234
14235    #[test]
14236    fn entrada_projects_option_ref_by_borrow() {
14237        // The by-borrow pin: [`Caixa::entrada`] returns
14238        // `Option<&Entrada>` by borrow — the returned reference
14239        // borrows the underlying `Option<Entrada>` storage of the
14240        // `:entrada` slot and the accessor must not clone the backing
14241        // composite on every call. Peer of the sibling
14242        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
14243        // `behavior_projects_option_ref_by_borrow` (35d8b52),
14244        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
14245        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
14246        // borrow pins on the outer top-level [`Caixa`]
14247        // `Option<&Composite>`-return sub-family — extended here to
14248        // the fifth and final axis of the same sub-family, closing
14249        // the discipline: the accessor's returned reference must
14250        // borrow from `&self` (the returned reference's lifetime is
14251        // tied to `&self`), and calling the accessor twice on the
14252        // same [`Caixa`] must yield references that are pointer-equal
14253        // (the underlying byte-buffer is the storage `Entrada`'s
14254        // allocation, not a fresh copy) as well as value-equal
14255        // (idempotent, no side effects on `&self`).
14256        //
14257        // Pins against a future silent detour that returned an owned
14258        // `Entrada` (which would type-check via the `Clone` impl but
14259        // silently clone on every call), a `&Entrada` panic-return on
14260        // the `None` arm (which would collapse the load-bearing
14261        // `Option` presence-bit into a runtime panic), or a one-arm-
14262        // only accessor that returned a saturating composite on some
14263        // sentinel input.
14264        use crate::aplicacao::Entrada;
14265        for entrada in [
14266            Some(Entrada {
14267                host: "checkout.quero.cloud".into(),
14268                para: "gateway".into(),
14269                paths: Vec::new(),
14270                port: crate::DEFAULT_SERVICO_PORT,
14271            }),
14272            Some(Entrada {
14273                host: "api.pleme.io".into(),
14274                para: "public-api".into(),
14275                paths: vec!["/v1".into(), "/v2".into()],
14276                port: 8080,
14277            }),
14278        ] {
14279            let c = caixa_aplicacao_with_entrada(entrada.clone());
14280            let first = c.entrada().unwrap();
14281            let second = c.entrada().unwrap();
14282            assert_eq!(
14283                first, second,
14284                "Caixa::entrada must be idempotent — two successive \
14285                 calls on the same &self must return the same &Entrada",
14286            );
14287            assert!(
14288                std::ptr::eq(first, second),
14289                "Caixa::entrada must borrow the underlying \
14290                 Option<Entrada> storage — two successive calls must \
14291                 return references with the same backing pointer (a \
14292                 fresh Entrada clone would change the pointer on every \
14293                 call)",
14294            );
14295            assert_eq!(
14296                Some(first),
14297                entrada.as_ref(),
14298                "Caixa::entrada must return :entrada verbatim by \
14299                 borrow — got {first:?}, expected {:?}",
14300                entrada.as_ref(),
14301            );
14302        }
14303        let c = caixa_aplicacao_with_entrada(None);
14304        assert!(
14305            c.entrada().is_none(),
14306            "Caixa::entrada must return None when :entrada is absent \
14307             — the author-omitted arm must project through the \
14308             accessor's Option::None unchanged",
14309        );
14310    }
14311
14312    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
14313
14314    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
14315        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14316        c.estrategia = estrategia;
14317        c
14318    }
14319
14320    #[test]
14321    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
14322        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
14323        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
14324        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
14325        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
14326        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
14327        // over the same discriminant the raw `self.estrategia` field
14328        // access carries, byte-equal across every representative fixture
14329        // in the accept-set — the author-omitted `None` shape (the
14330        // "defer to [`RestartStrategy::default`] through the
14331        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
14332        // every non-`Supervisor`-kind `defcaixa` carries by
14333        // `#[serde(default)]`), and each of the four closed-set variants
14334        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
14335        // / [`RestartStrategy::RestForOne`] /
14336        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
14337        // partitions on.
14338        //
14339        // Pins against a future silent detour that re-derived the
14340        // strategy from a peer axis (an accidental fallback to
14341        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
14342        // collapse that read the outer `:children` list-length axis into
14343        // the strategy discriminator at the accessor boundary), a
14344        // stale-derive detour that substituted [`RestartStrategy::default`]
14345        // when the outer `Option` held `None` (which would silently
14346        // collapse the load-bearing "author explicitly declared
14347        // `:estrategia OneForOne`" vs "author omitted the slot and
14348        // inherited the default" partition the [`Self::declared_supervisor_slots`]
14349        // presence-probe reads — the enumerator gate would still push
14350        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
14351        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
14352        // kind-coherence gate's traversal head from the
14353        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
14354        // composition head), a reference to an operator-resolved overlay
14355        // (the future per-cluster `:estrategia-overrides` slot — its
14356        // resolution must land at exactly this accessor body, not
14357        // silently divert the raw slot away from a second consumer), or
14358        // an axis-remap projection (a future detour that mapped
14359        // `OneForAll` through the accessor onto `OneForOne` would
14360        // silently split every downstream sibling-restart-strategy
14361        // consumer's per-arm fan-out).
14362        //
14363        // First outer top-level [`Caixa`] `Option<Copy>`-return
14364        // supervisor-tree-slot flat-spread accessor pin on the substrate
14365        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
14366        // projection pattern the sibling per-`Caixa` `:max-restarts` /
14367        // `:restart-window` future outer-scalar pins fold on. Peer of
14368        // the inner-altitude
14369        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
14370        // (eafb619) pin on the post-composition [`SupervisorSpec`]
14371        // altitude — same "the substrate-primitive accessor must byte-
14372        // equal the raw field access verbatim across every author-
14373        // declared value" discipline extended onto the pre-composition
14374        // outer author-surface [`Caixa`] altitude. Peer of the closed
14375        // outer-`Caixa` `Option<&Composite>` composite-reference family
14376        // the sibling `limits` / `behavior` / `politicas` / `placement` /
14377        // `entrada`
14378        // `..._returns_..._option_ref_verbatim_across_permutations` pins
14379        // already carry on the outer `Option<&Composite>` altitude.
14380        use crate::supervisor::RestartStrategy;
14381        let fixtures: Vec<Option<RestartStrategy>> = vec![
14382            None,
14383            Some(RestartStrategy::OneForOne),
14384            Some(RestartStrategy::OneForAll),
14385            Some(RestartStrategy::RestForOne),
14386            Some(RestartStrategy::SimpleOneForOne),
14387        ];
14388        for estrategia in fixtures {
14389            let c = caixa_with_estrategia(estrategia);
14390            assert_eq!(
14391                c.estrategia(),
14392                estrategia,
14393                "Caixa::estrategia must return :estrategia verbatim (got \
14394                 {:?}, expected {:?})",
14395                c.estrategia(),
14396                estrategia,
14397            );
14398            assert_eq!(
14399                c.estrategia(),
14400                c.estrategia,
14401                "Caixa::estrategia accessor and self.estrategia field \
14402                 access must byte-equal — the accessor is the substrate-\
14403                 primitive typed dispatch every downstream supervisor-\
14404                 tree flat-spread consumer must route through, and a \
14405                 discriminant split would silently break every consumer \
14406                 that relied on the accessor sharing the field's own \
14407                 Option<Copy> shape",
14408            );
14409            assert_eq!(
14410                c.estrategia().is_some(),
14411                c.estrategia.is_some(),
14412                "Caixa::estrategia().is_some() must byte-equal \
14413                 self.estrategia.is_some() — a presence-bit drift would \
14414                 silently split the paired Caixa::declared_supervisor_slots \
14415                 presence-probe arm from the Caixa::supervisor_view \
14416                 unwrap_or_default() fold's composition input",
14417            );
14418        }
14419    }
14420
14421    #[test]
14422    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
14423        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
14424        // `:estrategia` presence-probe arm must key off
14425        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
14426        // field-probe. Structurally: every `Caixa { estrategia:
14427        // Some(RestartStrategy::_), .. }` variant must push
14428        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
14429        // (the presence bit is `Some` for every closed-set variant, so
14430        // the M2 supervisor-tree kind-coherence gate must surface the
14431        // slot as "declared" regardless of which variant the author
14432        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
14433        // the label (the "author omitted the slot entirely, deferring
14434        // to [`RestartStrategy::default`] through the supervisor_view
14435        // fold" partition). The pair jointly pins the accessor +
14436        // declared-slot enumerator composition: any future silent detour
14437        // that had the accessor collapse `Some(RestartStrategy::default())`
14438        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
14439        // projection) would silently absorb the "declared but default-
14440        // valued" arm at the accessor boundary and the
14441        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
14442        // coherence gate would silently accept a struct-literal `Caixa`
14443        // carrying the drift.
14444        //
14445        // Peer of the sibling per-`Caixa`
14446        // `declared_servico_slots_limits_arm_routes_through_accessor`
14447        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
14448        // `Option<&LimitsSpec>` composition axis — same "the enumerator
14449        // gate must route through the substrate-primitive typed
14450        // dispatch" discipline extended onto the flat-spread M2
14451        // supervisor-tree `Option<RestartStrategy>`-composition surface,
14452        // opening the outer-`Caixa` supervisor-tree-slot arm of the
14453        // composition-pin family.
14454        use crate::supervisor::RestartStrategy;
14455        for estrategia in [
14456            RestartStrategy::OneForOne,
14457            RestartStrategy::OneForAll,
14458            RestartStrategy::RestForOne,
14459            RestartStrategy::SimpleOneForOne,
14460        ] {
14461            let c = caixa_with_estrategia(Some(estrategia));
14462            let slots = c.declared_supervisor_slots();
14463            assert!(
14464                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
14465                "declared_supervisor_slots must push \
14466                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
14467                 Some({estrategia:?}) — the accessor and the enumerator \
14468                 gate must route through the same substrate-primitive \
14469                 typed dispatch on the outer :estrategia presence bit \
14470                 (got slots={slots:?})",
14471            );
14472        }
14473        let c = caixa_with_estrategia(None);
14474        let slots = c.declared_supervisor_slots();
14475        assert!(
14476            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
14477            "declared_supervisor_slots must NOT push \
14478             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
14479             — the author-omitted arm must route through the accessor's \
14480             None-return unchanged (got slots={slots:?})",
14481        );
14482    }
14483
14484    #[test]
14485    fn supervisor_view_estrategia_arm_routes_through_accessor() {
14486        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
14487        // [`SupervisorSpec`] construction arm must key off
14488        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
14489        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
14490        // for every `:kind Supervisor` `Caixa` carrying an author-
14491        // declared `Some(RestartStrategy::_)` variant, the composed
14492        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
14493        // outer accessor's declared variant unchanged; and for a
14494        // `:kind Supervisor` `Caixa` carrying `None`, the composed
14495        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
14496        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
14497        // arm the flat-spread `unwrap_or_default()` fold projects to on
14498        // the author-omitted arm — this is the *composition* between the
14499        // outer `Option<RestartStrategy>` accessor's presence-bit
14500        // surface and the inner post-composition non-`Option`
14501        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
14502        // pins the accessor + supervisor_view composition: any future
14503        // silent detour that had the accessor promote `None` to
14504        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
14505        // projection) would silently collapse the two arms into one at
14506        // the accessor boundary and the [`Self::declared_supervisor_slots`]
14507        // presence probe would silently drift from the composition site.
14508        //
14509        // Peer of the sibling M2 supervisor-slot post-composition
14510        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
14511        // pin on the [`SupervisorSpec::validate`] altitude — this pin
14512        // extends that inner-altitude accessor-routing discipline onto
14513        // the pre-composition outer author-surface [`Caixa`] altitude,
14514        // pinning the composition edge between the flat-spread outer
14515        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
14516        // `RestartStrategy` axes.
14517        use crate::CaixaKind;
14518        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
14519        for estrategia in [
14520            RestartStrategy::OneForOne,
14521            RestartStrategy::OneForAll,
14522            RestartStrategy::RestForOne,
14523            RestartStrategy::SimpleOneForOne,
14524        ] {
14525            let mut c = caixa_with_estrategia(Some(estrategia));
14526            c.kind = CaixaKind::Supervisor;
14527            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
14528            // shape partition through the [`gen_platform::IsVariant`]
14529            // derive-generated
14530            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
14531            // than the raw `matches!(estrategia, RestartStrategy::
14532            // SimpleOneForOne)` open-coded pattern-match — same closed-
14533            // set-typed-enum arm-discriminator dispatch discipline the
14534            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
14535            // convergence (915a934) extended onto its two paired positive
14536            // / negated `matches!` sites and the peer
14537            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
14538            // predicate convergence (766ec63) extended onto the M3 mesh-
14539            // slot per-`:placement` distribution-strategy discriminator
14540            // axis. See the sibling `supervisor::tests::
14541            // round_trip_all_strategies` and
14542            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
14543            // fixtures — the three sites (all test-only,
14544            // acknowledged in 915a934's Prior-commits footnote as the
14545            // outstanding follow-up) now consult one typed dispatch on
14546            // the substrate primitive.
14547            c.children = if estrategia.is_simple_one_for_one() {
14548                Vec::new()
14549            } else {
14550                vec![ChildSpec {
14551                    caixa: "worker".into(),
14552                    versao: "^0.1".into(),
14553                    restart: RestartPolicy::Permanent,
14554                }]
14555            };
14556            let view = c.supervisor_view().expect(
14557                "supervisor_view must materialize a SupervisorSpec for a \
14558                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
14559            );
14560            assert_eq!(
14561                view.estrategia(),
14562                c.estrategia().unwrap(),
14563                "supervisor_view must carry the outer Caixa::estrategia() \
14564                 declared variant onto the composed SupervisorSpec.estrategia \
14565                 field verbatim on the Some arm (got {:?}, expected {:?})",
14566                view.estrategia(),
14567                c.estrategia().unwrap(),
14568            );
14569        }
14570        // The author-omitted arm: outer `None` → composed
14571        // `RestartStrategy::default()` through the flat-spread
14572        // `unwrap_or_default()` fold.
14573        let mut c = caixa_with_estrategia(None);
14574        c.kind = CaixaKind::Supervisor;
14575        // Populate children so the sibling supervisor slots are coherent
14576        // for the [`Self::supervisor_view`] projection; the `:estrategia`
14577        // arm still defers to [`RestartStrategy::default`] on the
14578        // author-omitted arm even when the sibling slots carry values.
14579        c.children = vec![ChildSpec {
14580            caixa: "worker".into(),
14581            versao: "^0.1".into(),
14582            restart: RestartPolicy::Permanent,
14583        }];
14584        let view = c.supervisor_view().expect(
14585            "supervisor_view must materialize a SupervisorSpec for a \
14586             :kind Supervisor Caixa carrying a None `:estrategia` slot",
14587        );
14588        assert_eq!(
14589            view.estrategia(),
14590            RestartStrategy::default(),
14591            "supervisor_view must project the outer Caixa::estrategia() \
14592             None arm onto RestartStrategy::default() through the flat-\
14593             spread unwrap_or_default() fold (got {:?}, expected {:?})",
14594            view.estrategia(),
14595            RestartStrategy::default(),
14596        );
14597        assert!(
14598            c.estrategia().is_none(),
14599            "Caixa::estrategia() must remain None on the author-omitted \
14600             arm — the supervisor_view fold must not mutate the outer \
14601             flat-spread presence bit",
14602        );
14603    }
14604
14605    #[test]
14606    fn estrategia_projects_option_by_copy() {
14607        // The by-`Copy` pin: [`Caixa::estrategia`] returns
14608        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
14609        // the accessor does not borrow `&self` past the call (no
14610        // lifetime on the return type), and calling the accessor twice
14611        // on the same [`Caixa`] must yield discriminant-equal values
14612        // (idempotent, no side effects on `&self`). Peer of the sibling
14613        // outer-`Caixa` `Option<&Composite>` by-borrow
14614        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
14615        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
14616        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
14617        // `placement_projects_option_ref_by_borrow` (4fb8074) /
14618        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
14619        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
14620        // extended here to the outer-`Caixa` `Option<Copy>`-return
14621        // flat-spread axis. The `Copy` discipline replaces the pointer-
14622        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
14623        // `Copy` discriminant is definitionally the same discriminant, so
14624        // the axis reduces to discriminant equality).
14625        //
14626        // Pins against a future silent detour that returned a fresh
14627        // `Option<&RestartStrategy>` (which would type-check but silently
14628        // introduce a borrow of `&self` past the call, collapsing the
14629        // load-bearing "no lifetime on the return type" `Copy` projection
14630        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
14631        // read side effect that flipped the outer discriminant on
14632        // successive calls, or an axis-remap projection that returned a
14633        // different variant than the field storage.
14634        use crate::supervisor::RestartStrategy;
14635        for estrategia in [
14636            Some(RestartStrategy::OneForOne),
14637            Some(RestartStrategy::OneForAll),
14638            Some(RestartStrategy::RestForOne),
14639            Some(RestartStrategy::SimpleOneForOne),
14640        ] {
14641            let c = caixa_with_estrategia(estrategia);
14642            let first = c.estrategia();
14643            let second = c.estrategia();
14644            assert_eq!(
14645                first, second,
14646                "Caixa::estrategia must be idempotent — two successive \
14647                 calls on the same &self must return the same \
14648                 Option<RestartStrategy>",
14649            );
14650            assert_eq!(
14651                first, estrategia,
14652                "Caixa::estrategia must return :estrategia verbatim by \
14653                 Copy — got {first:?}, expected {estrategia:?}",
14654            );
14655        }
14656        let c = caixa_with_estrategia(None);
14657        assert!(
14658            c.estrategia().is_none(),
14659            "Caixa::estrategia must return None when :estrategia is \
14660             absent — the author-omitted arm must project through the \
14661             accessor's Option::None unchanged",
14662        );
14663    }
14664
14665    // ── Caixa::max_restarts / Caixa::restart_window —
14666    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
14667    //    (Option<u32> / Option<&str>) folding on the ed04d3c
14668    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
14669
14670    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
14671        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14672        c.max_restarts = max_restarts;
14673        c
14674    }
14675
14676    fn caixa_supervisor_with_max_restarts_and_window(
14677        max_restarts: Option<u32>,
14678        restart_window: Option<&str>,
14679    ) -> Caixa {
14680        use crate::CaixaKind;
14681        use crate::supervisor::{ChildSpec, RestartPolicy};
14682        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
14683        c.kind = CaixaKind::Supervisor;
14684        c.max_restarts = max_restarts;
14685        c.restart_window = restart_window.map(str::to_string);
14686        c.children = vec![ChildSpec {
14687            caixa: "worker".into(),
14688            versao: "^0.1".into(),
14689            restart: RestartPolicy::Permanent,
14690        }];
14691        c
14692    }
14693
14694    #[test]
14695    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
14696        // Value-shape pin: [`Caixa::max_restarts`] returns the
14697        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
14698        // from the typed slot's own storage, byte-equal across the
14699        // author-omitted `None` arm (the "defer to the
14700        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
14701        // `{intensity, 5, 60}` default" partition every
14702        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
14703        // and each of the representative fixtures in the accept-set —
14704        // `0` (the zero-floor arm the peer
14705        // [`crate::supervisor::SupervisorSpec::validate`]
14706        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
14707        // the post-composition altitude — the accessor must ship the
14708        // raw slot verbatim so struct-literal fixtures continue to
14709        // expose the zero at the accessor boundary), the OTP-canonical
14710        // `5` default (`{intensity, 5, 60}` worker-supervisor from
14711        // Learn You Some Erlang), `1000` (the
14712        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
14713        // upper-bound gate accepts on the boundary), `u32::MAX` (a
14714        // past-the-cap sentinel that the substrate-primitive accessor
14715        // must still ship verbatim). Second outer top-level
14716        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
14717        // pin — folds on the sibling
14718        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
14719        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
14720        // onto the sibling `Option<u32>` restart-budget-count arm.
14721        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
14722        for max_restarts in fixtures {
14723            let c = caixa_with_max_restarts(max_restarts);
14724            assert_eq!(
14725                c.max_restarts(),
14726                max_restarts,
14727                "Caixa::max_restarts must return :max-restarts verbatim \
14728                 (got {:?}, expected {max_restarts:?})",
14729                c.max_restarts(),
14730            );
14731            assert_eq!(
14732                c.max_restarts(),
14733                c.max_restarts,
14734                "Caixa::max_restarts accessor and self.max_restarts \
14735                 field access must byte-equal — a presence-bit or count \
14736                 drift would silently split the paired \
14737                 Caixa::declared_supervisor_slots presence-probe arm \
14738                 from the Caixa::supervisor_view unwrap_or(5) fold's \
14739                 composition input",
14740            );
14741        }
14742    }
14743
14744    #[test]
14745    fn max_restarts_projects_option_by_copy() {
14746        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
14747        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
14748        // borrow `&self` past the call (no lifetime on the return type),
14749        // and calling the accessor twice on the same [`Caixa`] must
14750        // yield equal values (idempotent, no side effects). Peer of the
14751        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
14752        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
14753        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
14754            let c = caixa_with_max_restarts(max_restarts);
14755            let first = c.max_restarts();
14756            let second = c.max_restarts();
14757            assert_eq!(
14758                first, second,
14759                "Caixa::max_restarts must be idempotent — two successive \
14760                 calls on the same &self must return the same Option<u32>",
14761            );
14762            assert_eq!(
14763                first, max_restarts,
14764                "Caixa::max_restarts must return :max-restarts verbatim \
14765                 by Copy — got {first:?}, expected {max_restarts:?}",
14766            );
14767        }
14768    }
14769
14770    #[test]
14771    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
14772        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
14773        // `:max-restarts` presence-probe arm must key off
14774        // [`Caixa::max_restarts`], not the raw
14775        // `self.max_restarts.is_some()` field-probe. Structurally: every
14776        // `Caixa { max_restarts: Some(_), .. }` variant must push
14777        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
14778        // list (the presence bit is `Some` for every representative
14779        // count, so the M2 kind-coherence gate must surface the slot as
14780        // "declared"), and a `Caixa { max_restarts: None, .. }` must
14781        // NOT push the label. Peer of the sibling
14782        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
14783        // (ed04d3c) composition pin — same routing-through-accessor
14784        // discipline extended onto the sibling flat-spread `Option<u32>`
14785        // arm.
14786        for max_restarts in [0u32, 5, 1000, u32::MAX] {
14787            let c = caixa_with_max_restarts(Some(max_restarts));
14788            let slots = c.declared_supervisor_slots();
14789            assert!(
14790                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
14791                "declared_supervisor_slots must push \
14792                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
14793                 is Some({max_restarts}) — the accessor and the \
14794                 enumerator gate must route through the same \
14795                 substrate-primitive typed dispatch on the outer \
14796                 :max-restarts presence bit (got slots={slots:?})",
14797            );
14798        }
14799        let c = caixa_with_max_restarts(None);
14800        let slots = c.declared_supervisor_slots();
14801        assert!(
14802            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
14803            "declared_supervisor_slots must NOT push \
14804             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
14805             None — the author-omitted arm must route through the \
14806             accessor's None-return unchanged (got slots={slots:?})",
14807        );
14808    }
14809
14810    #[test]
14811    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
14812        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
14813        // [`SupervisorSpec`] construction arm must key off
14814        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
14815        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
14816        // every `:kind Supervisor` `Caixa` carrying an author-declared
14817        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
14818        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
14819        // carrying `None`, the composed [`SupervisorSpec`]'s
14820        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
14821        // of the sibling
14822        // `supervisor_view_estrategia_arm_routes_through_accessor`
14823        // (ed04d3c) composition pin.
14824        for max_restarts in [1u32, 5, 1000] {
14825            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
14826            let view = c.supervisor_view().expect(
14827                "supervisor_view must materialize a SupervisorSpec for a \
14828                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
14829            );
14830            assert_eq!(
14831                view.max_restarts(),
14832                max_restarts,
14833                "supervisor_view must carry the outer \
14834                 Caixa::max_restarts() Some arm onto the composed \
14835                 SupervisorSpec.max_restarts field verbatim (got {}, \
14836                 expected {max_restarts})",
14837                view.max_restarts(),
14838            );
14839        }
14840        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
14841        let view = c.supervisor_view().expect(
14842            "supervisor_view must materialize a SupervisorSpec for a \
14843             :kind Supervisor Caixa carrying a None :max-restarts",
14844        );
14845        assert_eq!(
14846            view.max_restarts(),
14847            5,
14848            "supervisor_view must project the outer \
14849             Caixa::max_restarts() None arm onto the OTP-canonical \
14850             {{intensity, 5, 60}} default (5) through the flat-spread \
14851             unwrap_or(5) fold (got {})",
14852            view.max_restarts(),
14853        );
14854        assert!(
14855            c.max_restarts().is_none(),
14856            "Caixa::max_restarts() must remain None on the author-\
14857             omitted arm — the supervisor_view fold must not mutate \
14858             the outer flat-spread presence bit",
14859        );
14860    }
14861
14862    #[test]
14863    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
14864        // Value-shape pin: [`Caixa::restart_window`] returns the
14865        // `:restart-window` typed `Option<String>` verbatim as an
14866        // `Option<&str>`, borrowed from the typed slot's own storage,
14867        // byte-equal across the author-omitted `None` arm and each of
14868        // the representative fixtures in the accept-set — the canonical
14869        // `"60s"` from `{intensity, 5, 60}`, the sibling
14870        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
14871        // / `"0s"`) the shared codec's positive-set sweep pin covers,
14872        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
14873        // seconds drift the sibling [`Self::validate_restart_window`]
14874        // gate refuses; the accessor must ship the raw slot verbatim
14875        // so struct-literal fixtures continue to expose the drift at
14876        // the accessor boundary). Third outer top-level [`Caixa`]
14877        // supervisor-tree flat-spread pin — extends the sub-family onto
14878        // the sibling `Option<&str>` raw-duration-string arm.
14879        for window in [
14880            None,
14881            Some("60s"),
14882            Some("5m"),
14883            Some("1h"),
14884            Some("500ms"),
14885            Some("1.5s"),
14886            Some(""),
14887        ] {
14888            let c = caixa_with_restart_window(window);
14889            assert_eq!(
14890                c.restart_window(),
14891                window,
14892                "Caixa::restart_window must return :restart-window \
14893                 verbatim as Option<&str> (got {:?}, expected {window:?})",
14894                c.restart_window(),
14895            );
14896            assert_eq!(
14897                c.restart_window(),
14898                c.restart_window.as_deref(),
14899                "Caixa::restart_window accessor and \
14900                 self.restart_window.as_deref() field access must \
14901                 byte-equal — a byte-level drift would silently split \
14902                 the paired Caixa::declared_supervisor_slots \
14903                 presence-probe arm from the \
14904                 Caixa::validate_restart_window shared-codec gate and \
14905                 the Caixa::supervisor_view soft-swallowing fold",
14906            );
14907        }
14908    }
14909
14910    #[test]
14911    fn restart_window_projects_slice_by_borrow() {
14912        // The by-borrow pin: [`Caixa::restart_window`] returns
14913        // `Option<&str>` by borrow — the returned string slice borrows
14914        // the underlying `Option<String>` storage of the `:restart-window`
14915        // slot and the accessor must not clone on every call. Peer of
14916        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
14917        // by-borrow pins on the universal-axis scalar family
14918        // (`licenca_projects_option_ref_by_borrow` /
14919        // `descricao_projects_option_ref_by_borrow` and siblings) —
14920        // extended onto the M2 supervisor-tree flat-spread
14921        // `Option<&str>` raw-duration-string axis.
14922        for window in [None, Some("60s"), Some("5m"), Some("")] {
14923            let c = caixa_with_restart_window(window);
14924            let first = c.restart_window();
14925            let second = c.restart_window();
14926            assert_eq!(
14927                first, second,
14928                "Caixa::restart_window must be idempotent — two \
14929                 successive calls on the same &self must return the \
14930                 same Option<&str>",
14931            );
14932            if let (Some(a), Some(b)) = (first, second) {
14933                assert_eq!(
14934                    a.as_ptr(),
14935                    b.as_ptr(),
14936                    "Caixa::restart_window must borrow the underlying \
14937                     String storage — two successive Some-arm calls must \
14938                     return slices with the same backing pointer (a fresh \
14939                     String clone would change the pointer on every call)",
14940                );
14941            }
14942            assert_eq!(
14943                first, window,
14944                "Caixa::restart_window must return :restart-window \
14945                 verbatim by borrow — got {first:?}, expected {window:?}",
14946            );
14947        }
14948    }
14949
14950    #[test]
14951    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
14952        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
14953        // `:restart-window` presence-probe arm must key off
14954        // [`Caixa::restart_window`], not the raw
14955        // `self.restart_window.is_some()` field-probe. Structurally:
14956        // every `Caixa { restart_window: Some(_), .. }` must push
14957        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
14958        // list, and a `Caixa { restart_window: None, .. }` must NOT
14959        // push the label. Peer of the sibling
14960        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
14961        // routing pin.
14962        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
14963            let c = caixa_with_restart_window(Some(window));
14964            let slots = c.declared_supervisor_slots();
14965            assert!(
14966                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
14967                "declared_supervisor_slots must push \
14968                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
14969                 `:restart-window` is Some({window:?}) — the accessor \
14970                 and the enumerator gate must route through the same \
14971                 substrate-primitive typed dispatch on the outer \
14972                 :restart-window presence bit (got slots={slots:?})",
14973            );
14974        }
14975        let c = caixa_with_restart_window(None);
14976        let slots = c.declared_supervisor_slots();
14977        assert!(
14978            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
14979            "declared_supervisor_slots must NOT push \
14980             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
14981             is None — the author-omitted arm must route through the \
14982             accessor's None-return unchanged (got slots={slots:?})",
14983        );
14984    }
14985
14986    #[test]
14987    fn validate_restart_window_arm_routes_through_accessor() {
14988        // Composition pin: [`Caixa::validate_restart_window`]'s
14989        // shared-codec fold arm must key off [`Caixa::restart_window`],
14990        // not the raw `self.restart_window.as_deref()` field-projection.
14991        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
14992        // express no reset" canonical shape); (2) a canonical `Some`
14993        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
14994        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
14995        // .. })` carrying the offending raw string verbatim. The three
14996        // arms jointly pin that the validator's raw-string binding is
14997        // the accessor's return, not a peer projection — any future
14998        // silent detour that had the accessor collapse `Some("")` to
14999        // `None` would silently absorb the empty-after-trim refusal
15000        // case at the accessor boundary.
15001        caixa_with_restart_window(None)
15002            .validate_restart_window()
15003            .expect("None :restart-window must validate through the accessor");
15004        caixa_with_restart_window(Some("60s"))
15005            .validate_restart_window()
15006            .expect("canonical :restart-window \"60s\" must validate through the accessor");
15007        let err = caixa_with_restart_window(Some("1.5s"))
15008            .validate_restart_window()
15009            .expect_err("fractional-seconds :restart-window must fail through the accessor");
15010        assert!(
15011            matches!(
15012                err,
15013                ManifestError::RestartWindowMalformed { ref restart_window, .. }
15014                    if restart_window == "1.5s"
15015            ),
15016            "validator must carry the offending raw string verbatim \
15017             from the accessor's borrowed &str (got {err:?})",
15018        );
15019    }
15020
15021    #[test]
15022    fn supervisor_view_restart_window_arm_routes_through_accessor() {
15023        // Composition pin: [`Caixa::supervisor_view`]'s
15024        // per-`:restart-window` [`SupervisorSpec`] construction arm
15025        // must key off [`Caixa::restart_window`]'s soft-swallowing
15026        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
15027        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
15028        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
15029        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
15030        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
15031        // (the shared codec's canonical parse); (3) codec-rejected
15032        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
15033        // (the soft-swallow preserving the view's best-effort shape).
15034        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
15035        let view = c.supervisor_view().expect("Supervisor kind has a view");
15036        assert_eq!(
15037            view.restart_window(),
15038            None,
15039            "supervisor_view must project outer None :restart-window \
15040             onto None on the composed SupervisorSpec (never-reset \
15041             sentinel) through the accessor's None-return unchanged",
15042        );
15043
15044        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
15045        let view = c.supervisor_view().expect("Supervisor kind has a view");
15046        assert_eq!(
15047            view.restart_window(),
15048            Some(std::time::Duration::from_secs(60)),
15049            "supervisor_view must fold outer Some(\"60s\") through the \
15050             shared duration_codec into Duration::from_secs(60) on the \
15051             composed SupervisorSpec (accessor's Some(&str) → codec \
15052             parse → Some(Duration))",
15053        );
15054
15055        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
15056        let view = c.supervisor_view().expect("Supervisor kind has a view");
15057        assert_eq!(
15058            view.restart_window(),
15059            None,
15060            "supervisor_view must soft-swallow the shared-codec parse \
15061             failure to None (the view's best-effort shape the sibling \
15062             manifest-level validate_restart_window surfaces as \
15063             RestartWindowMalformed); the accessor's raw-string return \
15064             is the single input every downstream consumer keys off",
15065        );
15066    }
15067
15068    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
15069
15070    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
15071        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15072        c.upgrade_from = upgrade_from;
15073        c
15074    }
15075
15076    #[test]
15077    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
15078        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
15079        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
15080        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
15081        // typed `Vec<UpgradeFromEntry>` verbatim as a
15082        // `&[UpgradeFromEntry]` slice-view over the same backing
15083        // buffer the raw `self.upgrade_from.as_slice()` field access
15084        // borrows from, element-equal across every representative
15085        // fixture in the accept-set — `[]` (the "no hot-upgrade path
15086        // declared" arm every `defcaixa` without an `:upgrade-from`
15087        // block carries; `#[serde(default)]` folds an omitted slot
15088        // onto `Vec::new()`), a canonical single-entry `Restart`
15089        // fixture (the shape most Servicos carry — a single prior
15090        // version with the fallback strategy), a canonical multi-
15091        // entry list carrying every typed instruction variant
15092        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
15093        // `Restart`), and a past-the-guard sentinel — a duplicate-
15094        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
15095        // ([`crate::upgrade::validate_upgrade_from`] rejects through
15096        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
15097        // ship the raw slot verbatim so struct-literal fixtures
15098        // continue to expose the duplicate at the accessor boundary).
15099        //
15100        // Pins against a future silent detour that returned an owned
15101        // `Vec<UpgradeFromEntry>` (which would type-check but silently
15102        // clone on every accessor call, breaking the zero-cost
15103        // projection every peer sibling slice accessor carries), a
15104        // `[dup, dup] → [dup]` dedup collapse (which would silently
15105        // absorb the `DuplicateFrom` refusal case at the accessor
15106        // boundary and the [`crate::StandardLayout::verify`] cross-
15107        // entry gate would silently accept a struct-literal `Caixa`
15108        // carrying the drift), a reference to an operator-resolved
15109        // overlay (the future per-cluster `:upgrade-overrides` slot
15110        // — its resolution must land at exactly this accessor body,
15111        // not silently divert the raw slot away from a second
15112        // consumer), or an axis-shuffled projection (a future detour
15113        // that reordered entries through the accessor would silently
15114        // split the paired [`crate::StandardLayout::verify`] per-
15115        // `:upgrade-from` shape gate's traversal input from the peer
15116        // [`crate::render::servico_m2_overlay`] emitter's projection
15117        // input, since the operator's hot-upgrade dispatch matches
15118        // per-`:from` and axis reordering would silently split the
15119        // per-entry script-path existence probe's iteration order
15120        // from the M2 overlay emitter's serialized-entry order).
15121        //
15122        // First outer top-level [`Caixa`] `&[Composite]`-return
15123        // slice accessor pin on the substrate primitive for M2 / M3
15124        // typed-slot vec-carry axes — opens the outer-`Caixa`
15125        // `&[Composite]` composite-slice projection pattern the
15126        // sibling `:children` [`crate::supervisor::ChildSpec`] /
15127        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
15128        // [`crate::aplicacao::WitContract`] future outer-composite-
15129        // slice pins fold on. Peer of the closed outer-`Caixa`
15130        // scalar `Option<&Composite>` composite-reference family the
15131        // sibling `limits` / `behavior` / `politicas` / `placement`
15132        // / `entrada` `..._returns_..._option_ref_verbatim_across_
15133        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
15134        // the "byte-equal, borrow-shared" outer-accessor discipline
15135        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
15136        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15137        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
15138            vec![],
15139            vec![UpgradeFromEntry {
15140                from: "0.0.1".into(),
15141                instructions: vec![UpgradeInstruction::Restart],
15142            }],
15143            vec![
15144                UpgradeFromEntry {
15145                    from: "0.0.1".into(),
15146                    instructions: vec![
15147                        UpgradeInstruction::LoadModule {
15148                            module: "demo".into(),
15149                        },
15150                        UpgradeInstruction::SoftPurge {
15151                            module: "demo".into(),
15152                        },
15153                    ],
15154                },
15155                UpgradeFromEntry {
15156                    from: "0.0.2".into(),
15157                    instructions: vec![
15158                        UpgradeInstruction::StateChange {
15159                            script: "servicos/upgrade.lisp".into(),
15160                        },
15161                        UpgradeInstruction::Purge {
15162                            module: "demo".into(),
15163                        },
15164                        UpgradeInstruction::Restart,
15165                    ],
15166                },
15167            ],
15168            vec![
15169                UpgradeFromEntry {
15170                    from: "0.1.0".into(),
15171                    instructions: vec![UpgradeInstruction::Restart],
15172                },
15173                UpgradeFromEntry {
15174                    from: "0.1.0".into(),
15175                    instructions: vec![UpgradeInstruction::Restart],
15176                },
15177            ],
15178        ];
15179        for upgrade_from in fixtures {
15180            let c = caixa_with_upgrade_from(upgrade_from.clone());
15181            assert_eq!(
15182                c.upgrade_from(),
15183                upgrade_from.as_slice(),
15184                "Caixa::upgrade_from must return :upgrade-from \
15185                 verbatim (got {:?}, expected {upgrade_from:?})",
15186                c.upgrade_from(),
15187            );
15188            assert_eq!(
15189                c.upgrade_from(),
15190                c.upgrade_from.as_slice(),
15191                "Caixa::upgrade_from must element-equal the raw \
15192                 `self.upgrade_from.as_slice()` field access across \
15193                 every value in the Vec<UpgradeFromEntry> accept-set",
15194            );
15195            assert_eq!(
15196                c.upgrade_from().is_empty(),
15197                c.upgrade_from.is_empty(),
15198                "Caixa::upgrade_from().is_empty() must byte-equal \
15199                 self.upgrade_from.is_empty() — a presence-bit drift \
15200                 would silently split the paired \
15201                 Caixa::declared_servico_slots M2 declared-slot \
15202                 enumerator's presence probe from the peer \
15203                 crate::render::servico_m2_overlay M2 overlay \
15204                 emitter's presence gate",
15205            );
15206        }
15207    }
15208
15209    #[test]
15210    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
15211        // Composition pin: [`Caixa::declared_servico_slots`]'s
15212        // `:upgrade-from` presence-probe arm must key off
15213        // [`Caixa::upgrade_from`], not the raw
15214        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
15215        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
15216        // instructions: vec![Restart] }], .. }` must push
15217        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
15218        // (the presence bit is non-empty, so the M2 kind-coherence
15219        // gate must surface the slot as "declared"), and a `Caixa {
15220        // upgrade_from: vec![], .. }` must NOT push the label (the
15221        // "author omitted the slot entirely" arm — the empty-slice
15222        // partition the serde-default folds onto). The pair jointly
15223        // pins the accessor + declared-slot enumerator composition:
15224        // any future silent detour that had the accessor collapse
15225        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
15226        // is_empty())` projection) would silently absorb the
15227        // "declared but degenerate" arm at the accessor boundary and
15228        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
15229        // coherence gate would silently accept a struct-literal
15230        // `Caixa` carrying the drift.
15231        //
15232        // Peer of the sibling
15233        // `declared_servico_slots_limits_arm_routes_through_accessor`
15234        // (b2bd9d7) and
15235        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15236        // (35d8b52) composition pins on the sibling `:limits` /
15237        // `:behavior` outer-`Option<&Composite>` arms — same "the
15238        // enumerator gate must route through the substrate-primitive
15239        // typed dispatch" discipline extended onto the third M2
15240        // Servico-runtime slot axis, closing the enumerator's routing
15241        // invariant on every M2 arm.
15242        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15243        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
15244            from: "0.0.1".into(),
15245            instructions: vec![UpgradeInstruction::Restart],
15246        }]);
15247        let slots = c.declared_servico_slots();
15248        assert!(
15249            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
15250            "declared_servico_slots must push \
15251             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
15252             non-empty — the accessor and the enumerator gate must \
15253             route through the same substrate-primitive typed \
15254             dispatch on the outer :upgrade-from presence bit (got \
15255             slots={slots:?})",
15256        );
15257        let c = caixa_with_upgrade_from(vec![]);
15258        let slots = c.declared_servico_slots();
15259        assert!(
15260            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
15261            "declared_servico_slots must NOT push \
15262             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
15263             empty — the author-omitted arm must route through the \
15264             accessor's empty-slice return unchanged (got \
15265             slots={slots:?})",
15266        );
15267    }
15268
15269    #[test]
15270    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
15271        // Composition pin: [`crate::render::servico_m2_overlay`]'s
15272        // per-`:upgrade-from` M2 overlay emit arm must key off
15273        // [`Caixa::upgrade_from`], not the raw
15274        // `!caixa.upgrade_from.is_empty()` presence gate + the
15275        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
15276        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
15277        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
15278        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
15279        // sequence in the overlay (the emitter fans onto the serde
15280        // slice-serialization), and a `Caixa { upgrade_from: vec![],
15281        // .. }` must omit the key entirely (the empty-slice
15282        // partition — the `!.is_empty()` outer gate elides the key
15283        // when the author omitted the slot). The pair jointly pins
15284        // the accessor + M2 overlay emitter composition: any future
15285        // silent detour that had the accessor return a fresh-cloned
15286        // `Vec<UpgradeFromEntry>` copy would silently break the
15287        // reference-identity pin the peer per-entry
15288        // `serde_yaml::to_value(caixa.upgrade_from())` projection
15289        // reads from — the projection would clone once per accessor
15290        // call instead of borrowing the storage buffer verbatim.
15291        //
15292        // Peer of the sibling
15293        // `servico_m2_overlay_limits_arm_routes_through_accessor`
15294        // (b2bd9d7) and
15295        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
15296        // (35d8b52) composition pins on the sibling `:limits` /
15297        // `:behavior` outer-`Option<&Composite>` arms — same "the
15298        // M2 overlay emitter must route through the substrate-
15299        // primitive typed dispatch" discipline extended onto the
15300        // third M2 Servico-runtime slot axis, closing the overlay
15301        // emitter's routing invariant on every M2 arm.
15302        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
15303        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15304        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
15305            from: "0.0.1".into(),
15306            instructions: vec![UpgradeInstruction::Restart],
15307        }]);
15308        let overlay = servico_m2_overlay(&c).unwrap();
15309        assert!(
15310            overlay.contains_key(M2_KEY_UPGRADE_FROM),
15311            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
15312             `:upgrade-from` is non-empty — the accessor and the M2 \
15313             overlay emitter must route through the same substrate- \
15314             primitive typed dispatch on the outer :upgrade-from \
15315             slice (got overlay={overlay:?})",
15316        );
15317        let c = caixa_with_upgrade_from(vec![]);
15318        let overlay = servico_m2_overlay(&c).unwrap();
15319        assert!(
15320            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
15321            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
15322             `:upgrade-from` is empty — the empty-slice partition \
15323             must route through the accessor's empty-slice return \
15324             unchanged (got overlay={overlay:?})",
15325        );
15326    }
15327
15328    #[test]
15329    fn upgrade_from_projects_slice_by_borrow() {
15330        // The by-borrow pin: [`Caixa::upgrade_from`] returns
15331        // `&[UpgradeFromEntry]` by borrow — the returned slice
15332        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
15333        // the `:upgrade-from` slot and the accessor must not clone
15334        // the backing `Vec` on every call. Peer of the sibling
15335        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
15336        // (`autores_projects_slice_by_borrow` b5d813f,
15337        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
15338        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15339        // `exe_projects_slice_by_borrow` 65d9527,
15340        // `servicos_projects_slice_by_borrow` 611f78b,
15341        // `deps_projects_slice_by_borrow` ad34b4e,
15342        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
15343        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
15344        // axes — extended here to the first outer-`Caixa`
15345        // composite-element `&[Composite]` axis: the accessor's
15346        // returned slice must borrow from `&self` (the returned
15347        // reference's lifetime is tied to `&self`), and calling the
15348        // accessor twice on the same [`Caixa`] must yield slices
15349        // that are pointer-equal (the underlying byte-buffer is the
15350        // storage `Vec`'s allocation, not a fresh copy) as well as
15351        // value-equal (idempotent, no side effects on `&self`).
15352        //
15353        // Pins against a future silent detour that returned an owned
15354        // `Vec<UpgradeFromEntry>` (which would type-check but
15355        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
15356        // return (which would leak the backing `Vec`'s
15357        // grow/push/reserve surface no downstream consumer reaches
15358        // for), or a one-arm-only accessor that returned a
15359        // saturating value on some sentinel input.
15360        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15361        for upgrade_from in [
15362            vec![],
15363            vec![UpgradeFromEntry {
15364                from: "0.0.1".into(),
15365                instructions: vec![UpgradeInstruction::Restart],
15366            }],
15367            vec![
15368                UpgradeFromEntry {
15369                    from: "0.0.1".into(),
15370                    instructions: vec![UpgradeInstruction::Restart],
15371                },
15372                UpgradeFromEntry {
15373                    from: "0.0.2".into(),
15374                    instructions: vec![UpgradeInstruction::SoftPurge {
15375                        module: "demo".into(),
15376                    }],
15377                },
15378            ],
15379        ] {
15380            let c = caixa_with_upgrade_from(upgrade_from.clone());
15381            let first = c.upgrade_from();
15382            let second = c.upgrade_from();
15383            assert_eq!(
15384                first, second,
15385                "Caixa::upgrade_from must be idempotent — two \
15386                 successive calls on the same &self must return the \
15387                 same &[UpgradeFromEntry]",
15388            );
15389            assert_eq!(
15390                first.as_ptr(),
15391                second.as_ptr(),
15392                "Caixa::upgrade_from must borrow the underlying \
15393                 Vec<UpgradeFromEntry> storage — two successive calls \
15394                 must return slices with the same backing pointer (a \
15395                 fresh Vec<UpgradeFromEntry> clone would change the \
15396                 pointer on every call)",
15397            );
15398            assert_eq!(
15399                first,
15400                upgrade_from.as_slice(),
15401                "Caixa::upgrade_from must return :upgrade-from \
15402                 verbatim by borrow — got {first:?}, expected \
15403                 {upgrade_from:?}",
15404            );
15405        }
15406    }
15407
15408    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
15409
15410    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
15411        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15412        c.children = children;
15413        c
15414    }
15415
15416    #[test]
15417    fn children_returns_children_slice_verbatim_across_permutations() {
15418        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
15419        // outer-composite `&[ChildSpec]`-return slice-shape pin:
15420        // [`Caixa::children`] must return the `:children` typed
15421        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
15422        // the same backing buffer the raw `self.children.as_slice()`
15423        // field access borrows from, element-equal across every
15424        // representative fixture in the accept-set — `[]` (the "no
15425        // static children declared" arm every non-`Supervisor`-kind
15426        // `defcaixa` carries by `#[serde(default)]` and every
15427        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
15428        // a canonical single-child `Permanent` fixture (the shape
15429        // most `OneForOne` supervisors carry — a single long-running
15430        // worker child), a canonical multi-child list carrying every
15431        // typed restart-policy variant (`Permanent` / `Transient` /
15432        // `Temporary`), and a past-the-guard sentinel — a duplicate
15433        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
15434        // ([`crate::SupervisorSpec::validate`] rejects through
15435        // `DuplicateChildNome { nome: "w" }` but the accessor must
15436        // ship the raw slot verbatim so struct-literal fixtures
15437        // continue to expose the duplicate at the accessor boundary).
15438        //
15439        // Pins against a future silent detour that returned an owned
15440        // `Vec<ChildSpec>` (which would type-check but silently clone
15441        // on every accessor call, breaking the zero-cost projection
15442        // every peer sibling slice accessor carries), a `[dup, dup] →
15443        // [dup]` dedup collapse (which would silently absorb the
15444        // `DuplicateChildNome` refusal case at the accessor boundary
15445        // and the [`crate::StandardLayout::verify`] cross-child gate
15446        // would silently accept a struct-literal `Caixa` carrying the
15447        // drift), a reference to an operator-resolved overlay (the
15448        // future per-cluster `:children-overrides` slot — its
15449        // resolution must land at exactly this accessor body, not
15450        // silently divert the raw slot away from a second consumer),
15451        // or an axis-shuffled projection (a future detour that
15452        // reordered children through the accessor would silently
15453        // split the paired [`crate::StandardLayout::verify`] per-
15454        // supervisor gate's traversal input from the peer
15455        // [`Self::supervisor_view`] fold-in path's clone-order input,
15456        // since the OTP `RestForOne` restart strategy dispatches on
15457        // declared child order and axis reordering would silently
15458        // split the operator's per-cluster restart-fan-out order
15459        // from the caixa.lisp source-order).
15460        //
15461        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
15462        // accessor pin on the substrate primitive for M2 / M3 typed-
15463        // slot vec-carry axes — folds on the outer-`Caixa`
15464        // `&[Composite]` composite-slice sub-family the sibling
15465        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
15466        // (2a1f907) pin opened, peer at the outer altitude of the
15467        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
15468        // (bc92bce) accessor on the same OTP-supervisor static-child-
15469        // list axis.
15470        use crate::supervisor::{ChildSpec, RestartPolicy};
15471        let fixtures: Vec<Vec<ChildSpec>> = vec![
15472            vec![],
15473            vec![ChildSpec {
15474                caixa: "worker".into(),
15475                versao: "^0.1".into(),
15476                restart: RestartPolicy::Permanent,
15477            }],
15478            vec![
15479                ChildSpec {
15480                    caixa: "worker-a".into(),
15481                    versao: "^0.1".into(),
15482                    restart: RestartPolicy::Permanent,
15483                },
15484                ChildSpec {
15485                    caixa: "worker-b".into(),
15486                    versao: "^0.1".into(),
15487                    restart: RestartPolicy::Transient,
15488                },
15489                ChildSpec {
15490                    caixa: "worker-c".into(),
15491                    versao: "^0.1".into(),
15492                    restart: RestartPolicy::Temporary,
15493                },
15494            ],
15495            vec![
15496                ChildSpec {
15497                    caixa: "w".into(),
15498                    versao: "^0.1".into(),
15499                    restart: RestartPolicy::Permanent,
15500                },
15501                ChildSpec {
15502                    caixa: "w".into(),
15503                    versao: "^0.1".into(),
15504                    restart: RestartPolicy::Permanent,
15505                },
15506            ],
15507        ];
15508        for children in fixtures {
15509            let c = caixa_with_children(children.clone());
15510            assert_eq!(
15511                c.children(),
15512                children.as_slice(),
15513                "Caixa::children must return :children verbatim \
15514                 (got {:?}, expected {children:?})",
15515                c.children(),
15516            );
15517            assert_eq!(
15518                c.children(),
15519                c.children.as_slice(),
15520                "Caixa::children must element-equal the raw \
15521                 `self.children.as_slice()` field access across \
15522                 every value in the Vec<ChildSpec> accept-set",
15523            );
15524            assert_eq!(
15525                c.children().is_empty(),
15526                c.children.is_empty(),
15527                "Caixa::children().is_empty() must byte-equal \
15528                 self.children.is_empty() — a presence-bit drift \
15529                 would silently split the paired \
15530                 Caixa::declared_supervisor_slots supervisor-tree \
15531                 declared-slot enumerator's presence probe from the \
15532                 peer Caixa::supervisor_view typed-view composer's \
15533                 fold-in path",
15534            );
15535        }
15536    }
15537
15538    #[test]
15539    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
15540        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15541        // `:children` presence-probe arm must key off
15542        // [`Caixa::children`], not the raw
15543        // `!self.children.is_empty()` field-probe. Structurally: a
15544        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
15545        // "^0.1", restart: Permanent }], .. }` must push
15546        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
15547        // (the presence bit is non-empty, so the supervisor-tree
15548        // kind-coherence gate must surface the slot as "declared"),
15549        // and a `Caixa { children: vec![], .. }` must NOT push the
15550        // label (the "author omitted the slot entirely" arm — the
15551        // empty-slice partition the serde-default folds onto). The
15552        // pair jointly pins the accessor + declared-slot enumerator
15553        // composition: any future silent detour that had the accessor
15554        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
15555        // "__reserved__")` projection) would silently absorb the
15556        // "declared but degenerate" arm at the accessor boundary and
15557        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
15558        // kind-coherence gate would silently accept a struct-literal
15559        // `Caixa` carrying the drift.
15560        //
15561        // Peer of the sibling
15562        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
15563        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
15564        // same "the enumerator gate must route through the substrate-
15565        // primitive typed dispatch" discipline extended onto the
15566        // supervisor-tree `:children` composite-slice arm.
15567        use crate::supervisor::{ChildSpec, RestartPolicy};
15568        let c = caixa_with_children(vec![ChildSpec {
15569            caixa: "w".into(),
15570            versao: "^0.1".into(),
15571            restart: RestartPolicy::Permanent,
15572        }]);
15573        let slots = c.declared_supervisor_slots();
15574        assert!(
15575            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
15576            "declared_supervisor_slots must push \
15577             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
15578             non-empty — the accessor and the enumerator gate must \
15579             route through the same substrate-primitive typed \
15580             dispatch on the outer :children presence bit (got \
15581             slots={slots:?})",
15582        );
15583        let c = caixa_with_children(vec![]);
15584        let slots = c.declared_supervisor_slots();
15585        assert!(
15586            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
15587            "declared_supervisor_slots must NOT push \
15588             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
15589             empty — the author-omitted arm must route through the \
15590             accessor's empty-slice return unchanged (got \
15591             slots={slots:?})",
15592        );
15593    }
15594
15595    #[test]
15596    fn supervisor_view_children_arm_routes_through_accessor() {
15597        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
15598        // fold-in arm must key off [`Caixa::children`], not the raw
15599        // `self.children.clone()` field-clone. Structurally: a `Caixa {
15600        // kind: Supervisor, estrategia: Some(OneForOne), children:
15601        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
15602        // per-child list through the accessor into the typed
15603        // [`SupervisorSpec`] view's `children` field verbatim — every
15604        // entry the accessor surfaces must land in the view's
15605        // `children` slot in the same order. The pair jointly pins the
15606        // accessor + view-composer composition: any future silent
15607        // detour that had the accessor return a fresh-cloned
15608        // `Vec<ChildSpec>` copy would silently break the reference-
15609        // identity pin the peer `supervisor_view` fold-in path reads
15610        // from — the fold would clone once more per accessor call
15611        // instead of borrowing the storage buffer verbatim once.
15612        //
15613        // Peer of the sibling
15614        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
15615        // family) composition pin on the peer kind-gate arm — same
15616        // "the view composer must route through the substrate-
15617        // primitive typed dispatch" discipline extended onto the
15618        // per-`:children` fold-in arm, closing the supervisor-view
15619        // composer's routing invariant on the composite-slice input.
15620        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
15621        let mut c = caixa_with_children(vec![
15622            ChildSpec {
15623                caixa: "worker-a".into(),
15624                versao: "^0.1".into(),
15625                restart: RestartPolicy::Permanent,
15626            },
15627            ChildSpec {
15628                caixa: "worker-b".into(),
15629                versao: "^0.1".into(),
15630                restart: RestartPolicy::Transient,
15631            },
15632        ]);
15633        c.kind = crate::CaixaKind::Supervisor;
15634        c.estrategia = Some(RestartStrategy::OneForOne);
15635        let view = c
15636            .supervisor_view()
15637            .expect("Supervisor kind must produce a supervisor_view");
15638        assert_eq!(
15639            view.children(),
15640            c.children(),
15641            "supervisor_view must fold Caixa::children verbatim into \
15642             SupervisorSpec::children — the accessor and the view \
15643             composer must route through the same substrate-primitive \
15644             typed dispatch on the outer :children slice (got view \
15645             children={:?}, expected {:?})",
15646            view.children(),
15647            c.children(),
15648        );
15649    }
15650
15651    #[test]
15652    fn children_projects_slice_by_borrow() {
15653        // The by-borrow pin: [`Caixa::children`] returns
15654        // `&[ChildSpec]` by borrow — the returned slice borrows the
15655        // underlying `Vec<ChildSpec>` storage of the `:children` slot
15656        // and the accessor must not clone the backing `Vec` on every
15657        // call. Peer of the sibling outer top-level [`Caixa`]
15658        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
15659        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
15660        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15661        // `exe_projects_slice_by_borrow` 65d9527,
15662        // `servicos_projects_slice_by_borrow` 611f78b,
15663        // `deps_projects_slice_by_borrow` ad34b4e,
15664        // `deps_dev_projects_slice_by_borrow` f7fd81e,
15665        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
15666        // sibling outer top-level [`Caixa`] scalar-element and
15667        // composite-element `&[T]` axes — folds on the outer-`Caixa`
15668        // composite-element `&[Composite]` axis: the accessor's
15669        // returned slice must borrow from `&self` (the returned
15670        // reference's lifetime is tied to `&self`), and calling the
15671        // accessor twice on the same [`Caixa`] must yield slices
15672        // that are pointer-equal (the underlying byte-buffer is the
15673        // storage `Vec`'s allocation, not a fresh copy) as well as
15674        // value-equal (idempotent, no side effects on `&self`).
15675        //
15676        // Pins against a future silent detour that returned an owned
15677        // `Vec<ChildSpec>` (which would type-check but silently clone
15678        // on every call), a `&Vec<ChildSpec>` return (which would leak
15679        // the backing `Vec`'s grow/push/reserve surface no downstream
15680        // consumer reaches for), or a one-arm-only accessor that
15681        // returned a saturating value on some sentinel input.
15682        use crate::supervisor::{ChildSpec, RestartPolicy};
15683        for children in [
15684            vec![],
15685            vec![ChildSpec {
15686                caixa: "w".into(),
15687                versao: "^0.1".into(),
15688                restart: RestartPolicy::Permanent,
15689            }],
15690            vec![
15691                ChildSpec {
15692                    caixa: "worker-a".into(),
15693                    versao: "^0.1".into(),
15694                    restart: RestartPolicy::Permanent,
15695                },
15696                ChildSpec {
15697                    caixa: "worker-b".into(),
15698                    versao: "^0.1".into(),
15699                    restart: RestartPolicy::Transient,
15700                },
15701            ],
15702        ] {
15703            let c = caixa_with_children(children.clone());
15704            let first = c.children();
15705            let second = c.children();
15706            assert_eq!(
15707                first, second,
15708                "Caixa::children must be idempotent — two successive \
15709                 calls on the same &self must return the same \
15710                 &[ChildSpec]",
15711            );
15712            assert_eq!(
15713                first.as_ptr(),
15714                second.as_ptr(),
15715                "Caixa::children must borrow the underlying \
15716                 Vec<ChildSpec> storage — two successive calls must \
15717                 return slices with the same backing pointer (a fresh \
15718                 Vec<ChildSpec> clone would change the pointer on \
15719                 every call)",
15720            );
15721            assert_eq!(
15722                first,
15723                children.as_slice(),
15724                "Caixa::children must return :children verbatim by \
15725                 borrow — got {first:?}, expected {children:?}",
15726            );
15727        }
15728    }
15729
15730    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
15731
15732    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
15733        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15734        c.kind = CaixaKind::Aplicacao;
15735        c.membros = membros;
15736        c
15737    }
15738
15739    #[test]
15740    fn membros_returns_membros_slice_verbatim_across_permutations() {
15741        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
15742        // composite `&[Membro]`-return slice-shape pin:
15743        // [`Caixa::membros`] must return the `:membros` typed
15744        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
15745        // same backing buffer the raw `self.membros.as_slice()` field
15746        // access borrows from, element-equal across every
15747        // representative fixture in the accept-set — `[]` (the "no
15748        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
15749        // carries by `#[serde(default)]` and every partially-authored
15750        // Aplicacao carries before the
15751        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
15752        // canonical single-member fixture (the shape a minimal
15753        // Aplicacao carries — one Servico wrapping one contained
15754        // computation), a canonical multi-member list carrying three
15755        // distinct entries (the canonical checkout-shape Aplicacao —
15756        // cart / pricing / auth — every canonical example carries), and
15757        // a past-the-guard sentinel — a duplicate `:caixa`
15758        // `[("cart", ...), ("cart", ...)]` entry pair
15759        // ([`crate::AplicacaoSpec::validate`] rejects through
15760        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
15761        // the raw slot verbatim so struct-literal fixtures continue to
15762        // expose the duplicate at the accessor boundary).
15763        //
15764        // Pins against a future silent detour that returned an owned
15765        // `Vec<Membro>` (which would type-check but silently clone on
15766        // every accessor call, breaking the zero-cost projection every
15767        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
15768        // dedup collapse (which would silently absorb the
15769        // `DuplicateMembro` refusal case at the accessor boundary and
15770        // the [`crate::StandardLayout::verify`] cross-member gate would
15771        // silently accept a struct-literal `Caixa` carrying the drift),
15772        // a reference to an operator-resolved overlay (the future per-
15773        // cluster `:membros-overrides` slot — its resolution must land
15774        // at exactly this accessor body, not silently divert the raw
15775        // slot away from a second consumer), or an axis-shuffled
15776        // projection (a future detour that reordered members through
15777        // the accessor would silently split the paired
15778        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
15779        // traversal input from the peer [`Self::aplicacao_view`] fold-
15780        // in path's clone-order input, since the canonical `:contratos`
15781        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
15782        // read the member set through the same slice).
15783        //
15784        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
15785        // accessor pin on the substrate primitive for M2 / M3 typed-
15786        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
15787        // arm of the `&[Composite]` composite-slice sub-family the
15788        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
15789        // (2a1f907) and
15790        // `children_returns_children_slice_verbatim_across_permutations`
15791        // (c17b51e) pins opened, peer at the outer altitude of the
15792        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
15793        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
15794        // list axis.
15795        use crate::aplicacao::Membro;
15796        let fixtures: Vec<Vec<Membro>> = vec![
15797            vec![],
15798            vec![Membro {
15799                caixa: "cart".into(),
15800                versao: "^0.1".into(),
15801            }],
15802            vec![
15803                Membro {
15804                    caixa: "cart".into(),
15805                    versao: "^0.1".into(),
15806                },
15807                Membro {
15808                    caixa: "pricing".into(),
15809                    versao: "^0.2".into(),
15810                },
15811                Membro {
15812                    caixa: "auth".into(),
15813                    versao: "^1.0".into(),
15814                },
15815            ],
15816            vec![
15817                Membro {
15818                    caixa: "cart".into(),
15819                    versao: "^0.1".into(),
15820                },
15821                Membro {
15822                    caixa: "cart".into(),
15823                    versao: "^0.1".into(),
15824                },
15825            ],
15826        ];
15827        for membros in fixtures {
15828            let c = caixa_aplicacao_with_membros(membros.clone());
15829            assert_eq!(
15830                c.membros(),
15831                membros.as_slice(),
15832                "Caixa::membros must return :membros verbatim \
15833                 (got {:?}, expected {membros:?})",
15834                c.membros(),
15835            );
15836            assert_eq!(
15837                c.membros(),
15838                c.membros.as_slice(),
15839                "Caixa::membros must element-equal the raw \
15840                 `self.membros.as_slice()` field access across every \
15841                 value in the Vec<Membro> accept-set",
15842            );
15843            assert_eq!(
15844                c.membros().is_empty(),
15845                c.membros.is_empty(),
15846                "Caixa::membros().is_empty() must byte-equal \
15847                 self.membros.is_empty() — a presence-bit drift would \
15848                 silently split the paired Caixa::declared_mesh_slots \
15849                 mesh declared-slot enumerator's presence probe from \
15850                 the peer Caixa::aplicacao_view typed-view composer's \
15851                 fold-in path",
15852            );
15853        }
15854    }
15855
15856    #[test]
15857    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
15858        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
15859        // presence-probe arm must key off [`Caixa::membros`], not the
15860        // raw `!self.membros.is_empty()` field-probe. Structurally: a
15861        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
15862        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
15863        // declared-slot list (the presence bit is non-empty, so the
15864        // mesh kind-coherence gate must surface the slot as
15865        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
15866        // push the label (the "author omitted the slot entirely" arm
15867        // — the empty-slice partition the serde-default folds onto).
15868        // The pair jointly pins the accessor + declared-slot
15869        // enumerator composition: any future silent detour that had
15870        // the accessor collapse `[Membro { .. }]` to `[]` (a
15871        // `.filter(|m| m.nome() != "__reserved__")` projection) would
15872        // silently absorb the "declared but degenerate" arm at the
15873        // accessor boundary and the
15874        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
15875        // coherence gate would silently accept a struct-literal
15876        // `Caixa` carrying the drift.
15877        //
15878        // Peer of the sibling
15879        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
15880        // (2a1f907) and
15881        // `declared_supervisor_slots_children_arm_routes_through_accessor`
15882        // (c17b51e) composition pins on the M2 `:upgrade-from` /
15883        // `:children` composite-slice arms — same "the enumerator gate
15884        // must route through the substrate-primitive typed dispatch"
15885        // discipline extended onto the M3 `:membros` composite-slice
15886        // arm, opening the M3 arm of the declared-slot enumerator's
15887        // routing invariant.
15888        use crate::aplicacao::Membro;
15889        let c = caixa_aplicacao_with_membros(vec![Membro {
15890            caixa: "cart".into(),
15891            versao: "^0.1".into(),
15892        }]);
15893        let slots = c.declared_mesh_slots();
15894        assert!(
15895            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
15896            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
15897             `:membros` is non-empty — the accessor and the enumerator \
15898             gate must route through the same substrate-primitive \
15899             typed dispatch on the outer :membros presence bit (got \
15900             slots={slots:?})",
15901        );
15902        let c = caixa_aplicacao_with_membros(vec![]);
15903        let slots = c.declared_mesh_slots();
15904        assert!(
15905            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
15906            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
15907             when `:membros` is empty — the author-omitted arm must \
15908             route through the accessor's empty-slice return unchanged \
15909             (got slots={slots:?})",
15910        );
15911    }
15912
15913    #[test]
15914    fn aplicacao_view_membros_arm_routes_through_accessor() {
15915        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
15916        // fold-in arm must key off [`Caixa::membros`], not the raw
15917        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
15918        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
15919        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
15920        // member list through the accessor into the typed
15921        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
15922        // every entry the accessor surfaces must land in the view's
15923        // `membros` slot in the same order. The pair jointly pins the
15924        // accessor + view-composer composition: any future silent
15925        // detour that had the accessor return a fresh-cloned
15926        // `Vec<Membro>` copy would silently break the reference-
15927        // identity pin the peer `aplicacao_view` fold-in path reads
15928        // from — the fold would clone once more per accessor call
15929        // instead of borrowing the storage buffer verbatim once.
15930        //
15931        // Peer of the sibling
15932        // `aplicacao_view_politicas_arm_folds_through_accessor`
15933        // (5d23d29) /
15934        // `aplicacao_view_placement_arm_folds_through_accessor`
15935        // (4fb8074) /
15936        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
15937        // composition pins on the M3 `:politicas` / `:placement` /
15938        // `:entrada` outer-`Option<&Composite>` arms — extended here to
15939        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
15940        // closing the aplicacao-view composer's routing invariant on
15941        // the composite-slice input.
15942        use crate::aplicacao::Membro;
15943        let c = caixa_aplicacao_with_membros(vec![
15944            Membro {
15945                caixa: "cart".into(),
15946                versao: "^0.1".into(),
15947            },
15948            Membro {
15949                caixa: "pricing".into(),
15950                versao: "^0.2".into(),
15951            },
15952        ]);
15953        let view = c
15954            .aplicacao_view()
15955            .expect("Aplicacao kind must produce an aplicacao_view");
15956        assert_eq!(
15957            view.membros(),
15958            c.membros(),
15959            "aplicacao_view must fold Caixa::membros verbatim into \
15960             AplicacaoSpec::membros — the accessor and the view \
15961             composer must route through the same substrate-primitive \
15962             typed dispatch on the outer :membros slice (got view \
15963             membros={:?}, expected {:?})",
15964            view.membros(),
15965            c.membros(),
15966        );
15967    }
15968
15969    #[test]
15970    fn membros_projects_slice_by_borrow() {
15971        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
15972        // borrow — the returned slice borrows the underlying
15973        // `Vec<Membro>` storage of the `:membros` slot and the
15974        // accessor must not clone the backing `Vec` on every call.
15975        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
15976        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
15977        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
15978        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15979        // `exe_projects_slice_by_borrow` 65d9527,
15980        // `servicos_projects_slice_by_borrow` 611f78b,
15981        // `deps_projects_slice_by_borrow` ad34b4e,
15982        // `deps_dev_projects_slice_by_borrow` f7fd81e,
15983        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
15984        // `children_projects_slice_by_borrow` c17b51e) on the sibling
15985        // outer top-level [`Caixa`] scalar-element and composite-
15986        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
15987        // slot composite-element `&[Composite]` axis: the accessor's
15988        // returned slice must borrow from `&self` (the returned
15989        // reference's lifetime is tied to `&self`), and calling the
15990        // accessor twice on the same [`Caixa`] must yield slices that
15991        // are pointer-equal (the underlying byte-buffer is the storage
15992        // `Vec`'s allocation, not a fresh copy) as well as value-equal
15993        // (idempotent, no side effects on `&self`).
15994        //
15995        // Pins against a future silent detour that returned an owned
15996        // `Vec<Membro>` (which would type-check but silently clone on
15997        // every call), a `&Vec<Membro>` return (which would leak the
15998        // backing `Vec`'s grow/push/reserve surface no downstream
15999        // consumer reaches for), or a one-arm-only accessor that
16000        // returned a saturating value on some sentinel input.
16001        use crate::aplicacao::Membro;
16002        for membros in [
16003            vec![],
16004            vec![Membro {
16005                caixa: "cart".into(),
16006                versao: "^0.1".into(),
16007            }],
16008            vec![
16009                Membro {
16010                    caixa: "cart".into(),
16011                    versao: "^0.1".into(),
16012                },
16013                Membro {
16014                    caixa: "pricing".into(),
16015                    versao: "^0.2".into(),
16016                },
16017            ],
16018        ] {
16019            let c = caixa_aplicacao_with_membros(membros.clone());
16020            let first = c.membros();
16021            let second = c.membros();
16022            assert_eq!(
16023                first, second,
16024                "Caixa::membros must be idempotent — two successive \
16025                 calls on the same &self must return the same &[Membro]",
16026            );
16027            assert_eq!(
16028                first.as_ptr(),
16029                second.as_ptr(),
16030                "Caixa::membros must borrow the underlying Vec<Membro> \
16031                 storage — two successive calls must return slices with \
16032                 the same backing pointer (a fresh Vec<Membro> clone \
16033                 would change the pointer on every call)",
16034            );
16035            assert_eq!(
16036                first,
16037                membros.as_slice(),
16038                "Caixa::membros must return :membros verbatim by borrow \
16039                 — got {first:?}, expected {membros:?}",
16040            );
16041        }
16042    }
16043
16044    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
16045
16046    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
16047        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16048        c.kind = CaixaKind::Aplicacao;
16049        c.contratos = contratos;
16050        c
16051    }
16052
16053    fn contrato_http_for_test(
16054        de: &str,
16055        para: &str,
16056        endpoint: &str,
16057    ) -> crate::aplicacao::WitContract {
16058        crate::aplicacao::WitContract {
16059            de: de.into(),
16060            para: para.into(),
16061            wit: "wasi:http/proxy".into(),
16062            endpoint: Some(endpoint.into()),
16063            subject: None,
16064            slot: None,
16065        }
16066    }
16067
16068    #[test]
16069    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
16070        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
16071        // composite `&[WitContract]`-return slice-shape pin:
16072        // [`Caixa::contratos`] must return the `:contratos` typed
16073        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
16074        // over the same backing buffer the raw
16075        // `self.contratos.as_slice()` field access borrows from,
16076        // element-equal across every representative fixture in the
16077        // accept-set — `[]` (the "no contracts declared" arm every
16078        // non-`Aplicacao`-kind `defcaixa` carries by
16079        // `#[serde(default)]` and every leaf-Aplicacao with a single
16080        // member carries), a canonical single-edge fixture (the
16081        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
16082        // edge), and a canonical multi-edge fixture with three distinct
16083        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
16084        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
16085        //
16086        // Pins against a future silent detour that returned an owned
16087        // `Vec<WitContract>` (which would type-check but silently clone
16088        // on every accessor call, breaking the zero-cost projection
16089        // every peer sibling slice accessor carries), an axis-shuffled
16090        // projection (a future detour that reordered edges through the
16091        // accessor would silently split the paired
16092        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
16093        // traversal input from the peer [`Self::aplicacao_view`] fold-
16094        // in path's clone-order input, since every canonical
16095        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
16096        // seed dispatch reads the edge set through the same slice),
16097        // or a reference to an operator-resolved overlay (the future
16098        // per-cluster `:contratos-overrides` slot — its resolution
16099        // must land at exactly this accessor body, not silently divert
16100        // the raw slot away from a second consumer).
16101        //
16102        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
16103        // accessor pin on the substrate primitive for M2 / M3 typed-
16104        // slot vec-carry axes — closes the outer-`Caixa`
16105        // `&[Composite]` composite-slice sub-family the sibling M2
16106        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16107        // (2a1f907) and
16108        // `children_returns_children_slice_verbatim_across_permutations`
16109        // (c17b51e) pins opened and the M3
16110        // `membros_returns_membros_slice_verbatim_across_permutations`
16111        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
16112        // slot arm of the composite-slice sub-family. Peer at the outer
16113        // altitude of the closed inner-
16114        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
16115        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
16116        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
16117            vec![],
16118            vec![contrato_http_for_test("cart", "catalog", "/items")],
16119            vec![
16120                contrato_http_for_test("cart", "catalog", "/items"),
16121                contrato_http_for_test("cart", "pricing", "/price"),
16122                contrato_http_for_test("cart", "auth", "/whoami"),
16123            ],
16124        ];
16125        for contratos in fixtures {
16126            let c = caixa_aplicacao_with_contratos(contratos.clone());
16127            assert_eq!(
16128                c.contratos(),
16129                contratos.as_slice(),
16130                "Caixa::contratos must return :contratos verbatim \
16131                 (got {:?}, expected {contratos:?})",
16132                c.contratos(),
16133            );
16134            assert_eq!(
16135                c.contratos(),
16136                c.contratos.as_slice(),
16137                "Caixa::contratos must element-equal the raw \
16138                 `self.contratos.as_slice()` field access across every \
16139                 value in the Vec<WitContract> accept-set",
16140            );
16141            assert_eq!(
16142                c.contratos().is_empty(),
16143                c.contratos.is_empty(),
16144                "Caixa::contratos().is_empty() must byte-equal \
16145                 self.contratos.is_empty() — a presence-bit drift would \
16146                 silently split the paired Caixa::declared_mesh_slots \
16147                 mesh declared-slot enumerator's presence probe from \
16148                 the peer Caixa::aplicacao_view typed-view composer's \
16149                 fold-in path",
16150            );
16151        }
16152    }
16153
16154    #[test]
16155    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
16156        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
16157        // presence-probe arm must key off [`Caixa::contratos`], not the
16158        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
16159        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
16160        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
16161        // presence bit is non-empty, so the mesh kind-coherence gate
16162        // must surface the slot as "declared"), and a `Caixa {
16163        // contratos: vec![], .. }` must NOT push the label (the "author
16164        // omitted the slot entirely" arm — the empty-slice partition
16165        // the serde-default folds onto). The pair jointly pins the
16166        // accessor + declared-slot enumerator composition: any future
16167        // silent detour that had the accessor collapse
16168        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
16169        // "__reserved__")` projection) would silently absorb the
16170        // "declared but degenerate" arm at the accessor boundary and
16171        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16172        // coherence gate would silently accept a struct-literal
16173        // `Caixa` carrying the drift.
16174        //
16175        // Peer of the sibling
16176        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16177        // (2a1f907),
16178        // `declared_supervisor_slots_children_arm_routes_through_accessor`
16179        // (c17b51e), and
16180        // `declared_mesh_slots_membros_arm_routes_through_accessor`
16181        // (0f26987) composition pins on the M2 `:upgrade-from` /
16182        // `:children` / M3 `:membros` composite-slice arms — same "the
16183        // enumerator gate must route through the substrate-primitive
16184        // typed dispatch" discipline extended onto the M3 `:contratos`
16185        // composite-slice arm, closing the M3 mesh-slot arm of the
16186        // declared-slot enumerator's routing invariant on the
16187        // composite-slice inputs.
16188        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
16189            "cart", "catalog", "/items",
16190        )]);
16191        let slots = c.declared_mesh_slots();
16192        assert!(
16193            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
16194            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
16195             `:contratos` is non-empty — the accessor and the enumerator \
16196             gate must route through the same substrate-primitive \
16197             typed dispatch on the outer :contratos presence bit (got \
16198             slots={slots:?})",
16199        );
16200        let c = caixa_aplicacao_with_contratos(vec![]);
16201        let slots = c.declared_mesh_slots();
16202        assert!(
16203            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
16204            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
16205             when `:contratos` is empty — the author-omitted arm must \
16206             route through the accessor's empty-slice return unchanged \
16207             (got slots={slots:?})",
16208        );
16209    }
16210
16211    #[test]
16212    fn aplicacao_view_contratos_arm_routes_through_accessor() {
16213        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
16214        // fold-in arm must key off [`Caixa::contratos`], not the raw
16215        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
16216        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
16217        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
16218        // per-edge list through the accessor into the typed
16219        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
16220        // every entry the accessor surfaces must land in the view's
16221        // `contratos` slot in the same order. The pair jointly pins
16222        // the accessor + view-composer composition: a future silent
16223        // detour that had the accessor shuffle or drop an edge would
16224        // silently split the paired declared-slot enumerator's
16225        // presence bit from the typed-view composer's edge-list, a
16226        // two-consumer split at the enumerator and the view composer
16227        // far from the source `caixa.lisp`.
16228        //
16229        // Peer of the sibling
16230        // `aplicacao_view_membros_arm_routes_through_accessor`
16231        // (0f26987) composition pin on the M3 `:membros` outer-
16232        // `&[Composite]` composite-slice arm, closing the aplicacao-
16233        // view composer's routing invariant on the composite-slice
16234        // inputs at the outer altitude.
16235        let c = caixa_aplicacao_with_contratos(vec![
16236            contrato_http_for_test("cart", "catalog", "/items"),
16237            contrato_http_for_test("cart", "pricing", "/price"),
16238        ]);
16239        let view = c
16240            .aplicacao_view()
16241            .expect("Aplicacao kind must produce an aplicacao_view");
16242        assert_eq!(
16243            view.contratos(),
16244            c.contratos(),
16245            "aplicacao_view must fold Caixa::contratos verbatim into \
16246             AplicacaoSpec::contratos — the accessor and the view \
16247             composer must route through the same substrate-primitive \
16248             typed dispatch on the outer :contratos slice (got view \
16249             contratos={:?}, expected {:?})",
16250            view.contratos(),
16251            c.contratos(),
16252        );
16253    }
16254
16255    #[test]
16256    fn contratos_projects_slice_by_borrow() {
16257        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
16258        // by borrow — the returned slice borrows the underlying
16259        // `Vec<WitContract>` storage of the `:contratos` slot and the
16260        // accessor must not clone the backing `Vec` on every call.
16261        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
16262        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
16263        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16264        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16265        // `exe_projects_slice_by_borrow` 65d9527,
16266        // `servicos_projects_slice_by_borrow` 611f78b,
16267        // `deps_projects_slice_by_borrow` ad34b4e,
16268        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16269        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
16270        // `children_projects_slice_by_borrow` c17b51e,
16271        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
16272        // outer top-level [`Caixa`] scalar-element and composite-
16273        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
16274        // composite-element `&[Composite]` axis on the by-borrow pin:
16275        // the accessor's returned slice must borrow from `&self` (the
16276        // returned reference's lifetime is tied to `&self`), and
16277        // calling the accessor twice on the same [`Caixa`] must yield
16278        // slices that are pointer-equal (the underlying byte-buffer is
16279        // the storage `Vec`'s allocation, not a fresh copy) as well as
16280        // value-equal (idempotent, no side effects on `&self`).
16281        //
16282        // Pins against a future silent detour that returned an owned
16283        // `Vec<WitContract>` (which would type-check but silently clone
16284        // on every call), a `&Vec<WitContract>` return (which would
16285        // leak the backing `Vec`'s grow/push/reserve surface no
16286        // downstream consumer reaches for), or a one-arm-only accessor
16287        // that returned a saturating value on some sentinel input.
16288        for contratos in [
16289            vec![],
16290            vec![contrato_http_for_test("cart", "catalog", "/items")],
16291            vec![
16292                contrato_http_for_test("cart", "catalog", "/items"),
16293                contrato_http_for_test("cart", "pricing", "/price"),
16294            ],
16295        ] {
16296            let c = caixa_aplicacao_with_contratos(contratos.clone());
16297            let first = c.contratos();
16298            let second = c.contratos();
16299            assert_eq!(
16300                first, second,
16301                "Caixa::contratos must be idempotent — two successive \
16302                 calls on the same &self must return the same \
16303                 &[WitContract]",
16304            );
16305            assert_eq!(
16306                first.as_ptr(),
16307                second.as_ptr(),
16308                "Caixa::contratos must borrow the underlying \
16309                 Vec<WitContract> storage — two successive calls must \
16310                 return slices with the same backing pointer (a fresh \
16311                 Vec<WitContract> clone would change the pointer on \
16312                 every call)",
16313            );
16314            assert_eq!(
16315                first,
16316                contratos.as_slice(),
16317                "Caixa::contratos must return :contratos verbatim by \
16318                 borrow — got {first:?}, expected {contratos:?}",
16319            );
16320        }
16321    }
16322
16323    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
16324
16325    #[test]
16326    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
16327        // Load-bearing invariant: every multi-word top-level [`Caixa`]
16328        // serde-derived JSON key routes through a lifted `&'static str`
16329        // const. The Rust field names are `snake_case`
16330        // (`deps_dev` / `upgrade_from` / `max_restarts` /
16331        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
16332        // "camelCase")]` derive attribute maps each to the camelCase
16333        // byte-string the [`Caixa::to_lisp`] round-trip's
16334        // `serde_json::to_value(self)` step lands under before
16335        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
16336        // to the kebab-case `:deps-dev` / `:upgrade-from` /
16337        // `:max-restarts` / `:restart-window` author surface. Serialize
16338        // a fully-populated [`Caixa`] and pin that each canonical
16339        // byte-sequence appears verbatim in the JSON — a future
16340        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
16341        // verbatim-field-name flip at the derive attribute (any of
16342        // which would silently break every [`Caixa::to_lisp`]
16343        // round-trip and the future M4 operator-side manifest ingest's
16344        // `Value::get(<key>)` navigation) surfaces here as a build-time
16345        // test failure at `manifest.rs`, not as an apply-time
16346        // `.get(<stale-canonical-const>)` returning `None` far from the
16347        // derive-attr drift's commit. Same discipline the sibling
16348        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
16349        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
16350        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
16351        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
16352        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
16353        // [`UpgradeFromEntry`] per-entry axes — extended here to the
16354        // enclosing M0 [`Caixa`] top-level axis so the last of the four
16355        // multi-word top-level [`Caixa`] serde-derived JSON keys
16356        // (`depsDev`) joins the substrate's "one canonical byte-string
16357        // per typed serialized-key axis" discipline.
16358        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16359        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16360        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16361        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
16362        c.upgrade_from = vec![UpgradeFromEntry {
16363            from: "0.0.1".into(),
16364            instructions: vec![UpgradeInstruction::Restart],
16365        }];
16366        c.estrategia = Some(RestartStrategy::OneForOne);
16367        c.max_restarts = Some(3);
16368        c.restart_window = Some("60s".into());
16369        c.children = vec![ChildSpec {
16370            caixa: "child".into(),
16371            versao: "^0.1".into(),
16372            restart: RestartPolicy::Permanent,
16373        }];
16374        let json = serde_json::to_string(&c).unwrap();
16375        for key in [
16376            crate::render::CAIXA_KEY_DEPS_DEV,
16377            crate::render::M2_KEY_UPGRADE_FROM,
16378            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16379            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16380        ] {
16381            let quoted = format!("\"{key}\"");
16382            assert!(
16383                json.contains(&quoted),
16384                "serialized Caixa must carry the lifted top-level \
16385                 multi-word byte-sequence {quoted} verbatim in the JSON \
16386                 emission (got: {json})",
16387            );
16388        }
16389    }
16390
16391    #[test]
16392    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
16393        // Cross-axis drift-detection pin: a future collapse of the four
16394        // canonical [`Caixa`] top-level multi-word byte-strings onto the
16395        // same value (e.g. an accidental copy-paste flip of
16396        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
16397        // `"upgradeFrom"`) would silently reroute every downstream
16398        // `Value::get(<key>)` probe on one axis onto the sibling axis's
16399        // top-level entry and pass every propagation-probe test that
16400        // expected only the stale axis's value. Peer of the sibling
16401        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
16402        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
16403        let all = [
16404            crate::render::CAIXA_KEY_DEPS_DEV,
16405            crate::render::M2_KEY_UPGRADE_FROM,
16406            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16407            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16408        ];
16409        for (i, a) in all.iter().enumerate() {
16410            for b in all.iter().skip(i + 1) {
16411                assert_ne!(
16412                    a, b,
16413                    "Caixa top-level multi-word key consts must be \
16414                     pairwise-distinct canonical byte-sequences — got \
16415                     `{a}` == `{b}`",
16416                );
16417            }
16418        }
16419    }
16420
16421    #[test]
16422    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
16423        // Shape-pin: every [`Caixa`] top-level multi-word key const must
16424        // be a lowerCamelCase byte-sequence (no `snake_case`
16425        // underscores, no `kebab-case` hyphens, no leading colon, no
16426        // `PascalCase` leading capital, no whitespace / dots) — the
16427        // canonical shape the `#[serde(rename_all = "camelCase")]`
16428        // derive produces on [`Caixa`]. A future flip to a
16429        // non-camelCase attribute at the derive surfaces both here
16430        // (this test fails on the stale-constant shape) and at
16431        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16432        // (that test fails on the mismatch between const and derive).
16433        // Peer with `membro_key_consts_are_lower_camel_case_shape`
16434        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
16435        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
16436        for key in [
16437            crate::render::CAIXA_KEY_DEPS_DEV,
16438            crate::render::M2_KEY_UPGRADE_FROM,
16439            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16440            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16441        ] {
16442            assert!(
16443                !key.is_empty(),
16444                "Caixa top-level multi-word key const must be non-empty \
16445                 (got {key:?})"
16446            );
16447            let first = key.chars().next().unwrap();
16448            assert!(
16449                first.is_ascii_lowercase(),
16450                "Caixa top-level multi-word key const must lead with an \
16451                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
16452            );
16453            assert!(
16454                key.chars().all(|c| c.is_ascii_alphanumeric()),
16455                "Caixa top-level multi-word key const must be \
16456                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
16457                 whitespace (got {key:?})",
16458            );
16459        }
16460    }
16461
16462    #[test]
16463    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
16464        // Scalar-value pin: the byte-string the
16465        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
16466        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
16467        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
16468        // → `depsTest` matching a hypothetical per-test-target
16469        // vocabulary flip) lands as an edit to exactly one const AND
16470        // one derive attribute — the sibling
16471        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16472        // pin already ties the const to the derive attribute, so a
16473        // rebrand that touches only one side of the pair fails at
16474        // caixa-core build time. Same "scalar-value pin per const"
16475        // discipline the sibling
16476        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
16477        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
16478        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
16479        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
16480    }
16481
16482    #[test]
16483    fn caixa_key_deps_pins_canonical_byte_string() {
16484        // Scalar-value pin: the byte-string the
16485        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
16486        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
16487        // on the two-list dep-graph serialized-key axis — the sibling
16488        // pin covers the multi-word `deps_dev → depsDev` camelCase
16489        // arm, this pin covers the single-word `deps → deps` no-op arm
16490        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
16491        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
16492        // axis and the emitted JSON key equals the source-side field
16493        // name byte-for-byte). A future [`crate::Caixa::deps`] field
16494        // rename (`deps` → `dependencies` matching Cargo's verbatim
16495        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
16496        // hypothetical per-runtime-target vocabulary flip) OR an added
16497        // `#[serde(rename = "…")]` explicit override lands as an edit
16498        // to exactly one const AND one derive-attr / field name — the
16499        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
16500        // pin ties the const to the emitted JSON key, so a rebrand
16501        // that touches only one side of the pair fails at caixa-core
16502        // build time.
16503        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
16504    }
16505
16506    #[test]
16507    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
16508        // Load-bearing invariant on the single-word `deps` top-level
16509        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
16510        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
16511        // `serde_json::to_value(self)` step emits. Serialize a
16512        // populated [`Caixa`] whose `:deps` slot carries at least one
16513        // entry (the `#[serde(default)]` attribute on the field emits
16514        // an empty `[]` even without members, but a non-empty vec
16515        // additionally covers the codec's per-`Dep`-entry emission
16516        // path) and pin that `"deps"` appears verbatim in the JSON
16517        // emission — a future accidental `rename_all = "snake_case"` /
16518        // `"kebab-case"` flip at the derive attribute (or an added
16519        // `#[serde(rename = "…")]` explicit override on the field, or
16520        // a Rust field rename) would break every [`Caixa::to_lisp`]
16521        // round-trip and the future M4 operator-side manifest ingest's
16522        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
16523        // build-time test failure at `manifest.rs`, not as an
16524        // apply-time `.get(<stale-canonical-const>)` returning `None`
16525        // far from the drift's commit. Peer of the sibling
16526        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16527        // multi-word pin on the same M0 [`Caixa`] top-level
16528        // serialized-key axis, extended here to the single-word arm
16529        // the multi-word test's `rename_all = "camelCase"` sweep can't
16530        // reach (single-word `deps → deps` is a no-op the multi-word
16531        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
16532        // `\"restartWindow\"` byte-scan can never observe).
16533        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16534        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
16535        let json = serde_json::to_string(&c).unwrap();
16536        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
16537        assert!(
16538            json.contains(&quoted),
16539            "serialized Caixa must carry the lifted top-level `deps` \
16540             byte-sequence {quoted} verbatim in the JSON emission (got: \
16541             {json})",
16542        );
16543    }
16544
16545    #[test]
16546    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
16547        // Cross-axis drift-detection pin on the two-list dep-graph
16548        // renderer-side wire-key axis: a future collapse of the
16549        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
16550        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
16551        // same value (e.g. an accidental copy-paste flip of
16552        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
16553        // reroute every downstream `Value::get(<key>)` probe on one
16554        // axis onto the sibling axis's dep-list and pass every
16555        // propagation-probe test that expected only the stale axis's
16556        // value — a dev-only dep would land in the runtime closure at
16557        // publish time, or a runtime dep would be excluded from the
16558        // published lacre. Peer of the sibling four-way distinct pin
16559        // on the top-level multi-word tetrad
16560        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
16561        // and the two-way pin on the sibling
16562        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
16563        // author-facing arm (4da6fba's test), extended here to the
16564        // renderer-side wire-key arm of the same two-list dep-graph
16565        // axis so both halves of the "one canonical byte-string per
16566        // typed axis per (author, wire)" grid carry the same
16567        // distinct-ness discipline.
16568        assert_ne!(
16569            crate::render::CAIXA_KEY_DEPS,
16570            crate::render::CAIXA_KEY_DEPS_DEV,
16571            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
16572             canonical byte-sequences on the two-list dep-graph \
16573             renderer-side wire-key axis"
16574        );
16575    }
16576
16577    // ── DepList / Caixa::push_dep pin ────────────────────────────────
16578    //
16579    // The compounding pin: the two-arm closed-set typed enum
16580    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
16581    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
16582    // consumer of the top-level manifest's dep-mutation surface reads
16583    // through, and the typed dispatch [`Caixa::push_dep`] on the
16584    // substrate primitive folds the "select list → check within-list
16585    // dup → push" cascade onto one method call. Prior to this landing
16586    // the two axes lived across two `&'static str` constants
16587    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
16588    // set type carrying the pair; the `feira add` mutation site's
16589    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
16590    // caixa.deps }` dispatch expressed no compile-time link back to
16591    // the substrate primitive, and a future third dep-list axis would
16592    // have silently split at every open-coded mutation site.
16593
16594    #[test]
16595    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
16596        // Every arm returns the same `&'static str` the substrate's
16597        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
16598        // constants carry. A future rebrand on either constant reaches
16599        // the enum through one edit; a regression to inline literals
16600        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
16601        // quotes from the wire-format constants every consumer routes
16602        // through and this pin flags it at build time.
16603        assert_eq!(
16604            crate::dep::DepList::Prod.as_str(),
16605            crate::render::DEP_AUTHOR_KEY_DEPS
16606        );
16607        assert_eq!(
16608            crate::dep::DepList::Dev.as_str(),
16609            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
16610        );
16611    }
16612
16613    #[test]
16614    fn dep_list_display_routes_through_as_str() {
16615        // Same as-str-through-Display convergence discipline the
16616        // sibling closed-set typed enums carry — a `format!("{list}")`
16617        // call must land byte-for-byte on the accessor's return so a
16618        // future consumer that formats the enum for a diagnostic line
16619        // reaches the same wire-format constant the wire-format
16620        // producers do.
16621        assert_eq!(
16622            format!("{}", crate::dep::DepList::Prod),
16623            crate::dep::DepList::Prod.as_str()
16624        );
16625        assert_eq!(
16626            format!("{}", crate::dep::DepList::Dev),
16627            crate::dep::DepList::Dev.as_str()
16628        );
16629    }
16630
16631    #[test]
16632    fn dep_list_all_enumerates_every_variant_once() {
16633        // Exhaustive-iteration pin — every arm appears exactly once in
16634        // `ALL`, matching the closed set the compiler enforces on the
16635        // sibling `match self` arms. A future variant addition that
16636        // extends only one method's match without extending `ALL`
16637        // would silently drop the new arm from every consumer that
16638        // iterates the slice.
16639        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
16640        assert!(variants.contains(&crate::dep::DepList::Prod));
16641        assert!(variants.contains(&crate::dep::DepList::Dev));
16642        assert_eq!(variants.len(), 2);
16643    }
16644
16645    #[test]
16646    fn push_dep_routes_to_deps_slot_on_prod_arm() {
16647        // The `Prod` arm dispatches to the runtime-closure `:deps`
16648        // slot every downstream lacre-pipeline consumer resolves at
16649        // build time. A future arm that regressed to inline `&mut
16650        // self.deps_dev` on the `Prod` path would silently reroute
16651        // every runtime dep into the dev-only closure at publish time
16652        // — this pin refuses that regression.
16653        let src = Caixa::template("host");
16654        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16655        let before_deps = caixa.deps().len();
16656        let before_deps_dev = caixa.deps_dev().len();
16657        let dep = Dep {
16658            nome: "caixa-teia".to_string(),
16659            versao: "^0.1".to_string(),
16660            fonte: None,
16661            opcional: false,
16662            caracteristicas: Vec::new(),
16663        };
16664        caixa
16665            .push_dep(crate::dep::DepList::Prod, dep)
16666            .expect("first push into :deps succeeds");
16667        assert_eq!(caixa.deps().len(), before_deps + 1);
16668        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
16669        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
16670    }
16671
16672    #[test]
16673    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
16674        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
16675        // must dispatch to the dev-only-closure `:deps-dev` slot every
16676        // downstream test-facing artifact resolver reads. A future
16677        // regression that inverted the two arms would silently route
16678        // every dev-only dep into the runtime closure at publish time
16679        // and this pin catches it before the drift ships.
16680        let src = Caixa::template("host");
16681        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16682        let dep = Dep {
16683            nome: "tatara-check".to_string(),
16684            versao: "*".to_string(),
16685            fonte: None,
16686            opcional: false,
16687            caracteristicas: Vec::new(),
16688        };
16689        caixa
16690            .push_dep(crate::dep::DepList::Dev, dep)
16691            .expect("first push into :deps-dev succeeds");
16692        assert!(caixa.deps().is_empty());
16693        assert_eq!(caixa.deps_dev().len(), 1);
16694        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
16695    }
16696
16697    #[test]
16698    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
16699        // Within-list dup check routes through the canonical
16700        // [`DepError::DuplicateNome`] carrier — the substrate's typed
16701        // diagnostic for the same axis [`Caixa::validate_deps`]'s
16702        // parse-time [`crate::render::insert_first_seen`] walk raises
16703        // on. Prior to the lift the mutation site's inline
16704        // `bail!("dep '{}' already declared", …)` string-diagnostic
16705        // path expressed no through-line back to the typed error;
16706        // routing every dep-list refusal through one carrier means an
16707        // author reading a `feira add` refusal and a `feira build`
16708        // refusal reaches for the same corrective surface without
16709        // switching diagnostic idioms.
16710        let src = Caixa::template("host");
16711        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16712        let dep = Dep {
16713            nome: "caixa-teia".to_string(),
16714            versao: "^0.1".to_string(),
16715            fonte: None,
16716            opcional: false,
16717            caracteristicas: Vec::new(),
16718        };
16719        caixa
16720            .push_dep(crate::dep::DepList::Prod, dep.clone())
16721            .expect("first push succeeds");
16722        let dup = Dep {
16723            nome: "caixa-teia".to_string(),
16724            versao: "^0.2".to_string(),
16725            fonte: None,
16726            opcional: false,
16727            caracteristicas: Vec::new(),
16728        };
16729        let err = caixa
16730            .push_dep(crate::dep::DepList::Prod, dup)
16731            .expect_err("second push with same :nome refuses");
16732        assert_eq!(
16733            err,
16734            DepError::DuplicateNome {
16735                nome: "caixa-teia".to_string(),
16736                list: crate::render::DEP_AUTHOR_KEY_DEPS,
16737            }
16738        );
16739        // The refused mutation must not corrupt the target list —
16740        // exactly one entry lives past the refusal, matching the
16741        // canonical single-source-of-truth invariant `Caixa::deps()`
16742        // carries.
16743        assert_eq!(caixa.deps().len(), 1);
16744    }
16745
16746    #[test]
16747    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
16748        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
16749        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
16750        // `list` payload so a future author reading the refusal grep's
16751        // for the correct `:deps-dev` block in their `caixa.lisp`,
16752        // not the sibling `:deps` block the runtime closure resolves.
16753        let src = Caixa::template("host");
16754        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16755        let dep = Dep {
16756            nome: "tatara-check".to_string(),
16757            versao: "*".to_string(),
16758            fonte: None,
16759            opcional: false,
16760            caracteristicas: Vec::new(),
16761        };
16762        caixa
16763            .push_dep(crate::dep::DepList::Dev, dep.clone())
16764            .expect("first push succeeds");
16765        let err = caixa
16766            .push_dep(crate::dep::DepList::Dev, dep)
16767            .expect_err("second push with same :nome refuses");
16768        assert!(matches!(
16769            err,
16770            DepError::DuplicateNome {
16771                ref nome,
16772                list,
16773            } if nome == "tatara-check"
16774                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
16775        ));
16776    }
16777
16778    #[test]
16779    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
16780        // The within-list dup check is scoped to the target arm — a
16781        // caixa may legitimately carry the same `:nome` under both
16782        // `:deps` and `:deps-dev` (though the substrate's peer
16783        // [`crate::Caixa::validate_deps`] walk still refuses the
16784        // shape at parse time; the mutation-site refusal is scoped to
16785        // the mutation-site's list to match the peer parse-time
16786        // per-list [`crate::render::insert_first_seen`] discipline).
16787        // The two arms hold independent seen-sets.
16788        let src = Caixa::template("host");
16789        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16790        let dep_prod = Dep {
16791            nome: "shared".to_string(),
16792            versao: "^0.1".to_string(),
16793            fonte: None,
16794            opcional: false,
16795            caracteristicas: Vec::new(),
16796        };
16797        let dep_dev = Dep {
16798            nome: "shared".to_string(),
16799            versao: "*".to_string(),
16800            fonte: None,
16801            opcional: false,
16802            caracteristicas: Vec::new(),
16803        };
16804        caixa
16805            .push_dep(crate::dep::DepList::Prod, dep_prod)
16806            .expect("push into :deps succeeds");
16807        caixa
16808            .push_dep(crate::dep::DepList::Dev, dep_dev)
16809            .expect("push same :nome into :deps-dev succeeds");
16810        assert_eq!(caixa.deps().len(), 1);
16811        assert_eq!(caixa.deps_dev().len(), 1);
16812    }
16813
16814    #[test]
16815    fn deps_of_prod_returns_the_deps_slot_verbatim() {
16816        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
16817        // accessor must project onto the runtime-closure `:deps` slot —
16818        // element-equal and length-equal to the sibling per-slot
16819        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
16820        // A future arm that regressed to `self.deps_dev()` on the `Prod`
16821        // path would silently reroute every downstream typed-dispatch
16822        // walker (the [`Caixa::validate_deps`] per-list
16823        // [`crate::render::insert_first_seen`] dedup walk, any future
16824        // per-axis-parametrised consumer) into the sibling dev-only
16825        // closure and this pin refuses that regression.
16826        let src = Caixa::template("host");
16827        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16828        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
16829        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
16830        let dep = Dep {
16831            nome: "caixa-teia".to_string(),
16832            versao: "^0.1".to_string(),
16833            fonte: None,
16834            opcional: false,
16835            caracteristicas: Vec::new(),
16836        };
16837        caixa
16838            .push_dep(crate::dep::DepList::Prod, dep.clone())
16839            .expect("push into :deps succeeds");
16840        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
16841        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
16842        assert_eq!(
16843            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
16844            "caixa-teia"
16845        );
16846    }
16847
16848    #[test]
16849    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
16850        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
16851        // [`Caixa::deps_of`] must project onto the dev-only-closure
16852        // `:deps-dev` slot, element-equal and length-equal to the
16853        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
16854        // future regression that inverted the two arms would silently
16855        // route every dev-list walker onto the runtime closure and this
16856        // pin catches it before the drift ships.
16857        let src = Caixa::template("host");
16858        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16859        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
16860        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
16861        let dep = Dep {
16862            nome: "tatara-check".to_string(),
16863            versao: "*".to_string(),
16864            fonte: None,
16865            opcional: false,
16866            caracteristicas: Vec::new(),
16867        };
16868        caixa
16869            .push_dep(crate::dep::DepList::Dev, dep)
16870            .expect("push into :deps-dev succeeds");
16871        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
16872        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
16873        assert_eq!(
16874            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
16875            "tatara-check"
16876        );
16877    }
16878
16879    #[test]
16880    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
16881        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
16882        // [`Caixa::deps_of`] must land on the same two-slot partition the
16883        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
16884        // expose — the canonical dispatch a future per-axis-parametrised
16885        // walker (a future `feira app graph` per-list dep summary, a
16886        // future M4 per-cluster dev-closure-audit overlay the CR
16887        // materializer resolves per-CR) reads through. Prior to the
16888        // lift the two-block iteration lived open-coded at every walker,
16889        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
16890        // §I) would have had to grow a third block at every consumer.
16891        // A regression that dropped the `Dev` arm from `ALL` would flip
16892        // the collected pairs to `[(":deps", &[])]` alone and this pin
16893        // refuses that shape.
16894        let src = Caixa::template("host");
16895        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16896        let prod_dep = Dep {
16897            nome: "caixa-teia".to_string(),
16898            versao: "^0.1".to_string(),
16899            fonte: None,
16900            opcional: false,
16901            caracteristicas: Vec::new(),
16902        };
16903        let dev_dep = Dep {
16904            nome: "tatara-check".to_string(),
16905            versao: "*".to_string(),
16906            fonte: None,
16907            opcional: false,
16908            caracteristicas: Vec::new(),
16909        };
16910        caixa
16911            .push_dep(crate::dep::DepList::Prod, prod_dep)
16912            .expect("push into :deps succeeds");
16913        caixa
16914            .push_dep(crate::dep::DepList::Dev, dev_dep)
16915            .expect("push into :deps-dev succeeds");
16916        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
16917            .iter()
16918            .map(|&list| {
16919                let slice = caixa.deps_of(list);
16920                (list.as_str(), slice.len(), slice[0].nome())
16921            })
16922            .collect();
16923        assert_eq!(
16924            collected,
16925            vec![
16926                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
16927                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
16928            ]
16929        );
16930    }
16931
16932    #[test]
16933    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
16934        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
16935        // must route its per-list [`crate::render::insert_first_seen`]
16936        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
16937        // rather than the pre-lift open-coded two-block iteration over
16938        // `self.deps()` + `self.deps_dev()`. A regression that dropped
16939        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
16940        // stop refusing within-list dups on the sibling arm; a
16941        // regression that flipped the arm-to-list-key mapping
16942        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
16943        // diagnostic surface. Both drifts surface here through a paired
16944        // duplicate-name refusal per arm plus an offending-list-key
16945        // check on the emitted [`DepError::DuplicateNome`] carrier.
16946        for &list in crate::dep::DepList::ALL {
16947            let src = Caixa::template("host");
16948            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
16949            let dup = Dep {
16950                nome: "twin".to_string(),
16951                versao: "^0.1".to_string(),
16952                fonte: None,
16953                opcional: false,
16954                caracteristicas: Vec::new(),
16955            };
16956            match list {
16957                crate::dep::DepList::Prod => {
16958                    caixa.deps.push(dup.clone());
16959                    caixa.deps.push(dup);
16960                }
16961                crate::dep::DepList::Dev => {
16962                    caixa.deps_dev.push(dup.clone());
16963                    caixa.deps_dev.push(dup);
16964                }
16965            }
16966            let err = caixa
16967                .validate_deps()
16968                .expect_err("within-list duplicate :nome must refuse");
16969            assert_eq!(
16970                err,
16971                DepError::DuplicateNome {
16972                    nome: "twin".to_string(),
16973                    list: list.as_str(),
16974                },
16975                "validate_deps on {list} arm must emit \
16976                 DepError::DuplicateNome carrying the arm's own \
16977                 as_str() diagnostic — the arm-to-list-key mapping \
16978                 flowed through DepList::ALL + Caixa::deps_of"
16979            );
16980        }
16981    }
16982}