Skip to main content

caixa_core/
manifest.rs

1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4use tatara_lisp::DeriveTataraDomain;
5
6use thiserror::Error;
7
8use crate::{
9    CaixaKind, Dep,
10    behavior::BehaviorSpec,
11    dep::DepError,
12    limits::LimitsSpec,
13    render::{
14        PathShapeViolation, is_computeunit_yaml_extension, is_git_repo_url, is_lisp_extension,
15        is_sandboxed_relative_path,
16    },
17    supervisor::SupervisorSpec,
18    upgrade::UpgradeFromEntry,
19};
20
21/// Top-level manifest for a caixa (a tatara-lisp package).
22///
23/// Authored as `caixa.lisp`:
24///
25/// ```lisp
26/// (defcaixa
27///   :nome        "pangea-tatara-aws"
28///   :versao      "0.1.0"
29///   :kind        Biblioteca
30///   :edicao      "2026"
31///   :descricao   "AWS provider caixa for tatara-lisp"
32///   :repositorio "github:pleme-io/pangea-tatara-aws"
33///   :licenca     "MIT"
34///   :autores     ("pleme-io")
35///   :etiquetas   ("iac" "aws" "pangea")
36///   :deps        ((:nome "caixa-teia"    :versao "^0.1")
37///                 (:nome "iac-forge-ir"  :versao "^0.5"))
38///   :deps-dev    ((:nome "tatara-check"  :versao "*"))
39///   :bibliotecas ("lib/pangea-tatara-aws.lisp"))
40/// ```
41///
42/// Because `Caixa` derives [`tatara_lisp::domain::TataraDomain`], the manifest
43/// is parsed directly by the tatara-lisp compiler — an ill-formed manifest is
44/// a compile error, not a runtime error.
45#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone, PartialEq)]
46#[serde(rename_all = "camelCase")]
47#[tatara(keyword = "defcaixa")]
48pub struct Caixa {
49    /// Package name — the canonical string used in `:deps`, the registry, and
50    /// the default lib/exe entry names.
51    pub nome: String,
52
53    /// Package version — a semver literal like `"0.1.0"`. Parsed lazily via
54    /// [`crate::CaixaVersion::parse`].
55    pub versao: String,
56
57    /// What this caixa produces. See [`CaixaKind`].
58    pub kind: CaixaKind,
59
60    /// Language edition — determines macro surface + compatibility flags.
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub edicao: Option<String>,
63
64    /// Free-form description shown in the registry listing.
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub descricao: Option<String>,
67
68    /// Homepage or repo URL.
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub repositorio: Option<String>,
71
72    /// SPDX license expression — `"MIT"`, `"Apache-2.0 OR MIT"`, etc.
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub licenca: Option<String>,
75
76    /// Authors — free-form strings.
77    #[serde(default)]
78    pub autores: Vec<String>,
79
80    /// Topical tags used for registry search.
81    #[serde(default)]
82    pub etiquetas: Vec<String>,
83
84    /// Runtime dependencies.
85    #[serde(default)]
86    pub deps: Vec<Dep>,
87
88    /// Development-only dependencies (tests, lint, bench).
89    #[serde(default)]
90    pub deps_dev: Vec<Dep>,
91
92    /// Paths to executable entry points (relative to the package root).
93    /// Required when `:kind Binario`.
94    #[serde(default)]
95    pub exe: Vec<String>,
96
97    /// Paths to library entry points (relative to the package root).
98    /// First entry is the canonical `lib/<nome>.lisp`; when omitted under
99    /// `:kind Biblioteca`, the layout check expects `lib/<nome>.lisp`.
100    #[serde(default)]
101    pub bibliotecas: Vec<String>,
102
103    /// Paths to service manifests (relative to the package root).
104    /// Required when `:kind Servico`.
105    #[serde(default)]
106    pub servicos: Vec<String>,
107
108    // ── M2 typed-substrate extensions per theory/ABSORPTION-ROADMAP.md ──
109    //
110    // All four are optional + default to "absent"; existing caixas
111    // round-trip unchanged. Each maps onto a prior-art primitive named
112    // in theory/INSPIRATIONS.md:
113    //
114    //   :limits        — Lunatic per-process limits (§III.1)
115    //   :behavior      — OTP gen_server callbacks  (§II.3)
116    //   :upgrade-from  — OTP appup migration       (§II.4)
117    //   :estrategia    — OTP supervisor strategy   (§II.2 + §III.2)
118    //   :children      — OTP supervisor children    (§II.2 + §III.2)
119    //
120    // The supervisor slots are flat on Caixa (vs nested under a
121    // SupervisorSpec sub-form) to keep tatara-lisp authoring at one
122    // level of nesting; SupervisorSpec exists for validation +
123    // composition convenience (`Caixa::supervisor_view()`).
124    /// Lunatic-style per-process resource limits. None = unbounded.
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub limits: Option<LimitsSpec>,
127
128    /// OTP-shaped behavior callbacks for Servico-kind caixas.
129    /// Authored as `(:on-init "..." :on-call "..." …)`.
130    #[serde(default, skip_serializing_if = "Option::is_none")]
131    pub behavior: Option<BehaviorSpec>,
132
133    /// OTP appup — declarative upgrade instructions per prior version.
134    /// Empty list = no hot-upgrade path declared (caller falls back to
135    /// `:Restart` strategy).
136    #[serde(default)]
137    pub upgrade_from: Vec<UpgradeFromEntry>,
138
139    /// OTP supervisor strategy. Required when `:kind Supervisor`;
140    /// ignored otherwise.
141    #[serde(default, skip_serializing_if = "Option::is_none")]
142    pub estrategia: Option<crate::supervisor::RestartStrategy>,
143
144    /// Max restarts before the supervisor itself fails. Defaults via
145    /// SupervisorSpec at validation time.
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub max_restarts: Option<u32>,
148
149    /// Sliding window for `max_restarts`. Authored as a duration
150    /// string (`"60s"`, `"5m"`).
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub restart_window: Option<String>,
153
154    /// Static children of a supervisor. Required for OneForOne /
155    /// OneForAll / RestForOne; must be empty for SimpleOneForOne.
156    #[serde(default)]
157    pub children: Vec<crate::supervisor::ChildSpec>,
158
159    // ── M3 Aplicacao slots (theory/MESH-COMPOSITION.md) ─────────────────
160    //
161    // Required when :kind Aplicacao; ignored otherwise.
162    // Composed into a typed AplicacaoSpec via Caixa::aplicacao_view().
163    /// Member Servicos that make up this Aplicacao. Each is a
164    /// caixa-name + version-constraint pair. Required for Aplicacao.
165    #[serde(default)]
166    pub membros: Vec<crate::aplicacao::Membro>,
167
168    /// WIT-typed inter-Servico contracts. Each `:de` and `:para`
169    /// must reference a name in `:membros`.
170    #[serde(default)]
171    pub contratos: Vec<crate::aplicacao::WitContract>,
172
173    /// Mesh-level policies (timeout, retries, circuit-breaker, mTLS,
174    /// rate-limit). Apply to every contrato unless overridden per-edge
175    /// in M4.
176    #[serde(default, skip_serializing_if = "Option::is_none")]
177    pub politicas: Option<crate::aplicacao::MeshPolicy>,
178
179    /// Placement strategy across the cluster fleet
180    /// (single-node | replicated | sharded).
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub placement: Option<crate::aplicacao::Placement>,
183
184    /// External entry point — gateway / ingress shape. Optional;
185    /// only for public Aplicacaos.
186    #[serde(default, skip_serializing_if = "Option::is_none")]
187    pub entrada: Option<crate::aplicacao::Entrada>,
188
189    // ── Acao slot (CANTEIRO §7.1-C) ──────────────────────────────────────
190    //
191    // Required when :kind Acao; ignored otherwise (mirrors the M2/
192    // supervisor-tree/M3 slot triads above — a declared-but-foreign `:ci`
193    // is a `LayoutError::CiOnNonAcao` build error, not a silent drop).
194    /// Typed CI run — a repo's CI run as a set of typed nodes + their
195    /// dependency edges. Required for `:kind Acao`; validated (not
196    /// rendered) by the `caixa-actions` renderer via
197    /// `canteiro_types::decompose`. See `caixa-actions`' crate docs for
198    /// the M0 validate-only contract.
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub ci: Option<canteiro_types::CiRun>,
201}
202
203/// Why reading a manifest into a [`Caixa`] failed.
204///
205/// Split from [`ManifestError`] (which reports a *parsed* manifest that is
206/// semantically wrong) because the two answer different questions, and the
207/// distinction is the whole point of this type: `ManifestError` means "your
208/// caixa is wrong", `LeituraError::DialetoEstrangeiro` means "this file is not
209/// a caixa".
210#[derive(Debug, thiserror::Error)]
211pub enum LeituraError {
212    /// The source is not readable as a `(defcaixa …)` package manifest — bad
213    /// syntax, a wrong head symbol, an unknown or mistyped slot.
214    ///
215    /// `#[source]`, not `#[error(transparent)]`. Transparent delegates
216    /// `source()` past the inner error to ITS source, which drops the
217    /// `LispError` off the cause chain — and `feira`'s
218    /// `load_caixa_parse_error_preserves_underlying_lisp_error_on_chain`
219    /// pins that a caller can `downcast_ref::<tatara_lisp::LispError>()`
220    /// through an anyhow context to read the typed payload. That pin caught
221    /// this exact regression when the variant first landed transparent.
222    #[error("{0}")]
223    Leitura(
224        #[source]
225        #[from]
226        tatara_lisp::LispError,
227    ),
228
229    /// The source IS a well-formed `(defcaixa …)` form, but of a different
230    /// declaration than this crate's.
231    ///
232    /// The variant that did not exist before, and whose absence is the defect.
233    /// A `(defcaixa :name "x" :ecosystem :go …)` used to reach the derive's
234    /// `parse_kwargs_strict` and come back as an unknown-keyword rejection —
235    /// byte-identical in shape to a typo in a real manifest. Measured over the
236    /// org checkout on 2026-07-31, that shape is the MAJORITY of the corpus, so
237    /// the confusing error was also the common one.
238    ///
239    /// Carrying the dialect means a consumer can branch on "not mine" without
240    /// re-parsing, and a census can count it. Every user-facing byte-string
241    /// (canonical keyword, one-line description, consuming crate) is a
242    /// projection of [`crate::dialeto::CaixaDialeto`] — the variant stores the
243    /// typed dialect and the `#[error]` template calls
244    /// [`CaixaDialeto::palavra_canonica`] /
245    /// [`CaixaDialeto::descricao`] / [`CaixaDialeto::consumidor`] on it, so
246    /// the three axes cannot silently diverge from the classification. Prior
247    /// to this closure the variant carried each accessor's return value as a
248    /// stored `&'static str` snapshot alongside `dialeto`, and the sole
249    /// constructor at [`Caixa::from_lisp`] filled all four fields — a caller
250    /// could construct `DialetoEstrangeiro { dialeto: Molde,
251    /// palavra_canonica: "defcaixa", … }` and every downstream consumer
252    /// (Display, ad-hoc audit, future JSON serialization) would silently
253    /// disagree with `dialeto.palavra_canonica() == "defmolde"`. The typed
254    /// enum owns the projections; the variant only carries the axis.
255    #[error(
256        "this is a `{palavra}` declaration ({desc}), read by \
257         {cons} — not a caixa-core package manifest. `defcaixa` is the \
258         tatara-lisp package manifest (`:nome :versao :kind :deps …`); the two \
259         are different declarations that shared one keyword until 2026-07-31",
260        palavra = dialeto.palavra_canonica(),
261        desc = dialeto.descricao(),
262        cons = dialeto.consumidor()
263    )]
264    DialetoEstrangeiro {
265        /// Which declaration this actually is. Sole authoritative axis;
266        /// every user-facing projection routes through
267        /// [`crate::dialeto::CaixaDialeto`]'s typed accessors so the four
268        /// axes cannot silently disagree.
269        dialeto: crate::dialeto::CaixaDialeto,
270    },
271
272    /// Not a manifest declaration at all.
273    #[error(transparent)]
274    Dialeto(#[from] crate::dialeto::DialetoError),
275}
276
277impl Caixa {
278    /// Parse a `caixa.lisp` source string to a typed `Caixa`.
279    ///
280    /// Classifies the dialect **before** parsing. A `(defcaixa …)` of another
281    /// declaration is [`LeituraError::DialetoEstrangeiro`], naming what it is
282    /// and who reads it, instead of an unknown-keyword rejection that reads as
283    /// "your manifest is broken".
284    ///
285    /// The ordering is load-bearing. Handing a foreign dialect to the derive
286    /// first and interpreting the failure afterwards would mean guessing from
287    /// an error message, and the guess would be wrong for every file whose
288    /// first unknown slot happens to be one both schemas could plausibly carry.
289    pub fn from_lisp(src: &str) -> Result<Self, LeituraError> {
290        use tatara_lisp::domain::TataraDomain;
291        let forms = tatara_lisp::read(src).map_err(LeituraError::Leitura)?;
292        let first = forms.first().ok_or(crate::dialeto::DialetoError::Vazio)?;
293
294        // Route the foreign-dialect rejection gate through the lifted
295        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
296        // typed predicate rather than the pre-lift hand-rolled three-arm
297        // `match { Pacote => {}, Desconhecido => {}, foreign => Err(…) }`
298        // literal — the `defmolde` declaration-family partition (the two-
299        // arity closure of [`crate::dialeto::CaixaDialeto::Molde`] and
300        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two arms
301        // whose sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
302        // projection already collapses onto `"defmolde"` and whose sibling
303        // [`crate::dialeto::CaixaDialeto::consumidor`] projection already
304        // collapses onto `"pleme-doc-gen"`) resolves through one dispatch
305        // on the substrate primitive. `Pacote` (the tatara-lisp package
306        // manifest this derive can parse) and `Desconhecido` (deliberately
307        // falls through to the derive rather than short-circuiting: a
308        // `(defcaixa …)` matching neither schema is most likely a genuine
309        // package manifest with a typo in `:nome`, and the derive's
310        // diagnostic — which names the offending keyword and suggests the
311        // nearest slot — is far better than anything this classifier
312        // could say) both return `false` from `is_molde_family()` and fall
313        // through to the derive. Only the typed dialect flows into the
314        // error — the three user-facing projections (canonical keyword,
315        // description, consumer) are read at Display time through
316        // [`crate::dialeto::CaixaDialeto`]'s own accessors, so the
317        // variant cannot carry a snapshot that drifts from
318        // [`crate::dialeto::CaixaDialeto::palavra_canonica`] /
319        // `descricao` / `consumidor`. A future fifth dialect the
320        // [`crate::dialeto`] module doc's "third dialect" hazard
321        // actualises that belongs to the `defmolde` family lands one
322        // match arm at [`crate::dialeto::CaixaDialeto::is_molde_family`]
323        // and this gate picks up the new arm by construction — the pre-
324        // lift wildcard `foreign =>` was compile-time-anonymous and would
325        // silently absorb any hypothetical fifth `defcaixa`-family arm as
326        // foreign; routing the partition through the typed predicate
327        // closes both drift surfaces.
328        let dialeto = crate::dialeto::classify_form(first)?;
329        if dialeto.is_molde_family() {
330            return Err(LeituraError::DialetoEstrangeiro { dialeto });
331        }
332
333        Self::compile_from_sexp(first).map_err(LeituraError::Leitura)
334    }
335
336    /// Register `Caixa` with the global tatara-lisp domain registry so
337    /// `defcaixa` is dispatchable from any tatara-lisp binary that seeds
338    /// the registry (e.g. `tatara-check`).
339    ///
340    /// Returns the typed [`tatara_lisp::KeywordCollision`] on the second
341    /// (and every subsequent) call in the same process — one keyword,
342    /// one type, per process is a hard invariant of the upstream
343    /// registry, and a caller that hits it must fix its crate graph
344    /// rather than swallowing the error. Peer of the sibling per-crate
345    /// `register()` entry points at `caixa-flake/src/flake.rs`,
346    /// `caixa-fmt/src/lisp_config.rs`, `caixa-lacre/src/lock.rs`,
347    /// `caixa-lint/src/lisp_config.rs`, `caixa-resolver/src/lisp_config.rs`
348    /// — every substrate crate that owns a tatara-lisp keyword now
349    /// propagates the same typed error verbatim, so a downstream binary
350    /// that seeds the registry (`tatara-check`, the future LSP) reaches
351    /// for one shape at every call site.
352    ///
353    /// # Errors
354    ///
355    /// [`tatara_lisp::KeywordCollision`] when a peer type has already
356    /// claimed the `defcaixa` keyword in this process.
357    pub fn register() -> Result<(), tatara_lisp::KeywordCollision> {
358        tatara_lisp::domain::register::<Self>()
359    }
360
361    /// Substrate-canonical per-`Caixa` `:licenca` SPDX-expression scalar
362    /// accessor every consumer of the top-level manifest's license axis
363    /// keys off — returns the author-declared `:licenca` byte-string
364    /// verbatim as an `Option<&str>`, borrowed from the typed slot's own
365    /// `Option<String>` storage. `None` when the slot is absent (the
366    /// canonical "omit to defer to the caixa-helm renderer's `MIT`
367    /// fallback" shape [`Self::validate_licenca`] documents at
368    /// caixa-core/src/manifest.rs:1560; the peer [`caixa-helm`]
369    /// `build_readme` fold at caixa-helm/src/lib.rs:962 reads this
370    /// predicate too, so an authored-but-unset `:licenca` round-trips to
371    /// a rendered `lareira-<nome>` chart's `README.md` `## License`
372    /// section structurally identical to one that omits the slot).
373    ///
374    /// The `:licenca` slot carries the universal-axis SPDX-expression
375    /// license identifier every kind of caixa emits under (CAIXA-SDLC
376    /// §I — the author-facing surface every `defcaixa` form supplies) —
377    /// the typed slot's `Option<String>` accept-set (empty-string
378    /// rejected through [`ManifestError::LicencaEmpty`], SPDX-alphabet-
379    /// invalid rejected through [`ManifestError::LicencaInvalid`]) maps
380    /// onto the `lareira-<nome>` Helm chart's `README.md` `## License`
381    /// section (caixa-helm/src/lib.rs:962) and (through future
382    /// tightening documented at [`Self::validate_licenca`]) the
383    /// Chart.yaml `annotations["artifacthub.io/license"]` axis every
384    /// registry-facing chart carries. Every downstream consumer that
385    /// reads the license byte-string keys off this scalar (the
386    /// [`Self::validate_licenca`] empty-arm + SPDX-shape gate that
387    /// routes through `self.licenca.as_deref()`, the caixa-helm
388    /// `build_readme` `unwrap_or_else(|| "MIT".into())` fold that keys
389    /// the fallback off the `Option::is_none()` arm, every future
390    /// per-`Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
391    /// acknowledges).
392    ///
393    /// Prior to this lift the `.licenca` field was accessed inline at
394    /// two production sites — [`Self::validate_licenca`]'s
395    /// `self.licenca.as_deref()` empty-and-shape gate binding and the
396    /// caixa-helm `build_readme` `caixa.licenca.clone().unwrap_or_else(||
397    /// "MIT".into())` `README.md` `## License` fold — two open-coded
398    /// field-accesses that expressed no compile-time link back to the
399    /// typed slot. A future extension of the `:licenca` axis to a
400    /// richer author surface — a per-`:licenca` structured SPDX
401    /// expression parser + license-id allowlist (the future tightening
402    /// [`Self::validate_licenca`]'s docstring acknowledges), a
403    /// per-cluster license-default overlay the M4 CR materializer
404    /// resolves per-CR (the "cluster policy pins `Apache-2.0` for every
405    /// unlisted caixa" arm), a promotion of the plain
406    /// `Option<String>` byte-string to a richer `SpdxExpression` enum
407    /// once the SPDX-expression parser lands — would have had to be
408    /// threaded through both open-coded copies in lockstep or the
409    /// validate gate and the caixa-helm emit path would silently
410    /// disagree on which license a given [`Caixa`] resolves to (an
411    /// author's `:licenca "MIT OR Apache-2.0"` would satisfy validate
412    /// while the emit path silently rendered a stale `MIT` fallback,
413    /// or vice versa). Lifting the resolution to a typed method on the
414    /// substrate primitive means every downstream consumer of the
415    /// caixa's per-`Caixa` license surface reaches for exactly one
416    /// typed dispatch — the resolver's accept-set migrates as a unit
417    /// on any future axis addition.
418    ///
419    /// First `Option<&str>`-return top-level [`Caixa`] scalar accessor —
420    /// opens the "outer [`Caixa`] `Option<&str>` scalar" projection
421    /// pattern the sibling per-`Caixa` `:descricao` / `:repositorio` /
422    /// `:edicao` future lifts fold on. Same "one typed dispatch on the
423    /// substrate primitive, thin projections at each consumer"
424    /// discipline the peer per-`:placement` [`crate::aplicacao::Placement::shard_key`]
425    /// (7cd2a28) / [`crate::aplicacao::Placement::affinity`] (74ec2d3)
426    /// / per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
427    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
428    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
429    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
430    /// typed-slot atom axes, extended here to the outer top-level
431    /// `Caixa` universal-axis surface. Named `licenca()` to match the
432    /// storage field's name; the accessor's identity maps onto the
433    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
434    /// carries.
435    #[must_use]
436    pub fn licenca(&self) -> Option<&str> {
437        self.licenca.as_deref()
438    }
439
440    /// Substrate-canonical per-`Caixa` `:repositorio` git-repo-URL scalar
441    /// accessor every consumer of the top-level manifest's homepage /
442    /// source-of-truth axis keys off — returns the author-declared
443    /// `:repositorio` byte-string verbatim as an `Option<&str>`, borrowed
444    /// from the typed slot's own `Option<String>` storage. `None` when
445    /// the slot is absent (the canonical "omit to defer to the renderer's
446    /// per-target placeholder" shape — [`caixa-helm`]'s `ChartYaml.home`
447    /// carries the `Option<String>` through verbatim so an author-omitted
448    /// `:repositorio` renders a `Chart.yaml` without a `home:` field
449    /// (`skip_serializing_if = "Option::is_none"`), while [`caixa-flux`]'s
450    /// `ClusterBundleOpts::for_caixa` folds the omitted slot through a
451    /// `format!("https://github.com/{DEFAULT_PLEME_GIT_ORG}/{nome}")`
452    /// fallback derived from `caixa.nome`).
453    ///
454    /// The `:repositorio` slot carries the universal-axis git-repo-URL
455    /// homepage identifier every kind of caixa emits under (CAIXA-SDLC
456    /// §I — the author-facing surface every `defcaixa` form supplies) —
457    /// the typed slot's `Option<String>` accept-set (empty-string
458    /// rejected through [`ManifestError::RepositorioEmpty`], git-repo-URL-
459    /// shape-invalid rejected through [`ManifestError::RepositorioInvalid`]
460    /// past the shared [`crate::render::is_git_repo_url`] predicate the
461    /// peer per-`:deps :fonte :repo` axis also routes through) maps onto
462    /// four load-bearing downstream consumers:
463    ///
464    ///   - [`Self::validate_repositorio`]'s empty-arm + shape-predicate
465    ///     gate binding at caixa-core/src/manifest.rs:1456 — the
466    ///     universal-axis identity gate wired at caixa-build time.
467    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.home` fold at
468    ///     caixa-helm/src/lib.rs:840 — the rendered `lareira-<nome>`
469    ///     Helm chart's `Chart.yaml` `home:` field, which every registry
470    ///     that ingests the chart (ArtifactHub, chartmuseum,
471    ///     `helm search repo`) surfaces as the chart's canonical source-
472    ///     of-truth link.
473    ///   - [`caixa-helm`]'s `build_readme` `## Source` fold at
474    ///     caixa-helm/src/lib.rs:957 — the rendered `lareira-<nome>`
475    ///     chart's `README.md` header link back to the source repo,
476    ///     which every author who inspects the rendered chart bundle
477    ///     lands at.
478    ///   - [`caixa-flux`]'s `ClusterBundleOpts::for_caixa`
479    ///     `GitRepository.spec.url` fold at caixa-flux/src/lib.rs:2006 —
480    ///     the rendered `GitRepository` CR's `spec.url` field, which
481    ///     FluxCD's `source-controller` polls to reconcile the caixa's
482    ///     manifest bundle from git.
483    ///
484    /// Prior to this lift the `.repositorio` field was accessed inline
485    /// at four production sites — [`Self::validate_repositorio`]'s
486    /// `self.repositorio.as_deref()` empty-and-shape gate binding, the
487    /// caixa-helm `build_chart_yaml` `caixa.repositorio.clone()`
488    /// `Chart.yaml` `home:` field fold, the caixa-helm `build_readme`
489    /// `caixa.repositorio.clone().unwrap_or_else(|| caixa.nome.clone())`
490    /// `README.md` `## Source` fold, and the caixa-flux
491    /// `ClusterBundleOpts::for_caixa`
492    /// `caixa.repositorio.clone().unwrap_or_else(|| format!(...))`
493    /// `GitRepository.spec.url` fold — four open-coded field-accesses
494    /// that expressed no compile-time link back to the typed slot. A
495    /// future extension of the `:repositorio` axis to a richer author
496    /// surface — a per-`:repositorio` structured
497    /// [`crate::render::GitRepoUrl`]-shaped scheme+host+path parse
498    /// (the future tightening [`Self::validate_repositorio`]'s
499    /// docstring anticipates alongside the peer per-`:deps :fonte
500    /// :repo` axis), a per-cluster repo-mirror overlay the M4 CR
501    /// materializer resolves per-CR (the "cluster policy rewrites
502    /// `github:pleme-io/...` to `git.internal/mirror/pleme-io/...`"
503    /// arm the private-registry story acknowledges), a promotion of
504    /// the plain `Option<String>` byte-string to a richer
505    /// `RepoUrl` enum discriminated on scheme — would have had to be
506    /// threaded through all four open-coded copies in lockstep or the
507    /// validate gate and the three emit paths would silently disagree
508    /// on which URL a given [`Caixa`] resolves to (an author's
509    /// `:repositorio "github:pleme-io/checkout"` would satisfy validate
510    /// while one of the emit paths silently rendered a stale URL, or
511    /// vice versa). Lifting the resolution to a typed method on the
512    /// substrate primitive means every downstream consumer of the
513    /// caixa's per-`Caixa` repo-URL surface reaches for exactly one
514    /// typed dispatch — the resolver's accept-set migrates as a unit on
515    /// any future axis addition.
516    ///
517    /// Second outer top-level [`Caixa`] `Option<&str>`-return scalar
518    /// accessor — sibling of [`Self::licenca`] (6d5bc28), the accessor
519    /// that opened the "outer [`Caixa`] `Option<&str>` scalar"
520    /// projection pattern this lift folds on. Same "one typed dispatch
521    /// on the substrate primitive, thin projections at each consumer"
522    /// discipline the peer per-`:placement`
523    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
524    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
525    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
526    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
527    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
528    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
529    /// typed-slot atom axes, extended here to the second outer top-level
530    /// `Caixa` universal-axis surface. Named `repositorio()` to match
531    /// the storage field's name; the accessor's identity maps onto the
532    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
533    /// carries.
534    #[must_use]
535    pub fn repositorio(&self) -> Option<&str> {
536        self.repositorio.as_deref()
537    }
538
539    /// Substrate-canonical per-`Caixa` **resolved-git-repo-URL** composer —
540    /// returns the caixa's canonical git-source-of-truth URL as an owned
541    /// [`String`], author-declared `:repositorio` byte-string verbatim on
542    /// the `Some` arm and the substrate's canonical pleme-org github URL
543    /// fallback ([`crate::DEFAULT_PLEME_GIT_ORG`] and [`Self::nome`]
544    /// interpolated into `https://github.com/<org>/<nome>`) on the
545    /// `None` arm. Every substrate-side consumer that resolves
546    /// "which git URL does this caixa's source live at?" reaches for
547    /// exactly one typed dispatch on the substrate primitive — the raw
548    /// `caixa.repositorio().map(str::to_owned).unwrap_or_else(|| format!(
549    /// "https://github.com/{org}/{nome}", org = DEFAULT_PLEME_GIT_ORG,
550    /// nome = caixa.nome()))` open-coded composition every prior caller
551    /// re-derived collapses onto one canonical arm.
552    ///
553    /// Distinct from [`Self::repositorio`] (`Option<&str>`, exposes the
554    /// author-omitted / author-declared partition to the caller) — this
555    /// accessor is the **resolved** URL surface, folding the fallback in
556    /// at the substrate-primitive boundary. Every consumer that keys off
557    /// the `Option::is_none()` discriminator (a [`Chart.yaml`] `home:`
558    /// field emit that must omit the field entirely on an author-omitted
559    /// `:repositorio`, per the [`Self::repositorio`] docstring's
560    /// documented four-consumer list) reaches through the raw
561    /// [`Self::repositorio`] `Option<&str>` accessor by construction — the
562    /// resolved-URL composer sits alongside it as the second projection
563    /// on the same underlying `:repositorio` slot rather than replacing
564    /// the raw accessor.
565    ///
566    /// The fallback branch is the exact byte-image of the prior inline
567    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url` composer at
568    /// caixa-flux/src/lib.rs:2080 — pinned by the sibling caixa-flux
569    /// byte-parity test
570    /// `cluster_bundle_opts_for_caixa_git_url_routes_through_canonical_git_url_accessor`
571    /// against a future implementation of this method that reordered the
572    /// `format!` template arguments, migrated the `<org>` segment to a
573    /// different constant (the [`crate::DEFAULT_PLEME_GIT_ORG`] axis a
574    /// future substrate-side git-org migration may split off), or
575    /// silently absorbed the empty-string arm (a hypothetical
576    /// `Some("") → fallback` collapse the raw [`Self::repositorio`]
577    /// accessor's docstring explicitly rejects on the sibling raw
578    /// accessor).
579    ///
580    /// Peer of the sibling per-`&Caixa`-axis composed helpers
581    /// [`caixa-flux::cluster_bundle_for_caixa`] (06d52d7) on the sibling
582    /// substrate-side renderer surface — same "close the composed
583    /// substrate-primitive at one canonical arm on the single-`&Caixa`
584    /// dispatch, converge every prior open-coded caller onto the arm"
585    /// discipline extended onto the resolved-git-URL projection of the
586    /// per-`Caixa` `:repositorio` axis. Owns per-call [`String`]
587    /// allocation on both arms (the `Some` arm's `str::to_owned` and the
588    /// `None` arm's `format!`) — the by-value return matches every
589    /// downstream consumer's field-fill shape (the caixa-flux
590    /// `ClusterBundleOpts::git_url: String` field, every future
591    /// `Chart.yaml` `home:` fold's `Option<String>` field-fill on the
592    /// `Some` arm).
593    #[must_use]
594    pub fn canonical_git_url(&self) -> String {
595        self.repositorio().map_or_else(
596            || {
597                format!(
598                    "https://github.com/{org}/{nome}",
599                    org = crate::DEFAULT_PLEME_GIT_ORG,
600                    nome = self.nome(),
601                )
602            },
603            str::to_owned,
604        )
605    }
606
607    /// Substrate-canonical per-`Caixa` **resolved-publish-tag** composer —
608    /// returns the caixa's canonical Zig-style git-publish-tag as an owned
609    /// [`String`], derived by concatenating
610    /// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] with the typed
611    /// [`Self::versao`] byte-string on a single `format!` template.
612    /// Every substrate-side consumer that resolves "which git tag does this
613    /// caixa publish under?" reaches for exactly one typed dispatch on the
614    /// substrate primitive — the raw `format!("{prefix}{versao}", prefix =
615    /// caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao = caixa.versao())`
616    /// open-coded composition every prior caller re-derived collapses onto
617    /// one canonical arm.
618    ///
619    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
620    /// git-URL composer on the paired per-`Caixa` git-remote axis — same
621    /// "close the composed substrate-primitive at one canonical arm on the
622    /// single-`&Caixa` dispatch, converge every prior open-coded caller
623    /// onto the arm" discipline extended from the resolved-URL projection
624    /// of the per-`Caixa` `:repositorio` axis onto the resolved-tag
625    /// projection of the per-`Caixa` `:versao` axis. The two accessors
626    /// jointly close the pair of scalars every `FluxCD` `GitRepository` CR
627    /// keys off (`spec.url` via [`Self::canonical_git_url`],
628    /// `spec.ref.tag` via [`Self::publish_tag`]) at the substrate primitive
629    /// — a downstream consumer that reaches through both accessors reads
630    /// the complete published-git-identity of a caixa through two typed
631    /// dispatches, not four open-coded field accesses.
632    ///
633    /// The reader-side (`caixa-flux::cluster_bundle` /
634    /// `ClusterBundleOpts::for_caixa`'s `git_ref` field, every future
635    /// per-cluster snapshot bundle emitter, the future M4
636    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's tag-carrier
637    /// slot on the tatara `Process` intent) always resolves the tag under
638    /// the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] prefix — this
639    /// method encodes that reader-side convention. The writer-side
640    /// (`caixa-feira`'s `feira publish` `--prefix` clap flag) allows the
641    /// operator to override the prefix at publish time; the two surfaces
642    /// intentionally sit on the "canonical default + operator override"
643    /// pair the sibling [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] constant's
644    /// own docstring documents — a `feira publish --prefix release/`
645    /// override is the operator's explicit opt-out from the substrate
646    /// default, not a supported drift axis.
647    ///
648    /// The composition body is the exact byte-image of the prior inline
649    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_ref` composer at
650    /// caixa-flux/src/lib.rs:2105 — pinned by the sibling caixa-flux
651    /// byte-parity test
652    /// `cluster_bundle_opts_for_caixa_git_ref_routes_through_publish_tag_accessor`
653    /// against a future implementation of this method that reordered the
654    /// `format!` template arguments, migrated the `<prefix>` segment to a
655    /// different constant (the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] axis
656    /// a future Zig-style-tag rebrand may split off — the constant's own
657    /// docstring anticipates a substrate-side move to `release/<versao>`
658    /// or bare `<versao>` shapes once a sibling forge convention adopts a
659    /// slash-namespaced or bare-scalar form), interposed a canonicalization
660    /// pass on the `:versao` axis (a SemVer-2 build-metadata strip an OCI-
661    /// tag normalizer might apply once the M4 registry-alignment slot
662    /// lands), or silently absorbed an empty `:versao` arm (which cannot
663    /// occur past the [`Self::validate_versao`] gate but which a
664    /// hypothetical bypass on the accessor path must not silently paper
665    /// over).
666    ///
667    /// Owns per-call [`String`] allocation via the single `format!`
668    /// invocation — the by-value return matches every downstream
669    /// consumer's field-fill shape (the caixa-flux `GitRefSpec::Tag(String)`
670    /// variant's owned payload, every future `intent.aplicacao.tag: String`
671    /// field-fill on the M4 CR materializer's tag-carrier slot).
672    #[must_use]
673    pub fn publish_tag(&self) -> String {
674        format!(
675            "{prefix}{versao}",
676            prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
677            versao = self.versao(),
678        )
679    }
680
681    /// Substrate-canonical per-`Caixa` **resolved-Helm-chart-name** composer
682    /// — returns the caixa's canonical `lareira-<nome>` per-Servico Helm
683    /// chart identity as an owned [`String`], derived by dispatching through
684    /// the substrate-canonical [`crate::lareira_chart_name`] helper against
685    /// the typed [`Self::nome`] byte-string. Every substrate-side consumer
686    /// that resolves "which Helm chart identity does this caixa render
687    /// under?" reaches for exactly one typed dispatch on the substrate
688    /// primitive — the raw `caixa_core::lareira_chart_name(caixa.nome())`
689    /// two-step compose every prior caller re-derived collapses onto one
690    /// canonical arm on the single-`&Caixa` dispatch.
691    ///
692    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
693    /// git-URL composer + [`Self::publish_tag`] (07e05b8) resolved-publish-
694    /// tag composer on the paired per-`Caixa` published-artifact-identity
695    /// axis — same "close the composed substrate-primitive at one canonical
696    /// arm on the single-`&Caixa` dispatch, converge every prior open-coded
697    /// caller onto the arm" discipline extended from the resolved-URL /
698    /// resolved-tag projections of the `:repositorio` / `:versao` axes onto
699    /// the resolved-chart-name projection of the `:nome` axis. The three
700    /// accessors jointly close the triple of scalars every per-Servico
701    /// deploy artifact keys off (git source URL via
702    /// [`Self::canonical_git_url`], git source tag via
703    /// [`Self::publish_tag`], per-Servico Helm chart identity via
704    /// [`Self::lareira_chart_name`]) at the substrate primitive — a
705    /// downstream consumer that reaches through all three reads the
706    /// complete deploy-artifact identity of a caixa through three typed
707    /// dispatches, not six open-coded compositions across three renderer
708    /// crates.
709    ///
710    /// The reader-side (three production sites at the time of the lift —
711    /// [`caixa-helm::render_chart_for_servico_with`]'s `ChartDir.name`
712    /// composer at caixa-helm/src/lib.rs:778, the peer
713    /// [`caixa-flux::cluster_bundle`]'s per-CR `chart_name` binding at
714    /// caixa-flux/src/lib.rs:2219, and
715    /// [`caixa-tatara::process_for_aplicacao`]'s `release_name`
716    /// composer at caixa-tatara/src/lib.rs:227, plus every future
717    /// per-Servico OCI publish emitter the CAIXA-SDLC §II
718    /// `caixa-publish.yml` reusable workflow's `skopeo push` step keys
719    /// off, the future per-cluster snapshot bundle emitter, the future
720    /// M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
721    /// per-member chart-carrier slot on the tatara `Process` intent) —
722    /// always resolves the chart name under the canonical
723    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] prefix; this method encodes
724    /// that reader-side convention. The joint-length invariant the peer
725    /// [`Self::validate_nome_chart_name_budget`] gate enforces at
726    /// caixa-build time (author-declared `:nome` + fixed prefix ≤
727    /// [`crate::DNS_1123_LABEL_MAX_LEN`]) is verified on the input to
728    /// this composer by construction, so the produced `lareira-<nome>`
729    /// string is a valid Helm chart-name segment on every accept-set
730    /// input.
731    ///
732    /// The composition body is the exact byte-image of the prior inline
733    /// `caixa_core::lareira_chart_name(caixa.nome())` two-step form every
734    /// prior caller re-derived — pinned by the sibling caixa-helm /
735    /// caixa-flux / caixa-tatara byte-parity tests
736    /// `<crate>_lareira_chart_name_routes_through_caixa_accessor` against
737    /// a future implementation of this method that reordered the
738    /// composition arguments, migrated the `<prefix>` segment to a
739    /// different constant (the [`crate::LAREIRA_CHART_NAME_PREFIX`] axis a
740    /// future substrate-side chart-family rebrand may split off — the
741    /// constant's own docstring anticipates a substrate-side move once
742    /// the `lareira-` scoping intent outlives the family it names),
743    /// interposed a canonicalization pass on the `:nome` axis (a per-
744    /// registry namespace-qualification an M4 CR materializer might apply
745    /// per-CR — the "`pleme-io/checkout` vs `partner-org/checkout`
746    /// collision" arm the multi-tenant-registry story acknowledges), or
747    /// silently absorbed an empty `:nome` arm (which cannot occur past
748    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
749    /// on the accessor path must not silently paper over).
750    ///
751    /// Owns per-call [`String`] allocation via the single
752    /// [`crate::lareira_chart_name`] `format!` invocation — the by-value
753    /// return matches every downstream consumer's field-fill shape (the
754    /// caixa-helm `ChartDir.name: String` field, the caixa-flux per-CR
755    /// `chart_name: String` binding, the caixa-tatara
756    /// `AplicacaoIntent.release_name: Option<String>` field-fill on the
757    /// `Some` arm).
758    #[must_use]
759    pub fn lareira_chart_name(&self) -> String {
760        crate::lareira_chart_name(self.nome())
761    }
762
763    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
764    /// chart-description scalar accessor every consumer of the top-level
765    /// manifest's Chart.yaml `description:` axis keys off — returns the
766    /// author-declared `:descricao` byte-string verbatim as an
767    /// `Option<&str>`, borrowed from the typed slot's own
768    /// `Option<String>` storage. `None` when the slot is absent (the
769    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
770    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
771    /// omitted slot through a `format!("Generated chart for caixa Servico
772    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
773    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
774    /// and [`caixa-feira`]'s `render_flake` folds it through a
775    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
776    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
777    ///
778    /// The `:descricao` slot carries the universal-axis free-form-prose
779    /// chart-description identifier every kind of caixa emits under
780    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
781    /// supplies) — the typed slot's `Option<String>` accept-set
782    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
783    /// chart-description-shape-invalid rejected through
784    /// [`ManifestError::DescricaoInvalid`] past the shared
785    /// [`crate::render::is_chart_description_shape`] predicate the peer
786    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
787    /// load-bearing downstream consumers:
788    ///
789    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
790    ///     gate binding — the universal-axis identity gate wired at
791    ///     caixa-build time.
792    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
793    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
794    ///     chart's `Chart.yaml` `description:` field, which
795    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
796    ///     `WARNING [chart.metadata.description]: description is required`
797    ///     when absent) and which every registry that ingests the chart
798    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
799    ///     chart's canonical one-line prose descriptor.
800    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
801    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
802    ///     header directly beneath the `# <chart-name>` title, which
803    ///     every author who inspects the rendered chart bundle lands at.
804    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
805    ///     top-level fold — the emitted `flake.nix`'s `description`
806    ///     field, which every Nix consumer (`nix flake show`,
807    ///     `nix flake metadata`, downstream flake-registry ingestors)
808    ///     surfaces as the flake's canonical descriptor.
809    ///
810    /// Prior to this lift the `.descricao` field was accessed inline at
811    /// four production sites — [`Self::validate_descricao`]'s
812    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
813    /// caixa-helm `build_chart_yaml`
814    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
815    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
816    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
817    /// `README.md` header fold, and the caixa-feira `render_flake`
818    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
819    /// `description = ""` fold — four open-coded field-accesses that
820    /// expressed no compile-time link back to the typed slot. A future
821    /// extension of the `:descricao` axis to a richer author surface —
822    /// a per-`:descricao` locale-tagged multi-language descriptor map
823    /// (the "one caixa, N language-tagged prose descriptions" arm
824    /// author-tooling internationalization anticipates), a
825    /// per-registry-target length-and-shape overlay the M4 CR
826    /// materializer resolves per-CR (the "ArtifactHub caps description
827    /// at 512 bytes but the internal registry caps at 256" arm), a
828    /// promotion of the plain `Option<String>` byte-string to a richer
829    /// `ChartDescription` newtype guaranteeing the
830    /// `is_chart_description_shape` predicate at the type level — would
831    /// have had to be threaded through all four open-coded copies in
832    /// lockstep or the validate gate and the three emit paths would
833    /// silently disagree on which prose string a given [`Caixa`]
834    /// resolves to (an author's
835    /// `:descricao "Checkout flow orchestration."` would satisfy
836    /// validate while one of the emit paths silently rendered a stale
837    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
838    /// resolution to a typed method on the substrate primitive means
839    /// every downstream consumer of the caixa's per-`Caixa`
840    /// chart-description surface reaches for exactly one typed dispatch
841    /// — the resolver's accept-set migrates as a unit on any future
842    /// axis addition.
843    ///
844    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
845    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
846    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
847    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
848    /// lift folds on. Same "one typed dispatch on the substrate
849    /// primitive, thin projections at each consumer" discipline the
850    /// peer per-`:placement`
851    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
852    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
853    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
854    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
855    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
856    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
857    /// typed-slot atom axes, extended here to the third outer top-level
858    /// `Caixa` universal-axis surface. Named `descricao()` to match the
859    /// storage field's name; the accessor's identity maps onto the
860    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
861    /// carries. The one remaining universal `Option<String>` slot
862    /// (`:edicao`) folds on this pattern next.
863    #[must_use]
864    pub fn descricao(&self) -> Option<&str> {
865        self.descricao.as_deref()
866    }
867
868    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
869    /// accessor every consumer of the top-level manifest's tatara-lisp
870    /// edition-selector axis keys off — returns the author-declared
871    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
872    /// the typed slot's own `Option<String>` storage. `None` when the
873    /// slot is absent (the canonical "omit the slot to defer to the
874    /// substrate's default edition" shape every existing
875    /// [`caixa-resolver`] integration test fixture carries via
876    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
877    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
878    /// arm by construction, so an author-omitted `:edicao` round-trips
879    /// to a build without triggering the year-shape predicate).
880    ///
881    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
882    /// decimal-year language-edition identifier every kind of caixa
883    /// emits under (CAIXA-SDLC §I — the author-facing surface every
884    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
885    /// accept-set (empty-string rejected through
886    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
887    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
888    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
889    /// onto one load-bearing downstream consumer today
890    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
891    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
892    /// future edition-aware substrate consumer the CAIXA-SDLC §I
893    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
894    /// selector every edition-aware build step keys off, the future
895    /// per-edition compatibility-flag overlay the M4 CR materializer
896    /// resolves per-CR, the peer [`Caixa::template`] canonical
897    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
898    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
899    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
900    /// carry `edicao: Some("2026".into())` by construction).
901    ///
902    /// Prior to this lift the `.edicao` field was accessed inline at
903    /// one production site — [`Self::validate_edicao`]'s
904    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
905    /// open-coded field-access that expressed no compile-time link
906    /// back to the typed slot. A future extension of the `:edicao`
907    /// axis to a richer author surface — a per-`:edicao` known-
908    /// edition allowlist (the future tightening
909    /// [`Self::validate_edicao`]'s docstring acknowledges past the
910    /// structural year-shape floor, rejecting year-shaped values that
911    /// don't name a tatara-lisp edition the substrate actually
912    /// understands — `"1999"` is year-shaped but no `1999` edition
913    /// exists), a per-edition compatibility-flag overlay the M4 CR
914    /// materializer resolves per-CR (the "edition `"2026"` enables
915    /// macro-surface features the sibling `"2018"` gates behind a
916    /// feature flag" arm the edition-selector story anticipates), a
917    /// promotion of the plain `Option<String>` byte-string to a
918    /// richer `CaixaEdition` enum discriminated on year once a sibling
919    /// edition to `"2026"` lands — would have had to be threaded
920    /// through the open-coded copy in lockstep with every future
921    /// edition-aware consumer, or the validate gate and the future
922    /// edition-aware consumer path would silently disagree on which
923    /// edition a given [`Caixa`] resolves to (an author's
924    /// `:edicao "2026"` would satisfy validate while a future
925    /// edition-aware consumer silently defaulted to a stale edition,
926    /// or vice versa). Lifting the resolution to a typed method on
927    /// the substrate primitive means every downstream consumer of the
928    /// caixa's per-`Caixa` edition surface reaches for exactly one
929    /// typed dispatch — the resolver's accept-set migrates as a unit
930    /// on any future axis addition.
931    ///
932    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
933    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
934    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
935    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
936    /// `Option<&str>` scalar" projection pattern this lift folds on.
937    /// Same "one typed dispatch on the substrate primitive, thin
938    /// projections at each consumer" discipline the peer per-`:placement`
939    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
940    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
941    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
942    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
943    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
944    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
945    /// typed-slot atom axes, extended here to close the outer top-level
946    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
947    /// slot. Named `edicao()` to match the storage field's name; the
948    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
949    /// vocabulary the slot's docstring already carries.
950    #[must_use]
951    pub fn edicao(&self) -> Option<&str> {
952        self.edicao.as_deref()
953    }
954
955    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
956    /// label caixa-identity scalar accessor every consumer of the top-
957    /// level manifest's identity axis keys off — returns the author-
958    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
959    /// the typed slot's own `String` storage. Non-optional (`:nome` is
960    /// a required-axis scalar every `defcaixa` form must supply; the
961    /// [`Self::from_lisp`] derive rejects an omitted / non-string
962    /// `:nome` at parse time, so a `Caixa` past parse definitionally
963    /// carries a non-`None` `:nome`).
964    ///
965    /// The `:nome` slot carries the universal-axis DNS-1123-label
966    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
967    /// the primary identity axis every `defcaixa` form supplies
968    /// alongside `:versao` / `:kind`; the substrate-wide identity every
969    /// other typed surface that names a caixa reaches through — `:deps`
970    /// entries, `:membros` entries, `:children` entries, the
971    /// `lareira-<nome>` Helm chart name every per-Servico renderer
972    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
973    /// renderer emits) — the typed slot's `String` accept-set (empty
974    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
975    /// invalid rejected through [`ManifestError::NomeInvalid`] past
976    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
977    /// the peer name axes each land on, joint-length-with-`lareira-`-
978    /// prefix rejected through
979    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
980    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
981    /// load-bearing downstream consumer the substrate carries — the
982    /// two universal-axis validate gates at caixa-build time
983    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
984    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
985    /// derivation every per-Servico renderer keys off, the caixa-helm
986    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
987    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
988    /// `HTTPRoute` per-Aplicacao name axes at
989    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
990    /// [`crate::pleme_program_selector`] /
991    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
992    /// derivations, and every future substrate renderer that emits an
993    /// artifact keyed by the caixa's identity.
994    ///
995    /// Prior to this lift the `.nome` field was accessed inline at a
996    /// dozen production sites across `caixa-core` (the two universal-
997    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
998    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
999    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
1000    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
1001    /// entry `name:` fold, the `flux_kustomization_source_subtree`
1002    /// per-cluster subpath derivation), and `caixa-mesh` (the
1003    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
1004    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
1005    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
1006    /// insert) — a dozen open-coded field-accesses that expressed no
1007    /// compile-time link back to the typed slot. A future extension of
1008    /// the `:nome` axis to a richer author surface — a per-`:nome`
1009    /// structured `CaixaIdentity` newtype that carries the joint-
1010    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
1011    /// enforces at the type level (rather than as a validate-time
1012    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
1013    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
1014    /// `partner-org/checkout` collision" arm the multi-tenant-registry
1015    /// story acknowledges), a promotion of the plain `String` byte-
1016    /// string to a richer `CaixaNome` newtype discriminated on
1017    /// namespace prefix — would have had to be threaded through every
1018    /// open-coded copy in lockstep or the two validate gates and the
1019    /// dozen emit paths would silently disagree on which identity a
1020    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
1021    /// would satisfy validate while one of the emit paths silently
1022    /// rendered a drifted other identity, or vice versa). Lifting the
1023    /// resolution to a typed method on the substrate primitive means
1024    /// every downstream consumer of the caixa's per-`Caixa` identity
1025    /// surface reaches for exactly one typed dispatch — the resolver's
1026    /// accept-set migrates as a unit on any future axis addition.
1027    ///
1028    /// First outer top-level [`Caixa`] `&str`-return required-scalar
1029    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
1030    /// projection pattern the sibling per-`Caixa` `:versao` future lift
1031    /// folds on. Sibling in shape to the peer per-`:membros`
1032    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
1033    /// [`crate::aplicacao::WitContract::source`] /
1034    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
1035    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
1036    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
1037    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
1038    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
1039    /// per-sub-struct required-axis accessors carry on the sibling M3
1040    /// mesh-slot-atom scalar-value axes, extended here to open the
1041    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
1042    /// Named `nome()` to match the storage field's name; the accessor's
1043    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1044    /// slot's docstring already carries.
1045    #[must_use]
1046    pub fn nome(&self) -> &str {
1047        &self.nome
1048    }
1049
1050    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
1051    /// pinned-version scalar accessor every consumer of the top-level
1052    /// manifest's version axis keys off — returns the author-declared
1053    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
1054    /// typed slot's own `String` storage. Non-optional (`:versao` is a
1055    /// required-axis scalar every `defcaixa` form must supply alongside
1056    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
1057    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
1058    /// parse definitionally carries a non-`None` `:versao`).
1059    ///
1060    /// The `:versao` slot carries the universal-axis SemVer-2
1061    /// concrete-version body every kind of caixa emits under
1062    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
1063    /// supplies alongside `:nome` / `:kind`; the substrate-wide
1064    /// pinned-version every downstream artifact-emitting consumer
1065    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
1066    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
1067    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
1068    /// prefix composes on top of, the programs.yaml entry's `versao:`
1069    /// value the `lareira-fleet-programs` aggregator carries onto each
1070    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
1071    /// tags every substrate-side `skopeo push` writes, the lacre
1072    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
1073    /// prior-version references peers in the exact same SemVer-2 shape).
1074    /// The typed slot's `String` accept-set (empty rejected through
1075    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
1076    /// through [`ManifestError::VersaoInvalid`] past
1077    /// [`semver::Version::parse`]) maps onto every load-bearing
1078    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
1079    /// universal-axis validate gate at caixa-build time, the
1080    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
1081    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
1082    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
1083    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
1084    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
1085    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
1086    /// tag derivation (`format!("{prefix}{versao}")`), and every future
1087    /// substrate renderer that emits an artifact keyed by the caixa's
1088    /// pinned version.
1089    ///
1090    /// Prior to this lift the `.versao` field was accessed inline at a
1091    /// dozen production sites across `caixa-core` (the universal-axis
1092    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
1093    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
1094    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
1095    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
1096    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
1097    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
1098    /// (the `feira publish` git-tag derivation + the `feira app graph` /
1099    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
1100    /// field-accesses that expressed no compile-time link back to the
1101    /// typed slot. A future extension of the `:versao` axis to a richer
1102    /// author surface — a per-`:versao` structured `CaixaVersion` at the
1103    /// storage layer (the substrate already carries a `CaixaVersion`
1104    /// newtype at [`crate::version::CaixaVersion`], deferred until the
1105    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
1106    /// a per-registry `:versao` immutability overlay the M4 CR
1107    /// materializer enforces per-CR, a promotion of the plain `String`
1108    /// byte-string to a richer `PinnedVersao` newtype discriminated on
1109    /// SemVer-2 pre-release / build-metadata presence — would have had
1110    /// to be threaded through every open-coded copy in lockstep or the
1111    /// validate gate and the dozen emit paths would silently disagree
1112    /// on which version a given [`Caixa`] resolves to (an author's
1113    /// `:versao "0.1.0"` would satisfy validate while one of the emit
1114    /// paths silently rendered a drifted other version, or vice versa).
1115    /// Lifting the resolution to a typed method on the substrate
1116    /// primitive means every downstream consumer of the caixa's
1117    /// per-`Caixa` pinned-version surface reaches for exactly one typed
1118    /// dispatch — the resolver's accept-set migrates as a unit on any
1119    /// future axis addition.
1120    ///
1121    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
1122    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
1123    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
1124    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
1125    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
1126    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
1127    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
1128    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
1129    /// on the sibling per-typed-slot version-carrier axes, extended here
1130    /// to close the second outer top-level [`Caixa`] required-`&str`-
1131    /// carrying axis so the two universal-axis identity-carrying
1132    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
1133    /// share the same "one typed dispatch per axis" discipline. Named
1134    /// `versao()` to match the storage field's name; the accessor's
1135    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1136    /// slot's docstring already carries.
1137    #[must_use]
1138    pub fn versao(&self) -> &str {
1139        &self.versao
1140    }
1141
1142    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
1143    /// closed-set-enum discriminant accessor every consumer of the top-
1144    /// level manifest's kind axis keys off — returns the author-declared
1145    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
1146    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
1147    /// (`:kind` is a required-axis discriminant every `defcaixa` form
1148    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
1149    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
1150    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
1151    /// variant).
1152    ///
1153    /// The `:kind` slot carries the universal-axis closed-set typed-
1154    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
1155    /// §I — the primary shape gate every renderer / verifier /
1156    /// operator branches on; the five variants `Biblioteca` /
1157    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
1158    /// the caixa surface into disjoint runtime contracts) — the typed
1159    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
1160    /// values through the derive-macro's symbol-arm gate, exhaustively
1161    /// matched at every downstream dispatch site) maps onto every
1162    /// load-bearing downstream consumer the substrate carries:
1163    ///
1164    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
1165    ///     predicate — the canonical two-line
1166    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
1167    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
1168    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
1169    ///     ComputeUnit` CR materializer) runs at its entry-point,
1170    ///     alongside the [`crate::render::KindMismatch`] error carrier's
1171    ///     `actual:` field the diagnostic surfaces to name the offending
1172    ///     caixa's variant.
1173    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
1174    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
1175    ///     `_view` composers that fold the flat mesh-slot / supervisor-
1176    ///     slot columns into their typed sub-spec only when the kind
1177    ///     matches (returns `None` otherwise); the future per-Servico
1178    ///     M2-view composer (`servico_view`) will follow the same shape.
1179    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
1180    ///     coherence gate — the `!self.kind.requires_exe()` /
1181    ///     `!self.kind.requires_servicos()` predicates that fence
1182    ///     each code-surface slot from the wrong owning kind.
1183    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
1184    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
1185    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
1186    ///     coherence error carriers (`SupervisorOwnsCode` /
1187    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
1188    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
1189    ///     / `ForeignCodeSlot`) which each name the offending caixa's
1190    ///     variant in their `kind:` field.
1191    ///
1192    /// Prior to this lift the `.kind` field was accessed inline at
1193    /// twenty-plus production sites across `caixa-core` (the
1194    /// [`crate::render::require_kind`] entry-gate predicate + the
1195    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
1196    /// composers, the `declared_foreign_code_slots` per-slot kind-
1197    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
1198    /// kind ↔ code-surface predicates + four error carriers) — a score
1199    /// of open-coded field-accesses that expressed no compile-time link
1200    /// back to the typed slot. A future extension of the `:kind` axis
1201    /// to a richer author surface — a per-`:kind` sub-variant discriminant
1202    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
1203    /// variant across the wasm-component / legacy-container / native-
1204    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
1205    /// kind-overlay the M4 CR materializer resolves per-CR (the
1206    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
1207    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
1208    /// enum to a richer `KindWithRuntime` discriminated on the
1209    /// component-model world axis — would have had to be threaded
1210    /// through every open-coded copy in lockstep or the entry gate,
1211    /// the view composers, and the layout invariants would silently
1212    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
1213    /// the resolution to a typed method on the substrate primitive
1214    /// means every downstream consumer of the caixa's per-`Caixa`
1215    /// kind surface reaches for exactly one typed dispatch — the
1216    /// resolver's accept-set migrates as a unit on any future axis
1217    /// addition.
1218    ///
1219    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
1220    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
1221    /// required-discriminant" projection pattern. Sibling in shape to
1222    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
1223    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
1224    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
1225    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
1226    /// on the sibling nested-spec typed-slot discriminator axes,
1227    /// extended here to the outer top-level [`Caixa`] universal-axis
1228    /// surface. Named `kind()` to match the storage field's name;
1229    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
1230    /// vocabulary the slot's docstring already carries.
1231    #[must_use]
1232    pub fn kind(&self) -> CaixaKind {
1233        self.kind
1234    }
1235
1236    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
1237    /// maintainer-name-list slice-accessor every consumer of the top-
1238    /// level manifest's maintainer axis keys off — returns the author-
1239    /// declared `:autores` list verbatim as a `&[String]` slice-view over
1240    /// the same backing buffer the raw `self.autores.as_slice()` field
1241    /// access borrows from. Empty-list-carrying (`:autores` is a default-
1242    /// empty axis every `defcaixa` form supplies with an empty `()` when
1243    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
1244    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1245    /// parse definitionally carries a `Vec<String>` slot — possibly
1246    /// empty — and the returned `&[String]` degenerates to an empty
1247    /// slice on that arm without any silent `None` collapse).
1248    ///
1249    /// The `:autores` slot carries the universal-axis maintainer-name
1250    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1251    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1252    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1253    /// every downstream registry-facing artifact emits under) — the
1254    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1255    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1256    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1257    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1258    /// onto every load-bearing downstream consumer the substrate carries
1259    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1260    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1261    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1262    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1263    /// name, email: None }` record, every future per-`Caixa` registry-
1264    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1265    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1266    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1267    /// the future per-cluster author-notification overlay the M4 CR
1268    /// materializer resolves per-CR).
1269    ///
1270    /// Prior to this lift the `.autores` field was accessed inline at
1271    /// two production sites — [`Self::validate_autores`]'s `for autor
1272    /// in &self.autores` walk that gates every entry through
1273    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1274    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1275    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1276    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1277    /// two open-coded field-accesses that expressed no compile-time link
1278    /// back to the typed slot. A future extension of the `:autores` axis
1279    /// to a richer author surface — a per-`:autores` structured
1280    /// `Maintainer { name, email, url }` at the storage layer once the
1281    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1282    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1283    /// enforces per-CR (the "cluster policy demands every author declare
1284    /// an on-file `mailto:` contact" arm), a promotion of the plain
1285    /// `Vec<String>` byte-string list to a richer
1286    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1287    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1288    /// predicate already resolves through — would have had to be
1289    /// threaded through both open-coded copies in lockstep or the
1290    /// validate gate and the caixa-helm emit path would silently
1291    /// disagree on which authors a given [`Caixa`] resolves to (an
1292    /// author's `:autores ("alice" "bob")` would satisfy validate while
1293    /// the caixa-helm emit path silently rendered a drifted other
1294    /// maintainer list, or vice versa). Lifting the resolution to a
1295    /// typed method on the substrate primitive means every downstream
1296    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1297    /// for exactly one typed dispatch — the resolver's accept-set
1298    /// migrates as a unit on any future axis addition.
1299    ///
1300    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1301    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1302    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1303    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1304    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1305    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1306    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1307    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1308    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1309    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1310    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1311    /// per-M3 typed-slot list axes, extended here to the outer top-level
1312    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1313    /// `&Vec<String>`) because every downstream consumer of the author
1314    /// list treats it as a read-only sequence — the slice-view is the
1315    /// narrowest borrow that supports every present + roadmapped consumer
1316    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1317    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1318    /// reaches for (the storage-side `Vec` remains reachable through the
1319    /// `pub autores` field for the mutation-carrying serde round-trip and
1320    /// per-test fixture-mutation paths). Named `autores()` to match the
1321    /// storage field's name; the accessor's identity maps onto the
1322    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1323    /// carries.
1324    #[must_use]
1325    pub fn autores(&self) -> &[String] {
1326        self.autores.as_slice()
1327    }
1328
1329    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1330    /// registry-search-tag-list slice-accessor every consumer of the
1331    /// top-level manifest's topical-tag axis keys off — returns the
1332    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1333    /// slice-view over the same backing buffer the raw
1334    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1335    /// list-carrying (`:etiquetas` is a default-empty axis every
1336    /// `defcaixa` form supplies with an empty `()` when unset; the
1337    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1338    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1339    /// definitionally carries a `Vec<String>` slot — possibly empty —
1340    /// and the returned `&[String]` degenerates to an empty slice on
1341    /// that arm without any silent `None` collapse).
1342    ///
1343    /// The `:etiquetas` slot carries the universal-axis topical-tag
1344    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1345    /// author-facing surface every `defcaixa` form supplies alongside
1346    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1347    /// search-facing axis every downstream registry-facing artifact
1348    /// emits under) — the typed slot's `Vec<String>` accept-set
1349    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1350    /// non-chart-keyword-shape rejected through
1351    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1352    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1353    /// every load-bearing downstream consumer the substrate carries —
1354    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1355    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1356    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1357    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1358    /// `Chart.yaml` `keywords:` array (chained with the
1359    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1360    /// dedup'd through a `BTreeSet` at emit time), every future per-
1361    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1362    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1363    /// annotation, the future per-cluster tag-notification overlay the
1364    /// M4 CR materializer resolves per-CR).
1365    ///
1366    /// Prior to this lift the `.etiquetas` field was accessed inline at
1367    /// two production sites — [`Self::validate_etiquetas`]'s `for
1368    /// etiqueta in &self.etiquetas` walk that gates every entry through
1369    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1370    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1371    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1372    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1373    /// two open-coded field-accesses that expressed no compile-time
1374    /// link back to the typed slot. A future extension of the
1375    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1376    /// structured `ChartKeyword { name, uri, category }` at the storage
1377    /// layer once the substrate absorbs `artifacthub.io/keywords`
1378    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1379    /// CR materializer enforces per-CR (the "cluster policy demands
1380    /// every tag come from a substrate-approved taxonomy" arm), a
1381    /// promotion of the plain `Vec<String>` byte-string list to a
1382    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1383    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1384    /// already resolves through — would have had to be threaded through
1385    /// both open-coded copies in lockstep or the validate gate and the
1386    /// caixa-helm emit path would silently disagree on which tags a
1387    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1388    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1389    /// path silently rendered a drifted other keyword list, or vice
1390    /// versa). Lifting the resolution to a typed method on the
1391    /// substrate primitive means every downstream consumer of the
1392    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1393    /// typed dispatch — the resolver's accept-set migrates as a unit
1394    /// on any future axis addition.
1395    ///
1396    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1397    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1398    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1399    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1400    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1401    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1402    /// fold onto the same pattern in future lifts. Sibling in shape to
1403    /// the peer per-`:supervisor`
1404    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1405    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1406    /// (a6e18d7), per-`:membros`
1407    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1408    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1409    /// (0dcc926), and per-`:upgrade-from :instructions`
1410    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1411    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1412    /// typed-slot list axes, extended here to the outer top-level
1413    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1414    /// `&Vec<String>`) because every downstream consumer of the tag
1415    /// list treats it as a read-only sequence — the slice-view is the
1416    /// narrowest borrow that supports every present + roadmapped
1417    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1418    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1419    /// the typed view reaches for (the storage-side `Vec` remains
1420    /// reachable through the `pub etiquetas` field for the mutation-
1421    /// carrying serde round-trip and per-test fixture-mutation paths).
1422    /// Named `etiquetas()` to match the storage field's name; the
1423    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1424    /// vocabulary the slot's docstring already carries.
1425    #[must_use]
1426    pub fn etiquetas(&self) -> &[String] {
1427        self.etiquetas.as_slice()
1428    }
1429
1430    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1431    /// library-source-path-list slice-accessor every consumer of the
1432    /// top-level manifest's Biblioteca-source axis keys off — returns
1433    /// the author-declared `:bibliotecas` list verbatim as a
1434    /// `&[String]` slice-view over the same backing buffer the raw
1435    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1436    /// list-carrying (`:bibliotecas` is a default-empty axis every
1437    /// `defcaixa` form supplies with an empty `()` when unset; the
1438    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1439    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1440    /// parse definitionally carries a `Vec<String>` slot — possibly
1441    /// empty — and the returned `&[String]` degenerates to an empty
1442    /// slice on that arm without any silent `None` collapse).
1443    ///
1444    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1445    /// entry-path list every `:kind Biblioteca` caixa emits under
1446    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1447    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1448    /// substrate-wide library-carrier axis every downstream
1449    /// authoring-facing consumer keys off) — the typed slot's
1450    /// `Vec<String>` accept-set (empty-per-entry rejected through
1451    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1452    /// non-sandboxed-relative-shape rejected through
1453    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1454    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1455    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1456    /// maps onto every load-bearing downstream consumer the substrate
1457    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1458    /// empty-check + per-entry file-exists loop at
1459    /// caixa-core/src/layout.rs that gates each entry through
1460    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1461    /// [`Self::validate_code_paths`] per-slot shape gate at
1462    /// caixa-core/src/manifest.rs that walks each entry through the
1463    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1464    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1465    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1466    /// declared library file for lexical / structural errors before
1467    /// downstream `importar` resolution, every future per-`Caixa`
1468    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1469    /// (the future `tatara-lispc` compilation entry the docstring at
1470    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1471    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1472    /// the future `caixa-lsp` per-library semantic-token stream the
1473    /// caixa-lsp docstring roadmaps).
1474    ///
1475    /// Prior to this lift the `.bibliotecas` field was accessed inline
1476    /// at three production sites — [`crate::LayoutInvariants`]'s
1477    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1478    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1479    /// declared library path through the on-disk-existence check,
1480    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1481    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1482    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1483    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1484    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1485    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1486    /// coded field-accesses that expressed no compile-time link back
1487    /// to the typed slot. A future extension of the `:bibliotecas`
1488    /// axis to a richer library surface — a per-`:bibliotecas`
1489    /// structured `BibliotecaEntry { path, edition, exports }` at the
1490    /// storage layer once the substrate absorbs the per-library
1491    /// language-edition + explicit-exports tuple the tatara-lisp
1492    /// module-system roadmap acknowledges, a per-registry
1493    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1494    /// per-CR (the "cluster policy demands every biblioteca declare
1495    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1496    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1497    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1498    /// [`crate::render::is_sandboxed_relative_path`] +
1499    /// [`crate::render::is_lisp_extension`] predicates already resolve
1500    /// through — would have had to be threaded through all three
1501    /// open-coded copies in lockstep or the layout gate, the shape
1502    /// validator, and the `feira build` phase-1 parse walk would
1503    /// silently disagree on which library paths a given [`Caixa`]
1504    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1505    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1506    /// silently parsed a drifted other list, or vice versa). Lifting
1507    /// the resolution to a typed method on the substrate primitive
1508    /// means every downstream consumer of the caixa's per-`Caixa`
1509    /// library-source surface reaches for exactly one typed dispatch
1510    /// — the resolver's accept-set migrates as a unit on any future
1511    /// axis addition.
1512    ///
1513    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1514    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1515    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1516    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1517    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1518    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1519    /// `:children` / `:membros` / `:contratos`) fold onto the same
1520    /// pattern in future lifts. Sibling in shape to the peer
1521    /// per-`:supervisor`
1522    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1523    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1524    /// (a6e18d7), per-`:membros`
1525    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1526    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1527    /// (0dcc926), and per-`:upgrade-from :instructions`
1528    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1529    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1530    /// typed-slot list axes, extended here to the outer top-level
1531    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1532    /// `&Vec<String>`) because every downstream consumer of the
1533    /// library-source list treats it as a read-only sequence — the
1534    /// slice-view is the narrowest borrow that supports every
1535    /// present + roadmapped consumer (`.iter()`, `.len()`,
1536    /// `.is_empty()`) without leaking the backing `Vec`'s
1537    /// grow/push/reserve surface no consumer of the typed view
1538    /// reaches for (the storage-side `Vec` remains reachable through
1539    /// the `pub bibliotecas` field for the mutation-carrying serde
1540    /// round-trip and per-test fixture-mutation paths). Named
1541    /// `bibliotecas()` to match the storage field's name; the
1542    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1543    /// vocabulary the slot's docstring already carries.
1544    #[must_use]
1545    pub fn bibliotecas(&self) -> &[String] {
1546        self.bibliotecas.as_slice()
1547    }
1548
1549    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1550    /// nix-built-executable-entry-path-list slice-accessor every consumer
1551    /// of the top-level manifest's Binario-executable axis keys off —
1552    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1553    /// slice-view over the same backing buffer the raw
1554    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1555    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1556    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1557    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1558    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1559    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1560    /// degenerates to an empty slice on that arm without any silent
1561    /// `None` collapse).
1562    ///
1563    /// The `:exe` slot carries the universal-axis nix-built executable
1564    /// entry-path list every `:kind Binario` caixa emits under
1565    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1566    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1567    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1568    /// downstream flake-build-facing consumer keys off) — the typed
1569    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1570    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1571    /// non-sandboxed-relative-shape rejected through
1572    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1573    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1574    /// directory paths rejected past the layout's
1575    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1576    /// onto every load-bearing downstream consumer the substrate carries
1577    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1578    /// per-entry file-exists + `exe/`-directory-fence loop at
1579    /// caixa-core/src/layout.rs that gates each entry through
1580    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1581    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1582    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1583    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1584    /// that fences code-surface slots off from the two no-code kinds,
1585    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1586    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1587    /// fences the `:exe` code surface off from every non-Binario code-
1588    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1589    /// that walks each entry through the sandbox-relative / cross-entry
1590    /// duplicate gates, every future per-`Caixa` executable-facing
1591    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1592    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1593    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1594    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1595    /// future `feira nix` per-executable Binario-target emit path).
1596    ///
1597    /// Prior to this lift the `.exe` field was accessed inline at three
1598    /// production sites — the compound-code-path `has_code =
1599    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1600    /// !caixa.servicos.is_empty()` OR-fold on the
1601    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1602    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1603    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1604    /// gate, the per-entry `for p in &caixa.exe`
1605    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1606    /// [`Self::declared_foreign_code_slots`]'s
1607    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1608    /// open-coded field-accesses that expressed no compile-time link
1609    /// back to the typed slot. A future extension of the `:exe` axis
1610    /// to a richer executable surface — a per-`:exe` structured
1611    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1612    /// layer once the substrate absorbs the per-executable
1613    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1614    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1615    /// the M4 CR materializer enforces per-CR (the "cluster policy
1616    /// demands every Binario declare an explicit `:wrapper`" arm), a
1617    /// promotion of the plain `Vec<String>` byte-string list to a
1618    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1619    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1620    /// fence already resolves through — would have had to be threaded
1621    /// through all four open-coded copies in lockstep or the layout
1622    /// gate, the shape validator, and the `feira nix` emit path would
1623    /// silently disagree on which executable paths a given [`Caixa`]
1624    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1625    /// satisfy layout while `feira nix` silently packaged a drifted
1626    /// other list, or vice versa). Lifting the resolution to a typed
1627    /// method on the substrate primitive means every downstream
1628    /// consumer of the caixa's per-`Caixa` executable-source surface
1629    /// reaches for exactly one typed dispatch — the resolver's accept-
1630    /// set migrates as a unit on any future axis addition.
1631    ///
1632    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1633    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1634    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1635    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1636    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1637    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1638    /// future lift closes onto (per the trio of code-surface list slots
1639    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1640    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1641    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1642    /// last unlifted code-surface slot). Sibling in shape to the peer
1643    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1644    /// (bc92bce), per-`:placement`
1645    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1646    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1647    /// (6c77e36), per-`:contratos`
1648    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1649    /// per-`:upgrade-from :instructions`
1650    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1651    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1652    /// typed-slot list axes, extended here to the outer top-level
1653    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1654    /// `&Vec<String>`) because every downstream consumer of the
1655    /// executable-source list treats it as a read-only sequence — the
1656    /// slice-view is the narrowest borrow that supports every
1657    /// present + roadmapped consumer (`.iter()`, `.len()`,
1658    /// `.is_empty()`) without leaking the backing `Vec`'s
1659    /// grow/push/reserve surface no consumer of the typed view
1660    /// reaches for (the storage-side `Vec` remains reachable through
1661    /// the `pub exe` field for the mutation-carrying serde
1662    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1663    /// to match the storage field's name; the accessor's identity
1664    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1665    /// docstring already carries.
1666    #[must_use]
1667    pub fn exe(&self) -> &[String] {
1668        self.exe.as_slice()
1669    }
1670
1671    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1672    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1673    /// of the top-level manifest's Servico-component axis keys off —
1674    /// returns the author-declared `:servicos` list verbatim as a
1675    /// `&[String]` slice-view over the same backing buffer the raw
1676    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1677    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1678    /// form supplies with an empty `()` when unset; the
1679    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1680    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1681    /// definitionally carries a `Vec<String>` slot — possibly empty —
1682    /// and the returned `&[String]` degenerates to an empty slice on
1683    /// that arm without any silent `None` collapse).
1684    ///
1685    /// The `:servicos` slot carries the universal-axis
1686    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1687    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1688    /// author-facing surface every `defcaixa` form supplies alongside
1689    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1690    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1691    /// Servico-facing renderer keys off) — the typed slot's
1692    /// `Vec<String>` accept-set (empty-per-entry rejected through
1693    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1694    /// non-sandboxed-relative-shape rejected through
1695    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1696    /// extension rejected through
1697    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1698    /// entry duplicate rejected through
1699    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1700    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1701    /// renderer entry-points, out-of-`servicos/`-directory paths
1702    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1703    /// `starts_with` fence) maps onto every load-bearing downstream
1704    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1705    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1706    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1707    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1708    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1709    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1710    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1711    /// that fences code-surface slots off from the two no-code kinds,
1712    /// [`Self::declared_foreign_code_slots`]'s
1713    /// `!self.servicos.is_empty()` arm on the
1714    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1715    /// `:servicos` code surface off from every non-Servico code-running
1716    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1717    /// walks each entry through the sandbox-relative / `.computeunit.
1718    /// yaml`-extension / cross-entry duplicate gates, the
1719    /// [`crate::require_single_servico`] V0 singularity gate every
1720    /// per-Servico renderer entry-point runs through
1721    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1722    /// `feira deploy` per-verb `first_servico_path` walk at
1723    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1724    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1725    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1726    /// per-Servico OCI packager, the future M4
1727    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1728    /// per-Servico OTel collector-config emit).
1729    ///
1730    /// Prior to this lift the `.servicos` field was accessed inline at
1731    /// five production sites — the compound-code-path `has_code =
1732    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1733    /// !caixa.servicos.is_empty()` OR-fold on the
1734    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1735    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1736    /// `caixa.servicos.is_empty()`
1737    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1738    /// per-entry `for p in &caixa.servicos`
1739    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1740    /// [`Self::declared_foreign_code_slots`]'s
1741    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1742    /// and the [`crate::require_single_servico`] V0 count gate's
1743    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1744    /// projection (both the accept-arm predicate and the
1745    /// diagnostic-carrying `ServicoCountMismatch { count }`
1746    /// projection) — five open-coded field-accesses across three
1747    /// crates that expressed no compile-time link back to the typed
1748    /// slot. A future extension of the `:servicos` axis to a richer
1749    /// component surface — a per-`:servicos` structured
1750    /// `ServicoEntry { path, world, capabilities }` at the storage
1751    /// layer once the substrate absorbs the per-component WIT-world +
1752    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1753    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1754    /// materializer enforces per-CR (the "cluster policy demands every
1755    /// Servico declare an explicit `:world`" arm), a promotion of the
1756    /// plain `Vec<String>` byte-string list to a richer
1757    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1758    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1759    /// `starts_with(servicos_dir)` fence and the
1760    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1761    /// already resolve through, a promotion of the V0 singleton
1762    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1763    /// component-model multi-world boundary — would have had to be
1764    /// threaded through all five open-coded copies in lockstep or the
1765    /// layout gate, the shape validator, the V0 count gate, and the
1766    /// `feira chart` / `feira deploy` entry-point walks would silently
1767    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1768    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1769    /// yaml")` would satisfy layout while `feira chart` silently
1770    /// packaged a drifted other list, or vice versa). Lifting the
1771    /// resolution to a typed method on the substrate primitive means
1772    /// every downstream consumer of the caixa's per-`Caixa`
1773    /// ComputeUnit-CR-source surface reaches for exactly one typed
1774    /// dispatch — the resolver's accept-set migrates as a unit on any
1775    /// future axis addition.
1776    ///
1777    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1778    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1779    /// projection pattern [`Self::autores`] (b5d813f) opened,
1780    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1781    /// (8a36c23) closed the universal-axis text-tag family of, and
1782    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1783    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1784    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1785    /// a substrate-canonical slice accessor, the trio of code-surface
1786    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1787    /// tuple carries is complete on the typed dispatch surface (the
1788    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1789    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1790    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1791    /// per-element accessor swap in isolation — a future companion lift
1792    /// promotes the tuple's element type to `&[String]` and threads the
1793    /// triple of typed dispatches through as a unit). Sibling in shape
1794    /// to the peer per-`:supervisor`
1795    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1796    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1797    /// (a6e18d7), per-`:membros`
1798    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1799    /// per-`:contratos`
1800    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1801    /// per-`:upgrade-from :instructions`
1802    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1803    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1804    /// typed-slot list axes, extended here to the outer top-level
1805    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1806    /// `&Vec<String>`) because every downstream consumer of the
1807    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1808    /// the slice-view is the narrowest borrow that supports every
1809    /// present + roadmapped consumer (`.iter()`, `.len()`,
1810    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1811    /// grow/push/reserve surface no consumer of the typed view reaches
1812    /// for (the storage-side `Vec` remains reachable through the
1813    /// `pub servicos` field for the mutation-carrying serde round-trip
1814    /// and per-test fixture-mutation paths, and for the
1815    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1816    /// homogeneous-element-type shape carries the raw field access
1817    /// until the trio-closure lift promotes the tuple as a unit).
1818    /// Named `servicos()` to match the storage field's name; the
1819    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1820    /// vocabulary the slot's docstring already carries.
1821    #[must_use]
1822    pub fn servicos(&self) -> &[String] {
1823        self.servicos.as_slice()
1824    }
1825
1826    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
1827    /// runtime-dependency-declaration-list slice-accessor every consumer
1828    /// of the top-level manifest's runtime-dep-graph axis keys off —
1829    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
1830    /// slice-view over the same backing buffer the raw
1831    /// `self.deps.as_slice()` field access borrows from. Empty-list-
1832    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
1833    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1834    /// derive folds an omitted `:deps` through `#[serde(default)]` to
1835    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1836    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
1837    /// degenerates to an empty slice on that arm without any silent
1838    /// `None` collapse).
1839    ///
1840    /// The `:deps` slot carries the universal-axis runtime dependency
1841    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1842    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1843    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
1844    /// every downstream resolver-facing artifact emits under) — the
1845    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
1846    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
1847    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
1848    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
1849    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
1850    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
1851    /// maps onto every load-bearing downstream consumer the substrate
1852    /// carries — the [`Self::validate_deps`] per-entry
1853    /// [`Dep::validate`] + within-list dedup walk at
1854    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
1855    /// cross-list self-reference gate at caixa-core/src/layout.rs that
1856    /// checks each entry against the caixa's own `:nome`, the
1857    /// caixa-resolver `for dep in &root.deps` closure walk at
1858    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
1859    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
1860    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
1861    /// caixa-crd/src/conversion.rs that materializes each entry into the
1862    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
1863    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1864    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
1865    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
1866    /// closure emit walk the caixa-resolver docstring roadmaps).
1867    ///
1868    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1869    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1870    /// sibling `:deps-dev` future lift closes on. Peer of the closed
1871    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
1872    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
1873    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
1874    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
1875    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
1876    /// pattern onto a novel element-type axis (`Dep` composite vs the
1877    /// prior sibling family's `String` scalar). Sibling in shape to the
1878    /// peer per-`:supervisor`
1879    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1880    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1881    /// (a6e18d7), per-`:membros`
1882    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1883    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1884    /// (0dcc926), and per-`:upgrade-from :instructions`
1885    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1886    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1887    /// typed-slot list axes, extended here to the outer top-level
1888    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
1889    /// (not `&Vec<Dep>`) because every downstream consumer of the
1890    /// runtime-dep list treats it as a read-only sequence — the slice-
1891    /// view is the narrowest borrow that supports every present +
1892    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1893    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1894    /// of the typed view reaches for (the storage-side `Vec` remains
1895    /// reachable through the `pub deps` field for the mutation-carrying
1896    /// serde round-trip and per-test fixture-mutation paths). Named
1897    /// `deps()` to match the storage field's name; the accessor's
1898    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1899    /// slot's docstring already carries.
1900    #[must_use]
1901    pub fn deps(&self) -> &[Dep] {
1902        self.deps.as_slice()
1903    }
1904
1905    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
1906    /// development-only-dependency-declaration-list slice-accessor every
1907    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
1908    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
1909    /// slice-view over the same backing buffer the raw
1910    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
1911    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
1912    /// form supplies with an empty `()` when unset; the
1913    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
1914    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1915    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
1916    /// the returned `&[Dep]` degenerates to an empty slice on that arm
1917    /// without any silent `None` collapse).
1918    ///
1919    /// The `:deps-dev` slot carries the universal-axis dev-only
1920    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
1921    /// the author-facing sibling of `:deps` that every `defcaixa` form
1922    /// supplies to declare tests / lint / bench closures the runtime
1923    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
1924    /// axis every downstream test-facing artifact emits under, matching
1925    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
1926    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
1927    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
1928    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
1929    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
1930    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
1931    /// within-list duplicate `:nome` rejected through
1932    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
1933    /// load-bearing downstream consumer the substrate carries — the
1934    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
1935    /// dedup walk at caixa-core/src/manifest.rs, the
1936    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
1937    /// gate at caixa-core/src/layout.rs that checks each entry against
1938    /// the caixa's own `:nome`, the caixa-resolver
1939    /// `for dep in &root.deps_dev` closure walk at
1940    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
1941    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
1942    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
1943    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
1944    /// overlay the M4 CR materializer resolves per-CR, the future
1945    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
1946    /// roadmaps).
1947    ///
1948    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1949    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1950    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
1951    /// jointly close the two-list dep-graph surface every downstream
1952    /// resolver-facing consumer keys off (runtime `:deps` +
1953    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
1954    /// pair the [`Self::validate_deps`] gate already walks in canonical
1955    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
1956    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
1957    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
1958    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
1959    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
1960    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
1961    /// dev-dep composite-element axis (`Dep` composite, matching the
1962    /// [`Self::deps`] element type). Sibling in shape to the peer
1963    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1964    /// (bc92bce), per-`:placement`
1965    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1966    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1967    /// (6c77e36), per-`:contratos`
1968    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1969    /// per-`:upgrade-from :instructions`
1970    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1971    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1972    /// typed-slot list axes, folded here to the outer top-level
1973    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
1974    /// (not `&Vec<Dep>`) because every downstream consumer of the
1975    /// dev-dep list treats it as a read-only sequence — the slice-view
1976    /// is the narrowest borrow that supports every present +
1977    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1978    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1979    /// of the typed view reaches for (the storage-side `Vec` remains
1980    /// reachable through the `pub deps_dev` field for the mutation-
1981    /// carrying serde round-trip and per-test fixture-mutation paths).
1982    /// Named `deps_dev()` to match the storage field's `snake_case` name;
1983    /// the kebab-case author-surface tag `:deps-dev` is the same axis
1984    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
1985    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1986    /// docstring already carries.
1987    #[must_use]
1988    pub fn deps_dev(&self) -> &[Dep] {
1989        self.deps_dev.as_slice()
1990    }
1991
1992    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
1993    /// every consumer that walks one of the two dep-list axes keyed on a
1994    /// [`crate::dep::DepList`] discriminant reaches for — routes the
1995    /// `(list: DepList) -> &[Dep]` projection through one typed method on
1996    /// the substrate primitive rather than the prior open-coded
1997    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
1998    /// inline dispatch every per-axis walker would otherwise carry.
1999    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
2000    /// `&[Dep]` slice-view over the same backing buffer the sibling
2001    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
2002    /// accessors borrow from, preserving the empty-list-carrying invariant
2003    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
2004    /// are default-empty axes every `defcaixa` form supplies with an empty
2005    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
2006    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
2007    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
2008    /// returned `&[Dep]` degenerates to an empty slice on either arm
2009    /// without any silent `None` collapse).
2010    ///
2011    /// The [`crate::dep::DepList`] closed-set typed enum is the
2012    /// substrate's canonical discriminator for the "runtime-closure
2013    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
2014    /// consumer dispatches on — the compiler-checked exhaustiveness on
2015    /// the enum's `match` arms is the build-time guarantee that no future
2016    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
2017    /// that a future third dep-list axis (a `:deps-build` build-only
2018    /// closure once the substrate grows cross-artifact heterogeneous
2019    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
2020    /// consumer. Prior to this the read side carried two per-slot
2021    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
2022    /// typed dispatch that a per-axis walker could parametrise on, so
2023    /// every per-list walker (the [`Self::validate_deps`] per-list
2024    /// [`crate::render::insert_first_seen`] dedup walk, a future
2025    /// `feira app graph` per-list dep summary, a future M4 per-cluster
2026    /// dev-closure-audit overlay the CR materializer resolves per-CR)
2027    /// open-coded the same two-block "run over `:deps`, then run over
2028    /// `:deps-dev`" pattern — a silent duplication that a future third
2029    /// dep-list axis would have had to grow a third block at every site.
2030    ///
2031    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
2032    /// (359fba5) — closes the two-side dispatch symmetry on the outer
2033    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
2034    /// side, `deps_of` on the read side, both keyed on the same
2035    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
2036    /// the substrate primitive, thin projections at each consumer"
2037    /// discipline the sibling per-slot read accessors ([`Self::nome`]
2038    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
2039    /// the outer-[`Caixa`] typed-dispatch read surface.
2040    #[must_use]
2041    pub fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
2042        match list {
2043            crate::dep::DepList::Prod => self.deps(),
2044            crate::dep::DepList::Dev => self.deps_dev(),
2045        }
2046    }
2047
2048    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
2049    /// consumer that appends to one of the two dep-list axes keys off
2050    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
2051    /// method on the substrate primitive rather than the prior
2052    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
2053    /// else { &mut caixa.deps }` inline dispatch + open-coded
2054    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
2055    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
2056    /// a within-list name collision — the same `list: &'static str`
2057    /// diagnostic shape [`Self::validate_deps`]'s per-list
2058    /// [`crate::render::insert_first_seen`] walk raises on the peer
2059    /// parse-time within-list dedup axis, so a future author reading a
2060    /// `feira add` refusal and a `feira build` refusal reaches for the
2061    /// same corrective surface without switching diagnostic idioms.
2062    ///
2063    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
2064    /// closed-set typed carrier for the "runtime-closure `:deps` vs
2065    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
2066    /// dispatches on — the compiler-checked exhaustiveness on the
2067    /// enum's `match` arms is the build-time guarantee that no future
2068    /// per-list mutation-site regresses to a bare-`bool`-flag
2069    /// (`is_dev: bool`) inline dispatch that a future third
2070    /// dep-list axis (a `:deps-build` build-only closure once the
2071    /// substrate grows cross-artifact heterogeneous dep-graphs, per
2072    /// CAIXA-SDLC §I) would silently split at every consumer.
2073    ///
2074    /// Same "one typed dispatch on the substrate primitive, thin
2075    /// projections at each consumer" discipline the sibling per-slot
2076    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
2077    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
2078    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
2079    /// the substrate's first typed-mutation dispatch on the top-level
2080    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
2081    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
2082    /// diagnostic path routed no through-line back to the typed slot,
2083    /// so a future extension of either dep-list axis to a richer author
2084    /// surface (a per-cluster override the operator pins through a
2085    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
2086    /// roadmap acknowledges, an M4
2087    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
2088    /// admission-webhook that normalized the list at admission time)
2089    /// would have had to be threaded through the `feira add` mutation
2090    /// site in lockstep with every read consumer or one path would
2091    /// silently disagree with the other on which list a given dep lands
2092    /// in. Lifting the resolution rule to a typed method on the
2093    /// substrate primitive means every downstream dep-list-mutating
2094    /// consumer of the top-level manifest reaches for exactly one typed
2095    /// dispatch — the resolver's accept-set migrates as a unit on any
2096    /// future axis addition.
2097    ///
2098    /// # Errors
2099    ///
2100    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
2101    /// when another entry in the same list already carries the same
2102    /// `:nome` — the mutation is refused and the caller can surface the
2103    /// typed diagnostic to the author (the `feira add` verb routes the
2104    /// error through `anyhow::Error::from`, which preserves the
2105    /// canonical `#[error(...)]`-templated diagnostic body).
2106    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
2107        let target = match list {
2108            crate::dep::DepList::Prod => &mut self.deps,
2109            crate::dep::DepList::Dev => &mut self.deps_dev,
2110        };
2111        if target.iter().any(|d| d.nome() == dep.nome()) {
2112            return Err(DepError::DuplicateNome {
2113                nome: dep.nome().to_string(),
2114                list: list.as_str(),
2115            });
2116        }
2117        target.push(dep);
2118        Ok(())
2119    }
2120
2121    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
2122    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
2123    /// composite-reference accessor every consumer of the top-level
2124    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
2125    /// off — returns the author-declared `:limits` typed composite
2126    /// verbatim as an `Option<&LimitsSpec>` reference over the same
2127    /// backing storage the raw `self.limits.as_ref()` field access
2128    /// borrows from, with `None` naming the "no `:limits` block
2129    /// authored — every per-axis Lunatic-sandbox cap defers to the
2130    /// wasm-engine-default arm named on the per-axis
2131    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
2132    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
2133    /// docstrings" partition every downstream Servico-M2-overlay
2134    /// emitter treats as "emit nothing" and the sibling
2135    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
2136    /// treats as "skip the per-axis
2137    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
2138    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
2139    ///
2140    /// The outer `:limits` slot carries the M2 Servico-runtime typed
2141    /// composite — the load-bearing container of every Lunatic-shaped
2142    /// per-process wasm32-sandbox cap axis every long-running wasm
2143    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
2144    /// Lunatic per-process linear-memory / fuel / wall-clock /
2145    /// millicore cap primitives translated onto pleme-io's typed
2146    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
2147    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2148    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2149    /// chart both fan on). Every per-`:limits` axis threads through a
2150    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
2151    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
2152    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
2153    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
2154    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
2155    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
2156    /// consumer that reaches for a limits axis first passes through
2157    /// this outer accessor onto the composite and then dispatches
2158    /// onto the per-axis accessor — the two-level dispatch means
2159    /// every per-`:limits` reader now routes through a typed dispatch
2160    /// on the substrate primitive at both altitudes.
2161    ///
2162    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
2163    /// was accessed inline at three production sites — the
2164    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
2165    /// `if let Some(l) = &caixa.limits { … }` traversal head
2166    /// (caixa-core/src/layout.rs:882, which drives the per-axis
2167    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
2168    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
2169    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
2170    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
2171    /// [`LimitsSpec::validate`] fans onto), the
2172    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
2173    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
2174    /// head (caixa-core/src/render.rs:18504, which drives the
2175    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
2176    /// projection every `caixa-helm` / `caixa-flux` Servico values-
2177    /// block emitter fans on), and the
2178    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2179    /// set enumerator's `self.limits.is_some()` presence probe
2180    /// (caixa-core/src/manifest.rs:1788, which drives the
2181    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
2182    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2183    /// gate reads) — three open-coded outer-field accesses that
2184    /// expressed no compile-time link back to the typed slot at the
2185    /// [`Caixa`] altitude. A future extension of the `:limits` outer
2186    /// axis to a richer author surface (a multi-`:limits` list the M4
2187    /// CR materializer resolves per-CR at admission time so a Servico
2188    /// can expose a compute-heavy + IO-heavy limits pair, a per-
2189    /// cluster `:limits-overrides` slot the operator pins so a
2190    /// cluster-specific policy can tighten a caixa-declared cap
2191    /// without re-authoring the `caixa.lisp`, a promotion of the
2192    /// plain `Option<LimitsSpec>` to a richer
2193    /// `{static, dynamic}` partition once the wasm-engine's runtime-
2194    /// resolved dynamic-cap surface lands) would have had to be
2195    /// threaded through all three open-coded copies in lockstep or
2196    /// one consumer would silently disagree with the peers on which
2197    /// limits composite a given Caixa resolves to — the layout gate's
2198    /// per-axis bracket-dispatch seed reading the raw slot while the
2199    /// peer `servico_m2_overlay` emitter read an operator-resolved
2200    /// slot would silently split the build-time sandbox-shape gate
2201    /// from the runtime `ComputeUnit` CR emission gate, a three-
2202    /// consumer split at the layout gate, the M2 overlay emitter, and
2203    /// the declared-slot enumerator far from the source `caixa.lisp`
2204    /// with no field naming the limits-drift root cause. Lifting the
2205    /// resolution rule to a typed method on the substrate primitive
2206    /// means every downstream consumer of the caixa's per-`Caixa`
2207    /// Lunatic-sandboxing outer-composite surface reaches for exactly
2208    /// one typed dispatch — the resolver's accept-set migrates as a
2209    /// unit on any future axis addition.
2210    ///
2211    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
2212    /// composite-reference accessor — opens the outer-`Caixa`
2213    /// `Option<&Composite>` composite-reference projection pattern the
2214    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
2215    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
2216    /// [`crate::aplicacao::Placement`] / `:entrada`
2217    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
2218    /// fold on. Peer of the M3 mesh-slot outer-composite family the
2219    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2220    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2221    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2222    /// accessors already close on the outer [`crate::AplicacaoSpec`]
2223    /// altitude — extends that "one typed dispatch on the substrate
2224    /// primitive, thin projections at each consumer" discipline onto
2225    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2226    /// runtime slot family's outer-composite axis. Returns
2227    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2228    /// clone) because every downstream consumer of the limits
2229    /// composite treats it as a read-only per-axis dispatch source —
2230    /// the reference-view is the narrowest borrow that supports every
2231    /// present + roadmapped consumer (per-axis accessor dispatch,
2232    /// `.is_empty()`-gated overlay projection, presence-probe early
2233    /// return on the "author-omitted `:limits` ⇒ engine-default
2234    /// applies" partition) without cloning the composite through
2235    /// every consumer's fast path. The `Option` half of the return-
2236    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2237    /// engine-default applies" partition (not a default composite the
2238    /// downstream must reject on emptiness) — the accessor projects
2239    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2240    /// reference-return unchanged. Named `limits()` to match the
2241    /// storage field's name verbatim and the tatara-lisp author-
2242    /// surface term (`:limits`) the field's own docstring already
2243    /// carries.
2244    #[must_use]
2245    pub fn limits(&self) -> Option<&LimitsSpec> {
2246        self.limits.as_ref()
2247    }
2248
2249    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2250    /// composite OTP-`gen_server`-shaped callback-table optional-
2251    /// composite-reference accessor every consumer of the top-level
2252    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2253    /// keys off — returns the author-declared `:behavior` typed
2254    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2255    /// the same backing storage the raw `self.behavior.as_ref()` field
2256    /// access borrows from, with `None` naming the "no `:behavior`
2257    /// block authored — every per-callback OTP-shaped hook defers to
2258    /// the wasm-engine's runtime default arm named on the per-axis
2259    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2260    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2261    /// [`BehaviorSpec::on_state_change`] /
2262    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2263    /// partition every downstream Servico-M2-overlay emitter treats as
2264    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2265    /// per-`:behavior` shape gate treats as "skip the per-arm
2266    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2267    /// per-callback on-disk `MissingEntry` existence check".
2268    ///
2269    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2270    /// composite — the load-bearing container of every OTP-shaped
2271    /// per-Servico lifecycle-callback path axis every long-running wasm
2272    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2273    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2274    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2275    /// translated onto pleme-io's typed `:behavior :on-init` /
2276    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2277    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2278    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2279    /// chart both fan on). Every per-`:behavior` axis threads through a
2280    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2281    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2282    /// Every downstream consumer that reaches for a behavior axis
2283    /// first passes through this outer accessor onto the composite
2284    /// and then dispatches onto the per-callback accessor — the
2285    /// two-level dispatch means every per-`:behavior` reader now
2286    /// routes through a typed dispatch on the substrate primitive at
2287    /// both altitudes.
2288    ///
2289    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2290    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2291    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2292    /// keys the "per-version `:state-change` instruction must have a
2293    /// `:on-state-change` callback" precondition off this accessor's
2294    /// composite (the callback-side counterpart to the
2295    /// `:upgrade-from :instructions :state-change :script` refusal at
2296    /// the appup-side). Threading that gate's traversal input through
2297    /// this accessor closes the cross-slot invariant on the substrate
2298    /// primitive, not on the raw field.
2299    ///
2300    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2301    /// composite was accessed inline at four production sites — the
2302    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2303    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2304    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2305    /// `BehaviorError` refusal cascade + the per-callback on-disk
2306    /// [`crate::LayoutError::MissingEntry`] existence check under
2307    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2308    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2309    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2310    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2311    /// drives the `:state-change` ↔ `:on-state-change` precondition
2312    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2313    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2314    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2315    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2316    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2317    /// Servico values-block emitter fans on), and the
2318    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2319    /// set enumerator's `self.behavior.is_some()` presence probe
2320    /// (caixa-core/src/manifest.rs:1919, which drives the
2321    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2322    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2323    /// gate reads) — four open-coded outer-field accesses that
2324    /// expressed no compile-time link back to the typed slot at the
2325    /// [`Caixa`] altitude. A future extension of the `:behavior`
2326    /// outer axis to a richer author surface (a per-callback overlay
2327    /// resolver the operator materializes at admission time so a
2328    /// cluster-specific policy can inject a per-callback tracing
2329    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2330    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2331    /// dynamic}` partition once a runtime-resolved behavior-swap
2332    /// surface lands, the M4 per-callback middleware chain the
2333    /// caixa-operator's per-Servico admission webhook keys off) would
2334    /// have had to be threaded through all four open-coded copies in
2335    /// lockstep or one consumer would silently disagree with the
2336    /// peers on which behavior composite a given Caixa resolves to —
2337    /// the layout gate's per-callback existence-check seed reading
2338    /// the raw slot while the peer `servico_m2_overlay` emitter read
2339    /// an operator-resolved slot would silently split the build-time
2340    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2341    /// gate from the cross-slot `:state-change` composition gate from
2342    /// the M2 declared-slot enumerator, a four-consumer split far
2343    /// from the source `caixa.lisp` with no field naming the
2344    /// behavior-drift root cause. Lifting the resolution rule to a
2345    /// typed method on the substrate primitive means every downstream
2346    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2347    /// composite surface reaches for exactly one typed dispatch — the
2348    /// resolver's accept-set migrates as a unit on any future axis
2349    /// addition.
2350    ///
2351    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2352    /// composite-reference accessor — sibling to the opening
2353    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2354    /// `Option<&Composite>` composite-reference sub-family, extends
2355    /// the "one typed dispatch on the substrate primitive, thin
2356    /// projections at each consumer" discipline onto the second of
2357    /// the three M2 Servico-runtime slots. The remaining
2358    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2359    /// altitude — the M3 mesh-slot family (`:politicas`,
2360    /// `:placement`, `:entrada` — already closed on the inner
2361    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2362    /// d32111c) — remain the future sibling lifts on the outer
2363    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2364    /// the owning composite by copy or clone) because every
2365    /// downstream consumer of the behavior composite treats it as a
2366    /// read-only per-callback dispatch source — the reference-view is
2367    /// the narrowest borrow that supports every present + roadmapped
2368    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2369    /// overlay projection, presence-probe early return on the
2370    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2371    /// partition, cross-slot `:state-change` composition input)
2372    /// without cloning the composite through every consumer's fast
2373    /// path. The `Option` half of the return-type preserves the
2374    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2375    /// applies" partition (not a default composite the downstream
2376    /// must reject on emptiness) — the accessor projects the raw
2377    /// `Option<BehaviorSpec>` slot's presence bit through the
2378    /// reference-return unchanged. Named `behavior()` to match the
2379    /// storage field's name verbatim and the tatara-lisp author-
2380    /// surface term (`:behavior`) the field's own docstring already
2381    /// carries.
2382    #[must_use]
2383    pub fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2384        self.behavior.as_ref()
2385    }
2386
2387    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2388    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2389    /// reference accessor every consumer of the top-level manifest's
2390    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2391    /// reader keys off — returns the author-declared `:politicas` typed
2392    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2393    /// same backing storage the raw `self.politicas.as_ref()` field
2394    /// access borrows from, with `None` naming the "no `:politicas`
2395    /// block authored — every per-axis mesh-policy scalar defers to the
2396    /// cluster-default arm named on the per-axis
2397    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2398    /// [`crate::aplicacao::MeshPolicy::retries`] /
2399    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2400    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2401    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2402    /// docstrings" partition every downstream caixa-mesh /
2403    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2404    /// "emit no per-`:politicas` overlay" and the sibling
2405    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2406    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2407    /// arm.
2408    ///
2409    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2410    /// Aplicacao typed composite — the load-bearing container of every
2411    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2412    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2413    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2414    /// composite; §V — the "no infinite blocking" per-call deadline +
2415    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2416    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2417    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2418    /// threads through a lifted per-slot accessor on the
2419    /// [`crate::aplicacao::MeshPolicy`] type: the
2420    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2421    /// mTLS-enforcement toggle, the
2422    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2423    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2424    /// (7073d0f) Gateway-API per-call deadline, the
2425    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2426    /// Envoy-outlier-detection composite. Every downstream consumer
2427    /// that reaches for a mesh-policy axis first passes through this
2428    /// outer accessor onto the composite and then dispatches onto the
2429    /// per-axis accessor — the two-level dispatch means every per-
2430    /// `:politicas` reader now routes through a typed dispatch on the
2431    /// substrate primitive at both altitudes.
2432    ///
2433    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2434    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2435    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2436    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2437    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2438    /// composite whether or not the author declared the outer slot.
2439    /// The outer accessor preserves the "author-omitted vs authored-
2440    /// empty" partition the inner accessor's `is_empty()`-gated
2441    /// renderer overlay collapses — routing the presence bit through
2442    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2443    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2444    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2445    ///
2446    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2447    /// composite was accessed inline at two production sites — the
2448    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2449    /// `self.politicas.clone().unwrap_or_default()` traversal head
2450    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2451    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2452    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2453    /// then observes), and the [`Self::declared_mesh_slots`] M3
2454    /// declared-slot-set enumerator's `self.politicas.is_some()`
2455    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2456    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2457    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2458    /// coherence gate reads) — two open-coded outer-field accesses
2459    /// that expressed no compile-time link back to the typed slot at
2460    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2461    /// outer axis to a richer author surface (a per-cluster
2462    /// `:politicas-overrides` slot the operator materializes at
2463    /// admission time so a cluster-specific policy can tighten the
2464    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2465    /// promotion of the plain `Option<MeshPolicy>` to a richer
2466    /// `{static, dynamic}` partition once the M4 per-edge
2467    /// contrato-scoped policy-override surface lands, the M5 traffic-
2468    /// shaping composition the caixa-operator's per-Aplicacao mesh
2469    /// admission webhook keys off) would have had to be threaded
2470    /// through both open-coded copies in lockstep or the Aplicacao-
2471    /// composition seed's default-fold arm would silently disagree
2472    /// with the M3 declared-slot enumerator on which policy composite
2473    /// a given Caixa resolves to — the seed reading an operator-
2474    /// resolved slot while the enumerator's presence probe read the
2475    /// raw slot would silently split the build-time mesh-artifact
2476    /// emission gate from the M3 declared-slot enumerator's kind-
2477    /// coherence gate, a two-consumer split far from the source
2478    /// `caixa.lisp` with no field naming the policy-drift root cause.
2479    /// Lifting the resolution rule to a typed method on the substrate
2480    /// primitive means every downstream consumer of the caixa's per-
2481    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2482    /// reaches for exactly one typed dispatch — the resolver's
2483    /// accept-set migrates as a unit on any future axis addition.
2484    ///
2485    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2486    /// composite-reference accessor — sibling to the opening
2487    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2488    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2489    /// reference sub-family, extends the "one typed dispatch on the
2490    /// substrate primitive, thin projections at each consumer"
2491    /// discipline onto the first of the three M3 mesh-slot axes.
2492    /// Peer of the closed inner mesh-slot outer-composite family the
2493    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2494    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2495    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2496    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2497    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2498    /// mesh-slot arm of the composite-reference family the remaining
2499    /// two axes (`:placement`, `:entrada`) fold onto in future
2500    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2501    /// composite by copy or clone) because every downstream consumer
2502    /// of the mesh-policy composite treats it as a read-only per-axis
2503    /// dispatch source — the reference-view is the narrowest borrow
2504    /// that supports every present + roadmapped consumer (per-axis
2505    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2506    /// presence-probe early return on the "author-omitted `:politicas`
2507    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2508    /// seed's default-fold arm) without cloning the composite through
2509    /// every consumer's fast path. The `Option` half of the return-
2510    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2511    /// cluster-default applies" partition (not a default composite
2512    /// the downstream must reject on emptiness) — the accessor
2513    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2514    /// through the reference-return unchanged. Named `politicas()` to
2515    /// match the storage field's name verbatim and the tatara-lisp
2516    /// author-surface term (`:politicas`) the field's own docstring
2517    /// already carries.
2518    #[must_use]
2519    pub fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2520        self.politicas.as_ref()
2521    }
2522
2523    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2524    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2525    /// reference accessor every consumer of the top-level manifest's
2526    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2527    /// reader keys off — returns the author-declared `:placement` typed
2528    /// composite verbatim as an `Option<&Placement>` reference over the
2529    /// same backing storage the raw `self.placement.as_ref()` field
2530    /// access borrows from, with `None` naming the "no `:placement`
2531    /// block authored — every per-axis placement scalar defers to the
2532    /// cluster-default arm named on the per-axis
2533    /// [`crate::aplicacao::Placement::estrategia`] /
2534    /// [`crate::aplicacao::Placement::clusters`] /
2535    /// [`crate::aplicacao::Placement::affinity`] /
2536    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2537    /// docstrings" partition every downstream caixa-mesh /
2538    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2539    /// "emit no per-`:placement` overlay" and the sibling
2540    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2541    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2542    ///
2543    /// The outer `:placement` slot carries the M3 mesh-slot per-
2544    /// Aplicacao typed distribution composite — the load-bearing
2545    /// container of every where-does-this-Aplicacao-run axis every
2546    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2547    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2548    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2549    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2550    /// Aplicacao's typed distribution composite; §V CSE invariants —
2551    /// "distribution is a first-class typed composite, not a runtime
2552    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2553    /// typed inter-Servico contrato-edge overlay the per-cluster
2554    /// mesh renderer keys off). Every per-`:placement` axis threads
2555    /// through a lifted per-slot accessor on the
2556    /// [`crate::aplicacao::Placement`] type: the
2557    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2558    /// MESH-COMPOSITION distribution-strategy scalar, the
2559    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2560    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2561    /// M3-Adaptive-compression-hint optional-scalar, and the
2562    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2563    /// sharding extractor-expression optional-scalar. Every downstream
2564    /// consumer that reaches for a placement axis first passes through
2565    /// this outer accessor onto the composite and then dispatches onto
2566    /// the per-axis accessor — the two-level dispatch means every per-
2567    /// `:placement` reader now routes through a typed dispatch on the
2568    /// substrate primitive at both altitudes.
2569    ///
2570    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2571    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2572    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2573    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2574    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2575    /// whether or not the author declared the outer slot. The outer
2576    /// accessor preserves the "author-omitted vs authored-empty" partition
2577    /// the inner accessor collapses at the cluster-default fold —
2578    /// routing the presence bit through this accessor keeps the
2579    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2580    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2581    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2582    /// dispatch.
2583    ///
2584    /// Prior to this lift the `.placement` `Option<Placement>`
2585    /// composite was accessed inline at two production sites — the
2586    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2587    /// `self.placement.clone().unwrap_or_default()` traversal head
2588    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2589    /// the [`crate::aplicacao::Placement::default`] cluster-default
2590    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2591    /// then observes), and the [`Self::declared_mesh_slots`] M3
2592    /// declared-slot-set enumerator's `self.placement.is_some()`
2593    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2594    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2595    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2596    /// coherence gate reads) — two open-coded outer-field accesses
2597    /// that expressed no compile-time link back to the typed slot at
2598    /// the [`Caixa`] altitude. A future extension of the `:placement`
2599    /// outer axis to a richer author surface (a per-cluster
2600    /// `:placement-overrides` slot the operator materializes at
2601    /// admission time so a cluster-specific placement can tighten the
2602    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2603    /// per-tenant placement-alias table the M4
2604    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2605    /// per-CR at admission time, a promotion of the plain
2606    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2607    /// once Orleans-style virtual-actor dynamic placement comes into
2608    /// typed scope) would have had to be threaded through both open-
2609    /// coded copies in lockstep or the Aplicacao-composition seed's
2610    /// default-fold arm would silently disagree with the M3 declared-
2611    /// slot enumerator on which distribution composite a given Caixa
2612    /// resolves to — the seed reading an operator-resolved slot while
2613    /// the enumerator's presence probe read the raw slot would
2614    /// silently split the build-time distribution-artifact emission
2615    /// gate from the M3 declared-slot enumerator's kind-coherence
2616    /// gate, a two-consumer split far from the source `caixa.lisp`
2617    /// with no field naming the distribution-drift root cause.
2618    /// Lifting the resolution rule to a typed method on the substrate
2619    /// primitive means every downstream consumer of the caixa's per-
2620    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2621    /// reaches for exactly one typed dispatch — the resolver's
2622    /// accept-set migrates as a unit on any future axis addition.
2623    ///
2624    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2625    /// composite-reference accessor — sibling to the opening
2626    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2627    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2628    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2629    /// composite-reference sub-family, folds on the "one typed
2630    /// dispatch on the substrate primitive, thin projections at each
2631    /// consumer" discipline extended onto the second of the three M3
2632    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2633    /// composite family the sibling
2634    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2635    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2636    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2637    /// accessor pins already close on the inner
2638    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2639    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2640    /// [`Self::politicas`] opened, extending the discipline onto the
2641    /// second of the three M3 mesh-slot axes. The remaining M3
2642    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2643    /// discipline in the final sibling lift, closing the outer top-
2644    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2645    /// Returns `Option<&Placement>` (not the owning composite by copy
2646    /// or clone) because every downstream consumer of the placement
2647    /// composite treats it as a read-only per-axis dispatch source —
2648    /// the reference-view is the narrowest borrow that supports every
2649    /// present + roadmapped consumer (per-axis accessor dispatch,
2650    /// serde composite-serialization on the programs.yaml overlay,
2651    /// presence-probe early return on the "author-omitted `:placement`
2652    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2653    /// seed's default-fold arm) without cloning the composite through
2654    /// every consumer's fast path. The `Option` half of the return-
2655    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2656    /// cluster-default applies" partition (not a default composite
2657    /// the downstream must reject on emptiness) — the accessor
2658    /// projects the raw `Option<Placement>` slot's presence bit
2659    /// through the reference-return unchanged. Named `placement()` to
2660    /// match the storage field's name verbatim and the tatara-lisp
2661    /// author-surface term (`:placement`) the field's own docstring
2662    /// already carries.
2663    #[must_use]
2664    pub fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2665        self.placement.as_ref()
2666    }
2667
2668    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2669    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2670    /// composite-reference accessor every consumer of the top-level
2671    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2672    /// composite reader keys off — returns the author-declared
2673    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2674    /// reference over the same backing storage the raw
2675    /// `self.entrada.as_ref()` field access borrows from, with `None`
2676    /// naming the "no `:entrada` block authored — this Aplicacao is
2677    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2678    /// partition every downstream caixa-mesh Gateway-API artifact
2679    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2680    /// backend for this Aplicacao" and the sibling
2681    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2682    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2683    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2684    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2685    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2686    /// the same `Option<&Entrada>` presence bit unchanged).
2687    ///
2688    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2689    /// Aplicacao typed external-gateway composite — the load-bearing
2690    /// container of every how-does-the-outside-world-reach-this-
2691    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2692    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2693    /// external-entry composite; §V CSE invariants — "the external
2694    /// gateway is a first-class typed composite, not a per-Servico
2695    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2696    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2697    /// API renderer keys off). Every per-`:entrada` axis threads
2698    /// through a lifted per-slot accessor on the
2699    /// [`crate::aplicacao::Entrada`] type: the
2700    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2701    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2702    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2703    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2704    /// backend `trigger.service.port` scalar, and the
2705    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2706    /// resolver every HTTPRoute-aware renderer consumes. Every
2707    /// downstream consumer that reaches for an entry axis first passes
2708    /// through this outer accessor onto the composite and then
2709    /// dispatches onto the per-axis accessor — the two-level dispatch
2710    /// means every per-`:entrada` reader now routes through a typed
2711    /// dispatch on the substrate primitive at both altitudes.
2712    ///
2713    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2714    /// seed: the Aplicacao-view builder forwards the outer `Option`
2715    /// arm verbatim (no default fold — `:entrada` is inherently
2716    /// optional; a cluster-internal Aplicacao has no external gateway
2717    /// at all, not "an external gateway that defaults to nothing"), so
2718    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2719    /// `Option<&Entrada>`-return accessor observes the same presence
2720    /// bit whether or not the author declared the outer slot. Routing
2721    /// the presence bit through this accessor keeps the
2722    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2723    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2724    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2725    /// hostname/backend/path emission dispatch.
2726    ///
2727    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2728    /// was accessed inline at two production sites — the
2729    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2730    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2731    /// which drives the forward onto the peer inner
2732    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2733    /// Gateway-API fan-out then observes), and the
2734    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2735    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2736    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2737    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2738    /// kind-coherence gate reads) — two open-coded outer-field
2739    /// accesses that expressed no compile-time link back to the typed
2740    /// slot at the [`Caixa`] altitude. A future extension of the
2741    /// `:entrada` outer axis to a richer author surface (a per-cluster
2742    /// `:entrada-overrides` slot the operator materializes at admission
2743    /// time so a cluster-specific hostname can pin the caixa-declared
2744    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2745    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2746    /// CR materializer resolves per-CR at admission time, a promotion
2747    /// of the plain `Option<Entrada>` to a richer
2748    /// `{public, private, internal}` partition once Cilium-identity-
2749    /// scoped internal gateways come into typed scope) would have had
2750    /// to be threaded through both open-coded copies in lockstep or the
2751    /// Aplicacao-composition seed's forward arm would silently
2752    /// disagree with the M3 declared-slot enumerator on which external-
2753    /// gateway composite a given Caixa resolves to — the seed reading
2754    /// an operator-resolved slot while the enumerator's presence probe
2755    /// read the raw slot would silently split the build-time gateway-
2756    /// artifact emission gate from the M3 declared-slot enumerator's
2757    /// kind-coherence gate, a two-consumer split far from the source
2758    /// `caixa.lisp` with no field naming the entry-drift root cause.
2759    /// Lifting the resolution rule to a typed method on the substrate
2760    /// primitive means every downstream consumer of the caixa's per-
2761    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2762    /// surface reaches for exactly one typed dispatch — the resolver's
2763    /// accept-set migrates as a unit on any future axis addition.
2764    ///
2765    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2766    /// return composite-reference accessor — closes the outer-`Caixa`
2767    /// `Option<&Composite>` composite-reference sub-family opened by
2768    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2769    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2770    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2771    /// folds on the "one typed dispatch on the substrate primitive,
2772    /// thin projections at each consumer" discipline extended onto the
2773    /// third and final M3 mesh-slot axis. Peer of the closed inner
2774    /// mesh-slot outer-composite family the sibling
2775    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2776    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2777    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2778    /// accessor pins already close on the inner
2779    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2780    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2781    /// altitudes of the outer-composite reference-return discipline
2782    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2783    /// slot presence) now carry the full five-arm accept-set behind a
2784    /// typed dispatch on the substrate primitive. Returns
2785    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2786    /// because every downstream consumer of the entrada composite
2787    /// treats it as a read-only per-axis dispatch source — the
2788    /// reference-view is the narrowest borrow that supports every
2789    /// present + roadmapped consumer (per-axis accessor dispatch,
2790    /// serde composite-serialization on the programs.yaml overlay,
2791    /// presence-probe early return on the "author-omitted `:entrada`
2792    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2793    /// seed's forward arm) without cloning the composite through every
2794    /// consumer's fast path. The `Option` half of the return-type
2795    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2796    /// cluster-internal Aplicacao" partition (not a default composite
2797    /// the downstream must reject on emptiness — a cluster-internal
2798    /// Aplicacao has no external gateway at all, not "a default gateway
2799    /// that emits nothing"); the accessor projects the raw
2800    /// `Option<Entrada>` slot's presence bit through the reference-
2801    /// return unchanged. Named `entrada()` to match the storage field's
2802    /// name verbatim and the tatara-lisp author-surface term
2803    /// (`:entrada`) the field's own docstring already carries.
2804    #[must_use]
2805    pub fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
2806        self.entrada.as_ref()
2807    }
2808
2809    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
2810    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
2811    /// an `Option<&CiRun>`, borrowed from the typed slot's own
2812    /// `Option<CiRun>` storage. `None` when the slot is absent (every
2813    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
2814    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
2815    /// not silently accepted).
2816    ///
2817    /// Named `ci()` to match the storage field's name and the
2818    /// tatara-lisp author surface (`:ci`); mirrors the sibling
2819    /// `Option<&Composite>` accessors on this same `Caixa` altitude
2820    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
2821    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
2822    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
2823    /// at every consumer.
2824    #[must_use]
2825    pub fn ci(&self) -> Option<&canteiro_types::CiRun> {
2826        self.ci.as_ref()
2827    }
2828
2829    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
2830    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
2831    /// accessor every consumer of the top-level manifest's per-Supervisor
2832    /// restart-strategy axis keys off — returns the author-declared
2833    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
2834    /// `Copy`-projected from the typed slot's own
2835    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
2836    /// (`:estrategia` is a flat-spread supervisor-only slot every
2837    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
2838    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2839    /// still omit to defer to [`RestartStrategy::default`] —
2840    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
2841    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
2842    /// [`SupervisorSpec::default`]-inherited strategy without any silent
2843    /// promotion to a fresh explicit variant at the accessor boundary).
2844    ///
2845    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
2846    /// restart-strategy discriminant every substrate-side per-Supervisor
2847    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
2848    /// closed-set `one_for_one | one_for_all | rest_for_one |
2849    /// simple_one_for_one` algebra translated onto pleme-io's typed
2850    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
2851    /// slot algebra the operator's hierarchical reconciliation scheduler
2852    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
2853    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
2854    /// supervisor slots are flat on Caixa (vs nested under a
2855    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
2856    /// level of nesting"), so the accessor's altitude is the outer
2857    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
2858    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
2859    /// (eafb619) accessor keys off. The two typed axes — the outer
2860    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
2861    /// (author-omitted arm carried as `None`) and the inner post-
2862    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
2863    /// (`Option` collapsed through the [`Self::supervisor_view`]
2864    /// `unwrap_or_default()` fold) — now share one accessor discipline for
2865    /// the shared substrate concept "the author-declared OTP-shaped
2866    /// sibling-restart-strategy variant that partitions the downstream
2867    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
2868    /// `None` arm is the pre-composition presence bit every declared-slot
2869    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
2870    /// inner-altitude non-`Option` `RestartStrategy` is the post-
2871    /// composition partition-dispatch input every strategy-arm consumer
2872    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
2873    /// Supervisor sibling-restart branch, the future M4
2874    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
2875    /// webhook) fans on.
2876    ///
2877    /// Prior to this lift the `.estrategia` field was accessed inline at
2878    /// two production sites in `caixa-core/src/manifest.rs` — the
2879    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
2880    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
2881    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2882    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
2883    /// `SupervisorSpec` construction site at `estrategia:
2884    /// self.estrategia.unwrap_or_default()` (which composes the flat-
2885    /// spread outer author-surface `Option<RestartStrategy>` onto the
2886    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
2887    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
2888    /// coded field-accesses that expressed no compile-time link back to
2889    /// the typed slot. A future extension of the outer `:estrategia` axis
2890    /// to a richer author surface (a per-cluster strategy override the
2891    /// operator pins through a future `:estrategia-overrides` overlay the
2892    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
2893    /// a per-tenant strategy-alias table the M4 CR materializer resolves
2894    /// per-CR, a per-Supervisor dynamic strategy derivation the future
2895    /// adaptive-supervision engine computes from child-failure-history
2896    /// topology, a per-child-cohort strategy split the future
2897    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
2898    /// absorption roadmap acknowledges, a promotion of the plain
2899    /// `Option<RestartStrategy>` to a richer
2900    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
2901    /// operator-resolved overlay lands) would have had to be threaded
2902    /// through both open-coded copies in lockstep or the enumerator's
2903    /// presence probe and the composition site's `unwrap_or_default()`
2904    /// fold would silently disagree on which strategy a given [`Caixa`]
2905    /// resolves to (an author's `:estrategia OneForAll` would satisfy
2906    /// the enumerator's presence probe while the composition site
2907    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
2908    /// the resolution rule to a typed method on the substrate primitive
2909    /// means every downstream consumer of the caixa's per-`Caixa` outer-
2910    /// altitude sibling-restart-strategy surface reaches for exactly one
2911    /// typed dispatch — the resolver's accept-set migrates as a unit on
2912    /// any future axis addition.
2913    ///
2914    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2915    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2916    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
2917    /// projection pattern the sibling per-`Caixa` `:max-restarts`
2918    /// `Option<u32>` and (through the future duration-newtype landing)
2919    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
2920    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
2921    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
2922    /// the post-composition [`SupervisorSpec`] altitude — same "one
2923    /// typed dispatch on the substrate primitive, thin projections at
2924    /// each consumer" discipline extended onto the pre-composition outer
2925    /// author-surface [`Caixa`] altitude for the same OTP-shaped
2926    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
2927    /// `Option<&Composite>` composite-reference family the sibling
2928    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
2929    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
2930    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
2931    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
2932    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
2933    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
2934    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2935    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
2936    /// pins on the inner-altitude per-`:placement` composite. Named
2937    /// `estrategia()` to match the storage field's name and the
2938    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
2939    /// / per-[`crate::aplicacao::Placement`] peer
2940    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
2941    /// verbatim; the accessor's identity name maps onto the canonical
2942    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
2943    /// docstring already carries.
2944    #[must_use]
2945    pub fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
2946        self.estrategia
2947    }
2948
2949    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
2950    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
2951    /// scalar accessor every consumer of the top-level manifest's per-
2952    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
2953    /// returns the author-declared `:max-restarts` typed `Option<u32>`
2954    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
2955    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
2956    /// accessor returns by value; no borrow of `&self` past the call).
2957    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
2958    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
2959    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2960    /// still omit to defer to the [`Self::supervisor_view`]
2961    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
2962    ///
2963    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
2964    /// `MaxIntensity` restart-budget count that pairs with the sibling
2965    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
2966    /// restart-intensity ratio the supervisor trips its own escalation on
2967    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
2968    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
2969    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
2970    /// reconciliation scheduler fans on). The slot is *flat-spread* on
2971    /// the outer top-level `Caixa` (per the field-shape docstring at
2972    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
2973    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
2974    /// accessor's altitude is the outer [`Caixa`] surface rather than the
2975    /// composed [`SupervisorSpec`] altitude the sibling
2976    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
2977    /// off. The two typed axes — the outer author-surface `Option<u32>`
2978    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
2979    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
2980    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
2981    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
2982    /// shared substrate concept "the author-declared OTP-shaped
2983    /// restart-budget count every downstream per-Supervisor consumer's
2984    /// restart-intensity budget-vs-count comparator fans on".
2985    ///
2986    /// Prior to this lift the `.max_restarts` field was accessed inline
2987    /// at two production sites in `caixa-core/src/manifest.rs` — the
2988    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
2989    /// presence-probe arm at `if self.max_restarts.is_some()` (which
2990    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
2991    /// kind-coherence gate's per-slot label push) and the
2992    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
2993    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
2994    /// flat-spread outer author-surface `Option<u32>` onto the inner
2995    /// post-composition [`SupervisorSpec`] `u32` field the
2996    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
2997    /// coded field-accesses that expressed no compile-time link back to
2998    /// the typed slot. A future extension of the outer `:max-restarts`
2999    /// axis to a richer author surface (a per-cluster restart-budget
3000    /// override the operator pins through a future `:max-restarts-overrides`
3001    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
3002    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
3003    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
3004    /// budget derivation the future adaptive-supervision engine computes
3005    /// from child-failure-history topology, a promotion of the plain
3006    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
3007    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
3008    /// per-child-cohort roadmap lands) would have had to be threaded
3009    /// through both open-coded copies in lockstep or the enumerator's
3010    /// presence probe and the composition site's `unwrap_or(5)` fold
3011    /// would silently disagree on which restart-budget a given [`Caixa`]
3012    /// resolves to (an author's `:max-restarts 10` would satisfy the
3013    /// enumerator's presence probe while the composition site silently
3014    /// composed the OTP-canonical `5`, or vice versa). Lifting the
3015    /// resolution rule to a typed method on the substrate primitive means
3016    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
3017    /// restart-budget-count surface reaches for exactly one typed dispatch
3018    /// — the resolver's accept-set migrates as a unit on any future axis
3019    /// addition.
3020    ///
3021    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3022    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3023    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
3024    /// projection pattern the sibling per-`Caixa`
3025    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
3026    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
3027    /// Peer of the inner-altitude
3028    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
3029    /// on the post-composition [`SupervisorSpec`] altitude — same "one
3030    /// typed dispatch on the substrate primitive, thin projections at
3031    /// each consumer" discipline extended onto the pre-composition outer
3032    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
3033    /// shaped restart-budget-count axis. Named `max_restarts()` to match
3034    /// the storage field's name and the per-[`SupervisorSpec`] peer
3035    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
3036    /// discipline verbatim; the accessor's identity maps onto the
3037    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
3038    /// field's docstring already carries.
3039    #[must_use]
3040    pub const fn max_restarts(&self) -> Option<u32> {
3041        self.max_restarts
3042    }
3043
3044    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
3045    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
3046    /// denominator raw-duration-string scalar accessor every consumer of
3047    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
3048    /// window axis keys off — returns the author-declared `:restart-window`
3049    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
3050    /// from the typed slot's own `Option<String>` storage. `None` when
3051    /// the slot is absent (the canonical "never reset — every restart
3052    /// across the supervisor's lifetime counts against the sibling
3053    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
3054    /// `defcaixa` carries by `#[serde(default)]` and every
3055    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
3056    /// [`Self::supervisor_view`] `restart_window: None` composition
3057    /// through the [`crate::supervisor::duration_codec::parse`] soft-
3058    /// swallow `.and_then(|s| … .ok())` fold).
3059    ///
3060    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
3061    /// shaped `Period` sliding-observation-interval duration string that
3062    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
3063    /// budget count to form the `MaxIntensity / Period` restart-intensity
3064    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
3065    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
3066    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
3067    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
3068    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
3069    /// holds an `Option<Duration>` routed through the shared
3070    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
3071    /// — so the outer altitude's accessor returns `Option<&str>` (raw
3072    /// authoring surface) while the inner altitude's
3073    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
3074    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
3075    /// is closed by the sibling [`Self::validate_restart_window`] gate
3076    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
3077    /// the offending value; the view-construction path
3078    /// [`Self::supervisor_view`] soft-swallows the same parse error to
3079    /// `None` to keep the view best-effort.
3080    ///
3081    /// Prior to this lift the `.restart_window` field was accessed inline
3082    /// at three production sites in `caixa-core/src/manifest.rs` — the
3083    /// [`Self::declared_supervisor_slots`]
3084    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
3085    /// `if self.restart_window.is_some()` (which drives the
3086    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3087    /// coherence gate's per-slot label push), the
3088    /// [`Self::validate_restart_window`] `let Some(s) =
3089    /// self.restart_window.as_deref()` empty-and-shape gate binding
3090    /// (which folds the raw string through the shared
3091    /// [`crate::supervisor::duration_codec::parse`] to surface
3092    /// [`ManifestError::RestartWindowMalformed`] naming the offending
3093    /// value), and the [`Self::supervisor_view`] `self.restart_window
3094    /// .as_deref().and_then(…)` view-construction fold (which composes
3095    /// the flat-spread outer author-surface `Option<String>` onto the
3096    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
3097    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
3098    /// three open-coded field-accesses that expressed no compile-time
3099    /// link back to the typed slot. A future extension of the outer
3100    /// `:restart-window` axis to a richer author surface (a per-cluster
3101    /// window override, a per-tenant window-alias table, a per-Supervisor
3102    /// dynamic window derivation the future adaptive-supervision engine
3103    /// computes from child-failure-history topology, a promotion of the
3104    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
3105    /// once the future author-surface parser lands at the [`Caixa`]
3106    /// altitude and the raw-string form is retired) would have had to be
3107    /// threaded through every open-coded copy in lockstep or the three
3108    /// consumers would silently disagree on which raw string a given
3109    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
3110    /// method on the substrate primitive means every downstream consumer
3111    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
3112    /// string surface reaches for exactly one typed dispatch — the
3113    /// resolver's accept-set migrates as a unit on any future axis
3114    /// addition.
3115    ///
3116    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
3117    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
3118    /// spread projection pattern the sibling per-`Caixa`
3119    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
3120    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
3121    /// the sub-family onto the sibling `Option<&str>` raw-duration-
3122    /// string arm (the outer altitude's raw-string form; the inner
3123    /// altitude's parsed [`Duration`] form is the peer
3124    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
3125    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
3126    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
3127    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
3128    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
3129    /// sub-family already carries — same "one typed dispatch on the
3130    /// substrate primitive, thin projections at each consumer"
3131    /// discipline extended onto the M2 supervisor-tree flat-spread
3132    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
3133    /// to match the storage field's name and the per-[`SupervisorSpec`]
3134    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
3135    /// method-name discipline verbatim; the accessor's identity maps
3136    /// onto the canonical OTP-shape supervision vocabulary the
3137    /// `:restart-window` field's docstring already carries.
3138    #[must_use]
3139    pub fn restart_window(&self) -> Option<&str> {
3140        self.restart_window.as_deref()
3141    }
3142
3143    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
3144    /// outer-composite OTP-appup-shaped per-prior-version migration-
3145    /// entry-list slice accessor every consumer of the top-level
3146    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
3147    /// slice-view keys off — returns the author-declared `:upgrade-from`
3148    /// typed `Vec<UpgradeFromEntry>` verbatim as a
3149    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
3150    /// the raw `self.upgrade_from.as_slice()` field access borrows
3151    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
3152    /// arm every `defcaixa` without an `:upgrade-from` block carries;
3153    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
3154    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
3155    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
3156    /// possibly empty — and the returned `&[UpgradeFromEntry]`
3157    /// degenerates to an empty slice on that arm without any silent
3158    /// `None` collapse).
3159    ///
3160    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
3161    /// migration block — the load-bearing container of every per-
3162    /// prior-`:versao` migration-instruction list the wasm-operator
3163    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
3164    /// `.appup` per-prior-version `LoadModule | StateChange |
3165    /// SoftPurge | Purge | Restart` instruction algebra translated
3166    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
3167    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
3168    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
3169    /// threads through a lifted per-entry accessor on the
3170    /// [`UpgradeFromEntry`] type: the
3171    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
3172    /// version scalar accessor and the
3173    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
3174    /// return per-entry instruction-list accessor (0137e5a). Every
3175    /// downstream consumer of the hot-upgrade path first passes
3176    /// through this outer accessor onto the slice and then dispatches
3177    /// per-entry through the inner accessors — the two-level dispatch
3178    /// means every per-`:upgrade-from` reader now routes through a
3179    /// typed dispatch on the substrate primitive at both altitudes.
3180    ///
3181    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
3182    /// slot was accessed inline at production sites across three
3183    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
3184    /// enumerator's `self.upgrade_from.is_empty()` presence probe
3185    /// (caixa-core/src/manifest.rs, which drives the
3186    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
3187    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
3188    /// gate reads), the [`crate::StandardLayout::verify`] per-
3189    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
3190    /// layout.rs, which fans onto the
3191    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
3192    /// cross-entry duplicate gate, the
3193    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
3194    /// SemVer-precedence cross-slot gate, the
3195    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
3196    /// `:state-change` ↔ `:on-state-change` cross-slot composition
3197    /// gate, and the per-instruction script-path existence-probe walk
3198    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
3199    /// resolve every declared migration script against the layout
3200    /// root), and the [`crate::render::servico_m2_overlay`] per-
3201    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
3202    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
3203    /// projection (caixa-core/src/render.rs, which drives the
3204    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
3205    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
3206    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
3207    /// A future extension of the outer `:upgrade-from` axis (a per-
3208    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
3209    /// resolves at admission time so a cluster-specific migration
3210    /// policy can tighten a caixa-declared step without re-authoring
3211    /// the `caixa.lisp`, promotion of the plain
3212    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
3213    /// partition once runtime-resolved hot-upgrade instructions land,
3214    /// per-entry priority annotation once multi-strategy fan-out
3215    /// lands) would have had to be threaded through all six open-
3216    /// coded copies in lockstep or one consumer would silently
3217    /// disagree with the peers on which upgrade slice a given Caixa
3218    /// resolves to — a six-consumer split at the enumerator, the
3219    /// three-stage validate pass, the script-path probe walk, and the
3220    /// M2 overlay emitter, far from the source `caixa.lisp` with no
3221    /// field naming the upgrade-drift root cause. Lifting the
3222    /// resolution rule to a typed method on the substrate primitive
3223    /// means every downstream consumer of the caixa's per-`Caixa`
3224    /// OTP-appup outer-slice surface reaches for exactly one typed
3225    /// dispatch — the resolver's accept-set migrates as a unit on any
3226    /// future axis addition.
3227    ///
3228    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3229    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3230    /// outer-`Caixa` `&[Composite]` composite-slice projection
3231    /// pattern the sibling `:children`
3232    /// [`crate::supervisor::ChildSpec`] / `:membros`
3233    /// [`crate::aplicacao::Membro`] / `:contratos`
3234    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3235    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3236    /// `Option<&Composite>` composite-reference family the sibling
3237    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3238    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3239    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3240    /// `Option<&Composite>` altitude, extended here to the outer-
3241    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3242    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3243    /// (0137e5a) — same "one typed dispatch on the substrate
3244    /// primitive, thin projections at each consumer" discipline
3245    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3246    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3247    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3248    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3249    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3250    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3251    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3252    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3253    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3254    /// slice" projection pattern onto the sibling M2 typed-composite-
3255    /// element axis (`UpgradeFromEntry` composite, matching the
3256    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3257    /// different altitude).
3258    ///
3259    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3260    /// because every downstream consumer of the hot-upgrade list
3261    /// treats it as a read-only sequence — the slice-view is the
3262    /// narrowest borrow that supports every present + roadmapped
3263    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3264    /// serialization through
3265    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3266    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3267    /// the typed view reaches for (the storage-side `Vec` remains
3268    /// reachable through the `pub upgrade_from` field for the
3269    /// mutation-carrying serde round-trip and per-test fixture-
3270    /// mutation paths). Named `upgrade_from()` to match the storage
3271    /// field's `snake_case` name; the kebab-case author-surface tag
3272    /// `:upgrade-from` is the same axis after tatara-lisp's
3273    /// kebab↔snake fold and the accessor's identity maps onto the
3274    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3275    /// already carries.
3276    #[must_use]
3277    pub fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3278        self.upgrade_from.as_slice()
3279    }
3280
3281    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3282    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3283    /// slice accessor every consumer of the top-level manifest's per-
3284    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3285    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3286    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3287    /// the same backing buffer the raw `self.children.as_slice()` field
3288    /// access borrows from. Empty-slice-carrying (the "no static children
3289    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3290    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3291    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3292    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3293    /// on those arms without any silent `None` collapse).
3294    ///
3295    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3296    /// static-child list — the load-bearing container of every per-
3297    /// child `{caixa, versao, restart}` triple the wasm-operator's
3298    /// hierarchical reconciler dispatches on at supervisor-tree
3299    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3300    /// static-child list translated onto pleme-io's typed
3301    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3302    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3303    /// dispatch fans on). Every per-child axis threads through a lifted
3304    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3305    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3306    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3307    /// version-requirement scalar accessor, and the
3308    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3309    /// per-child post-exit restart-decision-policy discriminant
3310    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3311    /// tree path first passes through this outer accessor onto the
3312    /// slice and then dispatches per-child through the inner accessors
3313    /// — the two-level dispatch means every per-`:children` reader now
3314    /// routes through a typed dispatch on the substrate primitive at
3315    /// both altitudes.
3316    ///
3317    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3318    /// accessed inline at three production sites across two files —
3319    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3320    /// declared-slot enumerator's `!self.children.is_empty()` presence
3321    /// probe (caixa-core/src/manifest.rs, which drives the
3322    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3323    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3324    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3325    /// per-supervisor typed-view composer's `self.children.clone()`
3326    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3327    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3328    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3329    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3330    /// `:children :caixa` self-parent refusal probe's
3331    /// `&caixa.children`-borrowed
3332    /// [`crate::supervisor::validate_no_self_supervision`] input
3333    /// (caixa-core/src/layout.rs, which pins the "no child names the
3334    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3335    /// extension of the outer `:children` axis (a per-cluster
3336    /// `:children-overrides` overlay the wasm-engine operator resolves
3337    /// at admission time so a cluster-specific child-set can tighten
3338    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3339    /// promotion of the plain `Vec<ChildSpec>` to a richer
3340    /// `{static, dynamic}` partition once Erlang/OTP's
3341    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3342    /// axis, per-child priority annotation once multi-strategy fan-out
3343    /// lands) would have had to be threaded through all three open-
3344    /// coded copies in lockstep or one consumer would silently
3345    /// disagree with the peers on which child slice a given Caixa
3346    /// resolves to — the enumerator's presence probe reading the raw
3347    /// slot while the peer view-composer's fold-in path read an
3348    /// operator-resolved slot would silently split the paired
3349    /// declared-slot enumerator and typed-view composition, and the
3350    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3351    /// refusal probe reading a third borrow would silently drift the
3352    /// cross-slot coherence gate's traversal input from the two peers,
3353    /// a three-consumer split at the enumerator, the view composer,
3354    /// and the self-parent gate far from the source `caixa.lisp` with
3355    /// no field naming the child-set-drift root cause. Lifting the
3356    /// resolution rule to a typed method on the substrate primitive
3357    /// means every downstream consumer of the caixa's per-`Caixa`
3358    /// OTP-supervisor outer-slice surface reaches for exactly one
3359    /// typed dispatch — the resolver's accept-set migrates as a unit
3360    /// on any future axis addition.
3361    ///
3362    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3363    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3364    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3365    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3366    /// at the outer altitude of the closed inner-`SupervisorSpec`
3367    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3368    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3369    /// borrow-shared" outer-accessor discipline extended onto the
3370    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3371    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3372    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3373    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3374    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3375    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3376    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3377    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3378    /// M2 typed-composite-element axis
3379    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3380    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3381    /// different altitude).
3382    ///
3383    /// Returns `&[crate::supervisor::ChildSpec]` (not
3384    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3385    /// child list treats it as a read-only sequence — the slice-view
3386    /// is the narrowest borrow that supports every present +
3387    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3388    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3389    /// input, `serde` slice-serialization) without leaking the backing
3390    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3391    /// reaches for (the storage-side `Vec` remains reachable through
3392    /// the `pub children` field for the mutation-carrying serde round-
3393    /// trip and per-test fixture-mutation paths, including the
3394    /// [`Self::supervisor_view`] fold-in path that clones the slot
3395    /// into the typed view). Named `children()` to match the storage
3396    /// field's name verbatim and the tatara-lisp author-surface term
3397    /// (`:children`) the field's own docstring already carries; the
3398    /// accessor's identity maps onto the canonical OTP supervision
3399    /// vocabulary the [`Caixa::children`] field's docstring already
3400    /// reaches for ("Static children of a supervisor").
3401    #[must_use]
3402    pub fn children(&self) -> &[crate::supervisor::ChildSpec] {
3403        self.children.as_slice()
3404    }
3405
3406    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3407    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3408    /// accessor every consumer of the top-level manifest's per-Aplicacao
3409    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3410    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3411    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3412    /// same backing buffer the raw `self.membros.as_slice()` field access
3413    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3414    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3415    /// and every partially-authored Aplicacao carries before the
3416    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3417    /// `&[Membro]` degenerates to an empty slice on those arms without any
3418    /// silent `None` collapse).
3419    ///
3420    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3421    /// per-Aplicacao member list — the load-bearing container of every
3422    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3423    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3424    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3425    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3426    /// the `:entrada :para` external-gateway destination validates
3427    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3428    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3429    /// threads through a lifted per-entry accessor on the
3430    /// [`crate::aplicacao::Membro`] type: the
3431    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3432    /// identity scalar accessor (4a32abf) and the peer
3433    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3434    /// version-requirement scalar accessor (a40b0e3). Every downstream
3435    /// consumer of the mesh-graph path first passes through this outer
3436    /// accessor onto the slice and then dispatches per-member through
3437    /// the inner accessors — the two-level dispatch means every per-
3438    /// `:membros` reader now routes through a typed dispatch on the
3439    /// substrate primitive at both altitudes.
3440    ///
3441    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3442    /// inline at three production sites across two files — the
3443    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3444    /// enumerator's `!self.membros.is_empty()` presence probe
3445    /// (caixa-core/src/manifest.rs, which drives the
3446    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3447    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3448    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3449    /// composer's `self.membros.clone()` per-member fold-in path
3450    /// (caixa-core/src/manifest.rs, which materializes the typed
3451    /// [`crate::aplicacao::AplicacaoSpec`] view every
3452    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3453    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3454    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3455    /// [`crate::aplicacao::validate_no_self_membership`] input
3456    /// (caixa-core/src/layout.rs, which pins the "no member names the
3457    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3458    /// extension of the outer `:membros` axis (a per-cluster
3459    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3460    /// admission time so a cluster-specific member-set can tighten a
3461    /// caixa-declared list without re-authoring the `caixa.lisp`,
3462    /// promotion of the plain `Vec<Membro>` to a richer
3463    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3464    /// members land as a typed axis, per-member priority annotation once
3465    /// multi-strategy fan-out lands) would have had to be threaded
3466    /// through all three open-coded copies in lockstep or one consumer
3467    /// would silently disagree with the peers on which member slice a
3468    /// given Caixa resolves to — the enumerator's presence probe reading
3469    /// the raw slot while the peer view-composer's fold-in path read an
3470    /// operator-resolved slot would silently split the paired
3471    /// declared-slot enumerator and typed-view composition, and the
3472    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3473    /// refusal probe reading a third borrow would silently drift the
3474    /// cross-slot coherence gate's traversal input from the two peers, a
3475    /// three-consumer split at the enumerator, the view composer, and
3476    /// the self-membership gate far from the source `caixa.lisp` with no
3477    /// field naming the member-set-drift root cause. Lifting the
3478    /// resolution rule to a typed method on the substrate primitive
3479    /// means every downstream consumer of the caixa's per-`Caixa`
3480    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3481    /// typed dispatch — the resolver's accept-set migrates as a unit on
3482    /// any future axis addition.
3483    ///
3484    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3485    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3486    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3487    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3488    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3489    /// altitude. Peer at the outer altitude of the closed inner-
3490    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3491    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3492    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3493    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3494    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3495    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3496    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3497    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3498    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3499    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3500    /// pattern onto the sibling M3 typed-composite-element axis
3501    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3502    /// [`crate::AplicacaoSpec::membros`] element type at a different
3503    /// altitude).
3504    ///
3505    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3506    /// because every downstream consumer of the member list treats it
3507    /// as a read-only sequence — the slice-view is the narrowest borrow
3508    /// that supports every present + roadmapped consumer (`.iter()`,
3509    /// `.len()`, `.is_empty()`, the
3510    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3511    /// input, `serde` slice-serialization) without leaking the backing
3512    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3513    /// reaches for (the storage-side `Vec` remains reachable through the
3514    /// `pub membros` field for the mutation-carrying serde round-trip
3515    /// and per-test fixture-mutation paths, including the
3516    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3517    /// the typed view). Named `membros()` to match the storage field's
3518    /// name verbatim and the tatara-lisp author-surface term
3519    /// (`:membros`) the field's own docstring already carries; the
3520    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3521    /// vocabulary the [`Caixa::membros`] field's docstring already
3522    /// reaches for ("Member Servicos that make up this Aplicacao").
3523    #[must_use]
3524    pub fn membros(&self) -> &[crate::aplicacao::Membro] {
3525        self.membros.as_slice()
3526    }
3527
3528    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3529    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3530    /// inter-Servico contract-list slice accessor every consumer of the
3531    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3532    /// slice-view keys off — returns the author-declared `:contratos`
3533    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3534    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3535    /// backing buffer the raw `self.contratos.as_slice()` field access
3536    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3537    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3538    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3539    /// single member with no inter-Servico edge carries; the returned
3540    /// `&[WitContract]` degenerates to an empty slice on those arms
3541    /// without any silent `None` collapse).
3542    ///
3543    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3544    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3545    /// container of every per-edge `{de, para, wit, endpoint | subject |
3546    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3547    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3548    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3549    /// adjacency-list seed dispatch on at mesh-artifact materialization
3550    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3551    /// `:membros` vertex set resolves against, closed by the
3552    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3553    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3554    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3555    /// per-edge axis threads through a lifted per-entry accessor on the
3556    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3557    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3558    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3559    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3560    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3561    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3562    /// and the WIT-world discriminant. Every downstream consumer of the
3563    /// mesh-graph edge path first passes through this outer accessor
3564    /// onto the slice and then dispatches per-contract through the
3565    /// inner accessors — the two-level dispatch means every
3566    /// per-`:contratos` reader now routes through a typed dispatch on
3567    /// the substrate primitive at both altitudes.
3568    ///
3569    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3570    /// accessed inline at two production sites in
3571    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3572    /// mesh-slot declared-slot enumerator's
3573    /// `!self.contratos.is_empty()` presence probe (which drives the
3574    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3575    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3576    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3577    /// typed-view composer's `self.contratos.clone()` per-contract
3578    /// fold-in path (which materializes the typed
3579    /// [`crate::aplicacao::AplicacaoSpec`] view every
3580    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3581    /// downstream `caixa-mesh` renderer dispatches on). A future
3582    /// extension of the outer `:contratos` axis (a per-cluster
3583    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3584    /// at admission time so a cluster-specific edge-set can tighten a
3585    /// caixa-declared list without re-authoring the `caixa.lisp`,
3586    /// promotion of the plain `Vec<WitContract>` to a richer
3587    /// `{static, dynamic}` partition once runtime-resolved contract
3588    /// edges land, per-edge policy annotation once the M4 per-edge
3589    /// policy overlay axis lands) would have had to be threaded through
3590    /// both open-coded copies in lockstep or one consumer would
3591    /// silently disagree with the peer on which edge slice a given
3592    /// Caixa resolves to — the enumerator's presence probe reading the
3593    /// raw slot while the peer view-composer's fold-in path read an
3594    /// operator-resolved slot would silently split the paired
3595    /// declared-slot enumerator and typed-view composition, a
3596    /// two-consumer split at the enumerator and the view composer far
3597    /// from the source `caixa.lisp` with no field naming the edge-set-
3598    /// drift root cause. Lifting the resolution rule to a typed method
3599    /// on the substrate primitive means every downstream consumer of
3600    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3601    /// reaches for exactly one typed dispatch — the resolver's
3602    /// accept-set migrates as a unit on any future axis addition.
3603    ///
3604    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3605    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3606    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3607    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3608    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3609    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3610    /// mesh-slot arm of the composite-slice sub-family the sibling
3611    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3612    /// Peer at the outer altitude of the closed inner-
3613    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3614    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3615    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3616    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3617    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3618    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3619    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3620    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3621    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3622    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3623    /// pattern onto the sibling M3 typed-composite-element axis
3624    /// ([`crate::aplicacao::WitContract`] composite, matching the
3625    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3626    /// different altitude).
3627    ///
3628    /// Returns `&[crate::aplicacao::WitContract]` (not
3629    /// `&Vec<WitContract>`) because every downstream consumer of the
3630    /// contract list treats it as a read-only sequence — the slice-view
3631    /// is the narrowest borrow that supports every present + roadmapped
3632    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3633    /// discriminant dispatch, `serde` slice-serialization) without
3634    /// leaking the backing `Vec`'s grow/push/reserve surface no
3635    /// consumer of the typed view reaches for (the storage-side `Vec`
3636    /// remains reachable through the `pub contratos` field for the
3637    /// mutation-carrying serde round-trip and per-test fixture-mutation
3638    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3639    /// clones the slot into the typed view). Named `contratos()` to
3640    /// match the storage field's name verbatim and the tatara-lisp
3641    /// author-surface term (`:contratos`) the field's own docstring
3642    /// already carries; the accessor's identity maps onto the canonical
3643    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3644    /// docstring already reaches for ("WIT-typed inter-Servico
3645    /// contracts").
3646    #[must_use]
3647    pub fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3648        self.contratos.as_slice()
3649    }
3650
3651    /// Compose the Aplicacao-related flat slots into a single typed
3652    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3653    /// downstream renderer consumption. Returns `None` when the
3654    /// caixa isn't a `:kind Aplicacao`.
3655    #[must_use]
3656    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3657        if !self.kind().is_aplicacao() {
3658            return None;
3659        }
3660        Some(crate::aplicacao::AplicacaoSpec {
3661            membros: self.membros().to_vec(),
3662            contratos: self.contratos().to_vec(),
3663            politicas: self.politicas().cloned().unwrap_or_default(),
3664            placement: self.placement().cloned().unwrap_or_default(),
3665            entrada: self.entrada().cloned(),
3666        })
3667    }
3668
3669    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3670    /// *declares* a value on, in canonical declaration order
3671    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3672    /// `:entrada`). A slot counts as declared when its backing field
3673    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3674    ///
3675    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3676    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3677    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3678    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3679    /// caixa-flux / caixa-helm renderers only emit them for an
3680    /// Aplicacao. On any *other* kind a declared mesh slot is the
3681    /// manifest field's documented "ignored otherwise" (see the
3682    /// `:membros` … `:entrada` field docs): it silently passes
3683    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3684    /// rendered — far from the source caixa.lisp.
3685    /// [`crate::StandardLayout::verify`] consults this to reject that
3686    /// silent-drop at caixa-build time
3687    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3688    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3689    /// a slot foreign to the kind is a build error, not a silent drop.
3690    ///
3691    /// Lifted as a typed method (rather than an inline disjunction at
3692    /// the verify call site) so the mesh-slot set lives in one place —
3693    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3694    /// overlay, distributed-app takeover config) is one push here, and
3695    /// every consumer reaching for "which mesh slots are set" (the
3696    /// verify gate, a future `feira lint` kind-coherence advisory)
3697    /// inherits the canonical order without rolling its own.
3698    ///
3699    /// Each per-arm kebab-case label is routed through the peer
3700    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3701    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3702    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3703    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3704    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3705    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3706    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3707    /// kebab-case label + renderer-side artifact key) route through one
3708    /// canonical declaration per arm — same discipline the peer
3709    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3710    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3711    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3712    /// axis, extended here to close the M3 mesh-slot author-facing-label
3713    /// axis so both altitudes of the typed-slot algebra
3714    /// (per-Servico M2 + per-Aplicacao M3) share the same
3715    /// "one canonical byte-string per arm, next to the axis" discipline.
3716    #[must_use]
3717    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3718        let mut slots = Vec::new();
3719        if !self.membros().is_empty() {
3720            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3721        }
3722        if !self.contratos().is_empty() {
3723            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3724        }
3725        if self.politicas().is_some() {
3726            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3727        }
3728        if self.placement().is_some() {
3729            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3730        }
3731        if self.entrada().is_some() {
3732            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3733        }
3734        slots
3735    }
3736
3737    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3738    /// caixa *declares* a value on, in canonical declaration order
3739    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3740    /// `:children`). A slot counts as declared when its backing field
3741    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3742    ///
3743    /// The supervisor-tree slots compose the typed OTP supervisor of a
3744    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3745    /// `:children` field docs above). [`Self::supervisor_view`] only
3746    /// folds them into a validatable [`SupervisorSpec`] when the kind
3747    /// matches (returns `None` otherwise), and the wasm-operator's
3748    /// hierarchical reconciler only consumes them for a Supervisor. On
3749    /// any *other* kind a declared supervisor slot is the manifest
3750    /// field's documented "ignored otherwise" (see the `:estrategia` …
3751    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3752    /// and then vanishes — never validated, never reconciled — far from
3753    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3754    /// this to reject that silent-drop at caixa-build time
3755    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3756    /// exact mirror of the [`Self::declared_mesh_slots`] /
3757    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3758    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3759    /// error, not a silent drop.
3760    #[must_use]
3761    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3762        let mut slots = Vec::new();
3763        if self.estrategia().is_some() {
3764            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3765        }
3766        if self.max_restarts().is_some() {
3767            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3768        }
3769        if self.restart_window().is_some() {
3770            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3771        }
3772        if !self.children().is_empty() {
3773            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3774        }
3775        slots
3776    }
3777
3778    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3779    /// caixa *declares* a value on, in canonical declaration order
3780    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3781    /// declared when its backing field carries a value — a `Some(...)`,
3782    /// or a non-empty `Vec`.
3783    ///
3784    /// The M2 slots configure the runtime of a long-running wasm
3785    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3786    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3787    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3788    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3789    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3790    /// emit these slots for a Servico; on any *other* kind a declared M2
3791    /// slot is the manifest field's documented "ignored otherwise": its
3792    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3793    /// but the value is never rendered into a chart / programs.yaml entry
3794    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
3795    /// vanishes, far from the source caixa.lisp.
3796    /// [`crate::StandardLayout::verify`] consults this to reject that
3797    /// silent-drop at caixa-build time
3798    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
3799    /// mirror of the [`Self::declared_mesh_slots`] /
3800    /// [`Self::declared_supervisor_slots`] gates on the peer
3801    /// kind-exclusive slot sets: a slot foreign to the kind is a build
3802    /// error, not a silent drop.
3803    ///
3804    /// Each per-arm kebab-case label is routed through the peer
3805    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
3806    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
3807    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
3808    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
3809    /// both halves of the M2 top-level slot's dual axis (author-facing
3810    /// kebab-case label + renderer-side camelCase overlay-container wire
3811    /// key) route through one canonical declaration per arm — same
3812    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
3813    /// author-label consts (889dc18) establish on the sibling
3814    /// per-callback axis inside the `:behavior` overlay block.
3815    #[must_use]
3816    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
3817        let mut slots = Vec::new();
3818        if self.limits().is_some() {
3819            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
3820        }
3821        if self.behavior().is_some() {
3822            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
3823        }
3824        if !self.upgrade_from().is_empty() {
3825            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
3826        }
3827        slots
3828    }
3829
3830    /// The kebab-case `:slot` tags of every code-surface slot this caixa
3831    /// declares a value on that its [`CaixaKind`] doesn't natively own,
3832    /// in canonical declaration order (`:exe` → `:servicos`). A
3833    /// code-surface slot is owned by exactly one kind: `:exe` by
3834    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
3835    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
3836    /// `ComputeUnit` daemon surface).
3837    ///
3838    /// Each is silently ignored when declared on the wrong kind: the
3839    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
3840    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
3841    /// code-running kind a declared `:exe` / `:servicos` is the manifest
3842    /// field's documented "ignored otherwise" — its path is checked for
3843    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
3844    /// (which run after [`Caixa::from_lisp`]), but the value is never
3845    /// rendered into a build target or programs.yaml entry. It silently
3846    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
3847    /// caixa.lisp, with no field naming which slot is foreign.
3848    ///
3849    /// [`crate::StandardLayout::verify`] consults this to reject that
3850    /// silent-drop at caixa-build time
3851    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
3852    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
3853    /// gates ([`Self::declared_servico_slots`] /
3854    /// [`Self::declared_supervisor_slots`] /
3855    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
3856    /// axis to be closed on the typed surface. The Supervisor /
3857    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
3858    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
3859    /// diagnostics — they fire ahead of this gate on the same `verify`
3860    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
3861    /// and this method is moot. For Biblioteca / Binario / Servico, this
3862    /// gate fires when a code-running kind declares another code-running
3863    /// kind's exclusive code surface.
3864    ///
3865    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
3866    /// may legitimately ship a `lib/` helper that the underlying
3867    /// substrate (the nix flake for Binario, the wasm component build
3868    /// for Servico) bundles into its build, so the slot's
3869    /// declared-on-wrong-kind cardinality isn't a structural error on
3870    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
3871    /// is the native case (the slot's owning kind). Supervisor /
3872    /// Aplicacao declaring `:bibliotecas` is gated upstream by
3873    /// [`crate::LayoutError::SupervisorOwnsCode`] /
3874    /// [`crate::LayoutError::AplicacaoOwnsCode`].
3875    ///
3876    /// Lifted as a typed method (rather than an inline disjunction at
3877    /// the verify call site) so the foreign-code-slot set lives in one
3878    /// place — a future kind that gains its own code-surface slot is
3879    /// one push here, and every consumer reaching for "which code
3880    /// surfaces are foreign to this kind" (the verify gate, a future
3881    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
3882    /// per-caixa build-target classifier) inherits the canonical order
3883    /// without rolling its own.
3884    #[must_use]
3885    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
3886        let mut slots = Vec::new();
3887        if !self.exe().is_empty() && !self.kind().requires_exe() {
3888            slots.push(":exe");
3889        }
3890        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
3891            slots.push(":servicos");
3892        }
3893        slots
3894    }
3895
3896    /// Validate every entry of `:deps` and `:deps-dev` through
3897    /// [`Dep::validate`] — closing the parity loop with the per-axis
3898    /// `:versao` gates already wired into the typed-graph
3899    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
3900    /// 9888b13) and typed supervisor tree
3901    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
3902    ///
3903    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
3904    /// were the only `:versao` axes still untyped past
3905    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
3906    /// as a String without parsing it, so a malformed-but-non-empty
3907    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
3908    /// silently passed parse and the `semver::Error` surfaced at
3909    /// lacre-resolve time, far from the source caixa.lisp, with no
3910    /// field naming which `:deps` entry carried the typo. Lifting the
3911    /// gate here makes the four `:versao` typed surfaces (`:deps`,
3912    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
3913    /// every requirement string past `validate_deps` is round-trippable
3914    /// through [`crate::parse_requirement`] without re-checking at the
3915    /// resolver layer.
3916    ///
3917    /// Both lists run through the same per-entry validator so a typo
3918    /// in `:deps-dev` surfaces with the same diagnostic as one in
3919    /// `:deps` — neither axis is a second-class citizen of the typed
3920    /// surface.
3921    ///
3922    /// Within each list, [`DepError::DuplicateNome`] closes the
3923    /// set-not-multiset discipline on the `:nome` axis: two entries
3924    /// naming the same caixa carry two `:versao` / `:fonte` / feature
3925    /// triples that the caixa-resolver's lacre pipeline collapses to one
3926    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
3927    /// silently overwrites the first at `concrete_versao`-resolve time
3928    /// (the same "second wins / one silently overwrites the other"
3929    /// shape the peer typed-graph duplicate gates already close on every
3930    /// other Vec-shaped authoring surface that keys by name). The
3931    /// duplicate check fires per-list and runs *after* each per-entry
3932    /// [`Dep::validate`] call so a malformed-and-duplicated entry
3933    /// surfaces its narrower per-entry diagnostic
3934    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
3935    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
3936    /// diagnostic — the canonical "per-entry shape before cross-entry
3937    /// uniqueness" precedence the peer `:children :caixa`
3938    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
3939    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
3940    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
3941    /// ([`crate::AplicacaoSpec::validate_placement`]),
3942    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
3943    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
3944    /// and the within-`:upgrade-from`-entry per-instruction-class
3945    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
3946    /// [`crate::UpgradeError::DuplicateStateChange`],
3947    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
3948    ///
3949    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
3950    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
3951    /// same name in both tables (the dev table's pin overrides the
3952    /// runtime table's pin in test/dev contexts), and caixa's surface
3953    /// mirrors that convention until a deliberate choice retires the
3954    /// override pattern. Only within-list duplicates are structurally
3955    /// incoherent — those are what this gate closes.
3956    pub fn validate_deps(&self) -> Result<(), DepError> {
3957        for &list in crate::dep::DepList::ALL {
3958            let mut seen = std::collections::HashSet::new();
3959            for dep in self.deps_of(list) {
3960                dep.validate()?;
3961                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
3962                    DepError::DuplicateNome {
3963                        nome: dep.nome().to_string(),
3964                        list: list.as_str(),
3965                    }
3966                })?;
3967            }
3968        }
3969        Ok(())
3970    }
3971
3972    /// Reject `:nome` values the K8s apiserver would refuse at admission
3973    /// time. The top-level Caixa identity flows directly into every
3974    /// substrate-side artifact's `metadata.name` axis: the
3975    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
3976    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
3977    /// aggregator keys ComputeUnit derivation off
3978    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
3979    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
3980    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
3981    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
3982    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
3983    /// ([`caixa-mesh::lib::cilium_network_policies`],
3984    /// [`caixa-mesh::lib::gateway_routes`]), and the default
3985    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
3986    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
3987    /// schema enforces the DNS-1123 label rule on admission; a
3988    /// structurally invalid `:nome` (`"MyApp"` — the canonical
3989    /// "I copied the display name verbatim" footgun, `"my_app"` — the
3990    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
3991    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
3992    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
3993    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
3994    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
3995    /// failure surfaced at `kubectl apply` time as a `metadata.name:
3996    /// Invalid value` rejection on whichever derived artifact admitted
3997    /// first, far from the source `caixa.lisp` and without any field
3998    /// naming the offending `:nome`.
3999    ///
4000    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
4001    /// substrate-side predicate the per-axis name gates already share:
4002    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
4003    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
4004    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
4005    /// diagnostic is self-locating (the offending `:nome` is named
4006    /// verbatim) and the author can grep their `caixa.lisp` for
4007    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
4008    /// every per-axis sibling gate already exposes
4009    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
4010    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
4011    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
4012    ///
4013    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
4014    /// derive macro stores the raw String) is gated by the narrower
4015    /// [`ManifestError::NomeEmpty`] arm before the predicate is
4016    /// consulted, mirroring the empty-first cascade every per-axis
4017    /// name gate already uses (e.g. `MembroCaixaEmpty` before
4018    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
4019    pub fn validate_nome(&self) -> Result<(), ManifestError> {
4020        // Routes through the shared
4021        // [`crate::render::require_valid_dns_1123_label`] gate the peer
4022        // name axes each land on so drift between the eight axes'
4023        // accepted DNS-1123-label sets is structurally impossible.
4024        let nome = self.nome();
4025        crate::render::require_valid_dns_1123_label(
4026            nome,
4027            || ManifestError::NomeEmpty,
4028            |reason| ManifestError::NomeInvalid {
4029                nome: nome.to_string(),
4030                reason,
4031            },
4032        )
4033    }
4034
4035    /// Reject `:nome` values whose joint length with the canonical
4036    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4037    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4038    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4039    /// substrate carries materializes the caixa's `:nome` through the
4040    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4041    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4042    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4043    /// `ChartDir.name` + `Chart.yaml::name`
4044    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4045    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4046    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4047    /// `oci://<registry>/lareira-<nome>` chart ref
4048    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4049    /// admission rule strict-parses against DNS-1123-label, the Helm
4050    /// operator's tracking-secret name is derived from `release_name`
4051    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4052    /// K8s object `metadata.name` axes embed the chart name as a
4053    /// prefix — every one fails admission on a > 63-byte chart name.
4054    ///
4055    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4056    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4057    /// `:nome` of 56–63 bytes silently passed validate (the inner
4058    /// DNS-1123 check accepts the bare `:nome`) but produced a
4059    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4060    /// rejected at admission — far from the source `caixa.lisp`, with
4061    /// no field naming the overflow root cause. The
4062    /// [`lareira_chart_name`] helper's own doc comment
4063    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4064    /// "the M4 admission webhook will pin the joint-length invariant
4065    /// when it lands". This gate lands the invariant at the
4066    /// manifest-validate layer rather than waiting for the apiserver
4067    /// — the same fail-at-the-source posture every peer per-axis
4068    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4069    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4070    /// `:edicao`, etc.) takes.
4071    ///
4072    /// Thin wrapper around
4073    /// [`crate::render::is_lareira_chart_name_shape`] (the
4074    /// substrate-side predicate that composes [`lareira_chart_name`] +
4075    /// [`is_dns_1123_label`] via the lifted
4076    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4077    /// shared parser-shaped reason into the
4078    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4079    /// diagnostic is self-locating (the offending `:nome` is named
4080    /// verbatim alongside the rendered chart name and the budget) and
4081    /// the author can shorten in one edit. The gate runs across every
4082    /// `:kind` — `:nome` is the substrate-wide identity axis any
4083    /// future renderer the substrate adds can derive a
4084    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4085    /// the drift footgun where a future kind grows a chart-emitting
4086    /// render path while the validate cascade doesn't catch it.
4087    ///
4088    /// Runs *after* [`Self::validate_nome`] so the narrower
4089    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4090    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4091    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4092    /// specific shape error rather than the chart-name-budget error,
4093    /// preserving the legitimate "well-shaped `:nome` that happens to
4094    /// overflow the joint cap" arm for this gate.
4095    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4096        let nome = self.nome();
4097        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
4098            ManifestError::NomeChartNameBudgetExceeded {
4099                nome: nome.to_string(),
4100                reason,
4101            }
4102        })
4103    }
4104
4105    /// Reject `:versao` values that don't parse as [`semver::Version`].
4106    /// The top-level Caixa version flows directly into every
4107    /// substrate-side artifact that carries a "this is which version of
4108    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4109    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4110    /// SemVer-2-strict at `helm template` / `helm install` time per
4111    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4112    /// `feira publish` Zig-style `v<versao>` git tag
4113    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4114    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4115    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4116    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4117    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4118    /// `skopeo push`, the lacre closure's pinned versions
4119    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4120    /// `:upgrade-from :from` references peers in this exact `versao`
4121    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4122    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4123    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4124    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4125    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4126    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4127    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4128    /// into the version field a peer `:deps :versao` accepts;
4129    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4130    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4131    /// derive macro stores the raw String) and the failure surfaced at
4132    /// the *first* downstream consumer that strict-parses it: at
4133    /// `helm install` time as a chart-version rejection, at
4134    /// `feira publish` time as a malformed git tag, at lacre-resolve
4135    /// time as a `semver::Error` not naming the offending caixa, at
4136    /// `feira upgrade --to <versao>` time as an unresolvable
4137    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4138    /// and without any field naming the offending `:versao`.
4139    ///
4140    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4141    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4142    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4143    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4144    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4145    /// variant, carrying the offending `:versao` verbatim + a
4146    /// parser-shaped reason naming the specific violation, so the
4147    /// diagnostic is self-locating (the author can grep their
4148    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4149    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4150    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4151    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4152    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4153    /// now structurally equivalent (every value past validate is
4154    /// round-trippable through [`semver::Version::parse`] without
4155    /// re-checking at the renderer, resolver, or operator hot-upgrade
4156    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4157    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4158    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4159    ///
4160    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4161    /// the derive macro stores the raw String) is gated by the
4162    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4163    /// consulted, mirroring the empty-first cascade every per-axis
4164    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4165    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4166    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4167    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4168        let versao = self.versao();
4169        if versao.is_empty() {
4170            return Err(ManifestError::VersaoEmpty);
4171        }
4172        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
4173            versao: versao.to_string(),
4174            reason: e.to_string(),
4175        })?;
4176        Ok(())
4177    }
4178
4179    /// Reject `:restart-window` values the shared
4180    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4181    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4182    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4183    /// `Option<Duration>` routed through the shared codec via `with =
4184    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4185    /// view-construction path ([`Self::supervisor_view`]) folds the
4186    /// raw string through the same shared codec and soft-swallows the
4187    /// parse error as `None` to keep the view best-effort. Without
4188    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4189    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4190    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4191    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4192    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4193    /// edge case) silently produced a `SupervisorSpec` with
4194    /// `restart_window: None`, indistinguishable from the canonical
4195    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4196    /// `MaxIntensity / Period` invariant turns into a never-reset
4197    /// supervisor far from the source `caixa.lisp`, with no field
4198    /// naming the offending `:restart-window`. Lifting the gate to a
4199    /// Caixa-level validator mirrors the trajectory of the peer
4200    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4201    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4202    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4203    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4204    ///
4205    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4206    /// (the shared codec backing `:supervisor :restart-window` as
4207    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4208    /// `:politicas :circuit-breaker :window` — all three covered by
4209    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4210    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4211    /// variant, carrying the offending raw string + a parser-shaped
4212    /// reason naming the canonical authoring form, so the diagnostic
4213    /// is self-locating (the author can grep their `caixa.lisp` for
4214    /// `:restart-window "<value>"` and fix it in one edit) and
4215    /// uniform with every other manifest-level validate diagnostic.
4216    /// With this gate the four `:restart-window`-shaped surfaces (the
4217    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4218    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4219    /// now structurally equivalent — every value past the codec is in
4220    /// one accepted set, by construction.
4221    ///
4222    /// `None` (the canonical "omit the slot to express no reset"
4223    /// shape) is accepted trivially — the gate is a no-op when the
4224    /// author didn't author a window. The empty string is rejected by
4225    /// the shared codec (its digit-only gate refuses an empty
4226    /// magnitude), surfacing the same `RestartWindowMalformed`
4227    /// diagnostic as every other rejected non-canonical shape.
4228    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4229        let Some(s) = self.restart_window() else {
4230            return Ok(());
4231        };
4232        crate::supervisor::duration_codec::parse(s)
4233            .map(|_| ())
4234            .map_err(|reason| ManifestError::RestartWindowMalformed {
4235                restart_window: s.to_string(),
4236                reason,
4237            })
4238    }
4239
4240    /// Reject per-entry values on the three Caixa-level code-surface
4241    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
4242    /// layout checker's `root.join(p)` sandbox would silently subvert.
4243    /// Same three structural footguns the peer
4244    /// [`BehaviorSpec::validate`] (b0c8389) and
4245    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
4246    /// (26da2c7) already close on the M2 `:behavior :on-*` and
4247    /// `:upgrade-from :state-change :script` axes, here lifted onto
4248    /// the three top-level code-path axes through the shared
4249    /// [`is_sandboxed_relative_path`] predicate:
4250    ///
4251    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
4252    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
4253    ///     [`Path::join`] as the base itself — `root.join("")` ==
4254    ///     `root`, so the existence check (`self.exists(&root)`)
4255    ///     trivially passes (the project root exists), and the layout
4256    ///     silently treats the project root as a biblioteca / exe /
4257    ///     servico entry. The `:bibliotecas` loop then hands the root
4258    ///     to `tatara_lisp::read` at `feira build` time as if the root
4259    ///     directory itself were a Lisp source file — a parse error
4260    ///     far from the source `caixa.lisp` with no field naming the
4261    ///     offending entry.
4262    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
4263    ///     [`Path::join`] *replaces* the base when the right-hand side
4264    ///     is absolute, so `root.join("/etc/passwd")` resolves to
4265    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
4266    ///     The existence check then silently consults whatever the
4267    ///     escaped path resolves to — for `:bibliotecas`, the layout
4268    ///     has no `starts_with`-fence (only `:exe` is fenced under
4269    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
4270    ///     `:bibliotecas` entry that happens to resolve on disk
4271    ///     silently passes. For `:exe` / `:servicos` the fence catches
4272    ///     the absolute case downstream as `ExeOutsideDir` /
4273    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
4274    ///     doesn't exist), but with a downstream-shaped diagnostic
4275    ///     that names the resolved escape path rather than the
4276    ///     authoring footgun at the source.
4277    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
4278    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
4279    ///     [`std::path::Component::ParentDir`] anywhere round-trips
4280    ///     through [`Path::join`] as a traversal above the caixa root.
4281    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
4282    ///     *component-aware* (not canonical-path-aware), so
4283    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
4284    ///     is **true** even though the canonical resolution
4285    ///     `{parent of root}/escape.lisp` lives outside the caixa root
4286    ///     — the fence silently lets the parent-escape through, and
4287    ///     the existence check passes if that escape-target happens
4288    ///     to exist. Caught regardless of where the `..` sits
4289    ///     (leading, mid-path, trailing) so the gate matches the peer
4290    ///     predicate's full coverage.
4291    ///
4292    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
4293    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
4294    /// same per-slot diagnostic shape every peer per-axis path-gate
4295    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
4296    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
4297    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
4298    /// order [`Caixa::declared_foreign_code_slots`] uses for its
4299    /// canonical foreign-code-slot diagnostic, so a manifest with
4300    /// multiple malformed slots surfaces the lexicographically-earliest
4301    /// slot's diagnostic deterministically.
4302    ///
4303    /// Lifted to the typed surface as a Caixa-level validator (peer
4304    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
4305    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
4306    /// and wired into [`crate::StandardLayout::verify`] before the
4307    /// existence-check loops so the diagnostic names the offending
4308    /// slot at the source caixa.lisp rather than reporting a
4309    /// downstream `MissingEntry` / `ExeOutsideDir` /
4310    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
4311    /// The fourth typed code-path surface — every author-supplied
4312    /// path on the manifest — is now structurally accept-shaped
4313    /// past validate, peer with `:behavior :on-*` and
4314    /// `:upgrade-from :state-change :script`.
4315    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
4316        /// Per-slot file-type contract for the three Caixa-level
4317        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
4318        /// Each variant names the predicate the per-entry file-type
4319        /// gate consults; [`Self::None`] opts the slot out of any
4320        /// file-type contract. Lifted as a typed local enum so the
4321        /// per-slot dispatch is exhaustive at the `match` — adding a
4322        /// future axis to the typed-substrate `:` slot set (the
4323        /// future `:assets` resource axis the M5 roadmap names, the
4324        /// future `:nix-flake` derivation axis the caixa-flake
4325        /// emitter consults) lands as one variant + one `match` arm,
4326        /// not a coordinated rewrite of every per-slot bool flag.
4327        ///
4328        /// Peer of the typed-substrate per-slot variant disciplines
4329        /// already established on this surface
4330        /// ([`crate::supervisor::RestartStrategy`] +
4331        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
4332        /// supervision-tree axis,
4333        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
4334        /// placement axis, [`crate::aplicacao::WitTarget`] on the
4335        /// `:contratos` payload-target axis): the typed `enum` is
4336        /// the substrate's single source of truth for the per-axis
4337        /// dispatch, and every consumer (the per-arm body here, the
4338        /// future feira-lint per-slot diagnostic renderer, the M4
4339        /// per-axis admission webhook) reaches for the same typed
4340        /// surface rather than re-deriving the partition from inline
4341        /// flag combinations.
4342        enum CodePathFileType {
4343            /// `:exe` — nix-build derivation output, no terminating-
4344            /// extension contract (the canonical `"exe/<name>"`
4345            /// fixtures the layout's `ExeOutsideDir` error message
4346            /// documents carry no extension by convention).
4347            None,
4348            /// `:bibliotecas` — tatara-lisp source files the
4349            /// `feira build` loop reads through `tatara_lisp::read`
4350            /// at parse time. Routes to [`is_lisp_extension`].
4351            LispSource,
4352            /// `:servicos` — ComputeUnit-CR YAML files the
4353            /// caixa-helm / caixa-flux renderers consume through
4354            /// `serde_yaml::from_str`. Routes to
4355            /// [`is_computeunit_yaml_extension`].
4356            ComputeUnitYaml,
4357        }
4358
4359        // The per-slot [`CodePathFileType`] selects which axes carry the
4360        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
4361        // source axis (the `feira build` loop at
4362        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
4363        // `tatara_lisp::read` at parse time) — the lifted
4364        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
4365        // `:exe` is the nix-built executable surface (per the canonical
4366        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
4367        // error message documents and every in-tree
4368        // `caixa_with_code_paths` positive control uses) — its file-type
4369        // contract is "nix-build derivation output", not a typed source
4370        // file, so [`CodePathFileType::None`] opts the slot out of any
4371        // file-type gate. `:servicos` is the `.computeunit.yaml`
4372        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
4373        // renderers consume each entry through `serde_yaml::from_str` as
4374        // a typed `ComputeUnit` CR) — the lifted
4375        // [`is_computeunit_yaml_extension`] predicate gates the compound
4376        // `.computeunit.yaml` suffix. All three axes are surfaced through
4377        // the same iteration so the sandbox-shape + duplicate gates
4378        // apply uniformly; the typed file-type dispatch fires per-slot
4379        // exactly where the downstream consumer's accepted set demands
4380        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
4381        // compounding lift on the peer 64772a9 `:bibliotecas`
4382        // `.lisp`-gate trajectory — the second of the three code-path
4383        // axes to land on a typed compound-suffix gate, with the same
4384        // self-locating per-slot diagnostic shape every peer per-axis
4385        // file-type lift uses (`*NonLispExtension { slot, path }` /
4386        // `*NonComputeUnitYamlExtension { slot, path }`).
4387        for (slot, list, file_type) in [
4388            (
4389                ":bibliotecas",
4390                &self.bibliotecas,
4391                CodePathFileType::LispSource,
4392            ),
4393            (":exe", &self.exe, CodePathFileType::None),
4394            (
4395                ":servicos",
4396                &self.servicos,
4397                CodePathFileType::ComputeUnitYaml,
4398            ),
4399        ] {
4400            // Per-slot set-not-multiset gate on the typed code-path axis.
4401            // Every peer Vec-shaped author-supplied list past validate is
4402            // a set, not a multiset: `:membros :caixa`
4403            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
4404            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
4405            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
4406            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
4407            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
4408            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
4409            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
4410            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
4411            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
4412            // the three code-path lists are the last Vec-shaped author-
4413            // supplied slots on the typed Caixa surface still admitting a
4414            // duplicate entry silently. Scope is per-list (`:bibliotecas`
4415            // duplicates are flagged within `:bibliotecas`, not across
4416            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
4417            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
4418            // legitimate dev-vs-runtime shape on the dep axis, fenced
4419            // separately by [`crate::dep::validate_no_self_dep`]). On the
4420            // code-path axis a cross-slot collision is structurally
4421            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
4422            // fence — `:exe` and `:servicos` entries are confined to their
4423            // own directory trees, so the only way a string could appear
4424            // on two code-path lists is the (rare, structurally invalid)
4425            // case where `:bibliotecas` carries an `"exe/<x>"` or
4426            // `"servicos/<x>.yaml"`-shaped path.
4427            //
4428            // Without the gate three authoring footguns silently passed:
4429            //
4430            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
4431            //     canonical copy-paste-the-wrong-file footgun. `feira
4432            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
4433            //     list and re-parses the same file twice, wasting work
4434            //     and silently masking the author's intent to declare a
4435            //     *second* biblioteca.
4436            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
4437            //     Binario surface. The future `caixa-flake` `nix flake`
4438            //     emitter that materializes each `:exe` entry as a flake
4439            //     `packages.<exe-name>` derivation would collide on the
4440            //     duplicate package name and surface a flake-eval error
4441            //     far from the source `caixa.lisp`.
4442            //   - `:servicos ("servicos/x.computeunit.yaml"
4443            //     "servicos/x.computeunit.yaml")` — the same footgun on
4444            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
4445            //     renderers already refuse `:servicos.len() != 1` with
4446            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
4447            //     that diagnostic surfaces "too many servicos" without
4448            //     naming "duplicate entry" — the typed self-locating
4449            //     "which entry is the duplicate" framing only lands at
4450            //     this gate.
4451            //
4452            // Same `seen.insert(entry.as_str())` shape every peer per-list
4453            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
4454            // 86c769b, `:deps` 359fba5) and the same "structural shape
4455            // checks fire before the duplicate check on the same entry"
4456            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
4457            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
4458            // empty entry first, not the duplicate on the later pair).
4459            let mut seen = std::collections::HashSet::new();
4460            for entry in list {
4461                let path = Path::new(entry);
4462                match is_sandboxed_relative_path(path) {
4463                    Ok(()) => {}
4464                    Err(PathShapeViolation::Empty) => {
4465                        return Err(ManifestError::CodePathEmpty { slot });
4466                    }
4467                    Err(PathShapeViolation::Absolute) => {
4468                        return Err(ManifestError::CodePathAbsolute {
4469                            slot,
4470                            path: path.to_path_buf(),
4471                        });
4472                    }
4473                    Err(PathShapeViolation::ParentEscape) => {
4474                        return Err(ManifestError::CodePathParentEscape {
4475                            slot,
4476                            path: path.to_path_buf(),
4477                        });
4478                    }
4479                }
4480                // The per-slot file-type gate dispatched through the
4481                // typed [`CodePathFileType`] selector above. Each variant
4482                // routes to the lifted predicate the downstream consumer
4483                // demands:
4484                //
4485                //   - [`LispSource`] → [`is_lisp_extension`] for
4486                //     `:bibliotecas` (the `feira build` loop's
4487                //     `tatara_lisp::read` consumer);
4488                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
4489                //     for `:servicos` (the caixa-helm / caixa-flux
4490                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
4491                //     accepted set);
4492                //   - [`None`] for `:exe` — the nix-build derivation-
4493                //     output axis has no terminating-extension contract.
4494                //
4495                // Fires after the sandbox-shape arms so a path that is
4496                // *both* sandbox-escaping and wrong-extension surfaces
4497                // the more fundamental sandbox-shape diagnostic first
4498                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
4499                // `ParentEscape` → `NonLispExtension` arm-ordering on
4500                // `:behavior :on-*` c97815a, and `EmptyScript` →
4501                // `AbsoluteScript` → `ParentEscapeScript` →
4502                // `NonLispExtensionScript` on
4503                // `:upgrade-from :state-change :script` 33cc830), and
4504                // before the duplicate gate so the narrower per-entry
4505                // file-type shape dominates the cross-entry uniqueness
4506                // diagnostic (a
4507                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
4508                // `:servicos` surfaces
4509                // `CodePathNonComputeUnitYamlExtension` on the first
4510                // entry rather than `CodePathDuplicate` on the pair —
4511                // peer with the 64772a9 `:bibliotecas`
4512                // `("lib/x.txt" "lib/x.txt")` ordering).
4513                match file_type {
4514                    CodePathFileType::None => {}
4515                    CodePathFileType::LispSource => {
4516                        if !is_lisp_extension(path) {
4517                            return Err(ManifestError::CodePathNonLispExtension {
4518                                slot,
4519                                path: path.to_path_buf(),
4520                            });
4521                        }
4522                    }
4523                    CodePathFileType::ComputeUnitYaml => {
4524                        if !is_computeunit_yaml_extension(path) {
4525                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
4526                                slot,
4527                                path: path.to_path_buf(),
4528                            });
4529                        }
4530                    }
4531                }
4532                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
4533                    ManifestError::CodePathDuplicate {
4534                        slot,
4535                        path: path.to_path_buf(),
4536                    }
4537                })?;
4538            }
4539        }
4540        Ok(())
4541    }
4542
4543    /// Reject `:etiquetas` lists with an empty entry or with two entries
4544    /// agreeing on the same string. `:etiquetas` is the universal
4545    /// registry-search-tag axis on [`Caixa`] (every kind carries the
4546    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4547    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
4548    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
4549    /// a [`std::collections::BTreeSet`] alongside the four substrate-
4550    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
4551    /// Two authoring footguns silently passed validate without this gate:
4552    ///
4553    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
4554    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
4555    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
4556    ///     `chart.metadata.keywords` admits the value without a strict
4557    ///     parser-side gate, but the empty keyword has no operational
4558    ///     meaning — it indexes nothing in the future caixa-registry
4559    ///     search axis and clutters the rendered chart with a no-op tag.
4560    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
4561    ///     copy-paste-the-wrong-tag footgun) silently passed validate
4562    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
4563    ///     at chart render — a "second wins / one silently disappears"
4564    ///     shape divergent from every peer typed-graph set gate
4565    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
4566    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
4567    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4568    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4569    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4570    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
4571    ///     on `:upgrade-from`, the per-instruction-class singularity
4572    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
4573    ///     [`crate::UpgradeError::DuplicateStateChange`] /
4574    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
4575    ///     discipline is uniform: every Vec-shaped author-supplied list
4576    ///     past validate is set-not-multiset, by construction.
4577    ///
4578    /// Past the empty arm the gate enforces the chart-keyword shape
4579    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
4580    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
4581    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
4582    /// continuation. Closes the canonical paste-from-doc footguns the
4583    /// bare empty + duplicate arms left open: paste-from-aligned-doc
4584    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
4585    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
4586    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
4587    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
4588    /// — the author meant three separate list entries), path-separator
4589    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
4590    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
4591    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
4592    /// control bytes that would silently land as malformed search tags
4593    /// in the rendered Chart.yaml `keywords:` array and break the
4594    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
4595    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
4596    /// established on the sibling universal-axis `Vec<String>` surface
4597    /// — the second universal-axis Vec<String> surface to land the
4598    /// empty-first-then-shape-then-duplicate per-entry cascade.
4599    ///
4600    /// Same empty-first cascade discipline every peer per-axis gate
4601    /// uses: the per-entry empty arm fires before the per-entry shape
4602    /// arm fires before the cross-entry duplicate arm, so an
4603    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
4604    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
4605    /// has no value" defect) before either the shape or the duplicate
4606    /// diagnostic. Walks the list in declaration order so the
4607    /// first-collision diagnostic surfaces the lexicographically-
4608    /// earliest offending position, peer with every other duplicate
4609    /// gate on this surface.
4610    ///
4611    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
4612    /// caixa-build gate alongside the peer universal gates
4613    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4614    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
4615    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
4616    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4617    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4618    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4619    /// slot sets. The future caixa-registry search axis can reach for
4620    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
4621    /// chart-keyword-shaped string without re-deriving the precondition.
4622    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
4623        let mut seen = std::collections::HashSet::new();
4624        for etiqueta in self.etiquetas() {
4625            if etiqueta.is_empty() {
4626                return Err(ManifestError::EtiquetaEmpty);
4627            }
4628            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
4629                ManifestError::EtiquetaInvalid {
4630                    etiqueta: etiqueta.clone(),
4631                    reason,
4632                }
4633            })?;
4634            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
4635                ManifestError::EtiquetaDuplicate {
4636                    etiqueta: etiqueta.clone(),
4637                }
4638            })?;
4639        }
4640        Ok(())
4641    }
4642
4643    /// Reject `:autores` lists with an empty entry or with two entries
4644    /// agreeing on the same string. `:autores` is the universal
4645    /// maintainer-axis on [`Caixa`] (every kind carries the
4646    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4647    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
4648    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
4649    /// to a `Maintainer { name, email: None }` without dedup). Two
4650    /// authoring footguns silently passed validate without this gate:
4651    ///
4652    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
4653    ///     blank-doc footgun) rendered as
4654    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
4655    ///     empty maintainer name has no operational meaning — it
4656    ///     identifies no one in the substrate's authorship index and
4657    ///     clutters the rendered chart with a no-op maintainer.
4658    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
4659    ///     the copy-paste-the-wrong-author footgun) silently passed
4660    ///     validate and rendered as two identical maintainer entries.
4661    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
4662    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
4663    ///     rendered `keywords:` array at chart-render time), the
4664    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
4665    ///     entries stack verbatim in the chart, divergent from every
4666    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
4667    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
4668    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4669    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4670    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4671    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
4672    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
4673    ///     `:etiquetas`).
4674    ///
4675    /// Past the empty arm the gate enforces the chart-maintainer-name
4676    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
4677    /// the structural single-line printable-UTF-8 floor every realistic
4678    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
4679    /// or trailing whitespace, no ASCII control characters anywhere,
4680    /// Unicode bytes accepted. Closes the canonical paste-from-doc
4681    /// footguns the bare empty + duplicate arms left open:
4682    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
4683    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
4684    /// pasted a multi-line block of author records into one `:autores`
4685    /// entry instead of splitting into one entry per author),
4686    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
4687    /// and the paste-from-binary-blob control bytes that would silently
4688    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
4689    /// `maintainers:` array. Mirrors the shape-predicate cascade
4690    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4691    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
4692    /// establish past their own empty arms on the sibling universal-axis
4693    /// `Option<String>` surfaces — the first universal-axis Vec<String>
4694    /// surface to land the empty-first-then-shape-then-duplicate per-entry
4695    /// cascade.
4696    ///
4697    /// Same empty-first cascade discipline every peer per-axis gate
4698    /// uses: the per-entry empty arm fires before the per-entry shape
4699    /// arm before the cross-entry duplicate arm. Walks the list in
4700    /// declaration order so the first-collision diagnostic surfaces the
4701    /// lexicographically-earliest offending position, peer with every
4702    /// other duplicate gate on this surface.
4703    ///
4704    /// Universal-axis (every kind carries `:autores`), so wired at the
4705    /// caixa-build gate alongside the peer universal gates
4706    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4707    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4708    /// [`Self::validate_code_paths`] — before the kind-coherence gates
4709    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4710    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4711    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4712    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4713    /// slot sets.
4714    pub fn validate_autores(&self) -> Result<(), ManifestError> {
4715        let mut seen = std::collections::HashSet::new();
4716        for autor in self.autores() {
4717            if autor.is_empty() {
4718                return Err(ManifestError::AutorEmpty);
4719            }
4720            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
4721                ManifestError::AutorInvalid {
4722                    autor: autor.clone(),
4723                    reason,
4724                }
4725            })?;
4726            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
4727                ManifestError::AutorDuplicate {
4728                    autor: autor.clone(),
4729                }
4730            })?;
4731        }
4732        Ok(())
4733    }
4734
4735    /// Reject `:repositorio` values whose shape the shared
4736    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
4737    /// `repositorio: Option<String>` slot on [`Caixa`] is the
4738    /// universal git-shaped homepage axis every kind carries — the
4739    /// substrate routes the same string through two load-bearing
4740    /// consumers:
4741    ///
4742    ///   - [`caixa-helm`] folds it verbatim into the rendered
4743    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
4744    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
4745    ///     the chart `README.md` `repo = …` interpolation
4746    ///     (`caixa-helm/src/lib.rs:359`).
4747    ///   - [`caixa-flux`] folds it verbatim into the standalone
4748    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
4749    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
4750    ///     `GitRepository.spec.url` the cluster's source-controller
4751    ///     polls — the load-bearing deploy-time axis.
4752    ///
4753    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4754    /// substitute a placeholder when the slot is absent (`None` → the
4755    /// fallback fires); a `Some("")` *skips the fallback* and silently
4756    /// passes the empty string through to `Chart.yaml home: ""` /
4757    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
4758    /// controller both reject the empty URL far from the source
4759    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
4760    /// Similarly a malformed `:repositorio` (whitespace, control char,
4761    /// missing `:` separator, leading `-`) silently lands in the
4762    /// rendered artifacts and breaks at `git clone` / `helm template`
4763    /// / `flux reconcile` time.
4764    ///
4765    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
4766    /// same shared predicate the peer [`crate::DepSource::validate`]
4767    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
4768    /// gate the two `git URL`-shaped surfaces on the typed Caixa
4769    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
4770    /// structurally equivalent: every value past validate is
4771    /// guaranteed-acceptable by the predicate's union of constraints
4772    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
4773    /// control chars, ASCII only, no leading `:`, contains a `:`
4774    /// separator). The predicate accepts every documented authoring
4775    /// shape — `github:org/repo` shorthand, `https://host/path`,
4776    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
4777    /// scp-style SSH, `file:///path` — and refuses the canonical
4778    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
4779    /// injection footguns at validate time. Maps the predicate's
4780    /// `String` reason verbatim into the
4781    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
4782    /// offending value + parser-shaped reason so the diagnostic is
4783    /// self-locating (the author can grep their `caixa.lisp` for
4784    /// `:repositorio "<value>"` and fix it in one edit).
4785    ///
4786    /// `None` (the canonical "omit the slot to express no published
4787    /// homepage" shape) is accepted trivially — the gate is a no-op
4788    /// when the author didn't declare a value. `Some("")` is gated by
4789    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
4790    /// shape predicate is consulted, mirroring the empty-first cascade
4791    /// every peer per-axis identity gate uses
4792    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
4793    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
4794    /// [`crate::DepError::FonteRepoEmpty`] →
4795    /// [`crate::DepError::FonteRepoInvalid`]).
4796    ///
4797    /// Universal-axis (every kind carries `:repositorio`), so wired at
4798    /// the caixa-build gate alongside the peer universal gates
4799    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4800    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4801    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
4802    /// before the kind-coherence gates
4803    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4804    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4805    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4806    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4807    /// specific slot sets.
4808    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
4809        let Some(s) = self.repositorio() else {
4810            return Ok(());
4811        };
4812        if s.is_empty() {
4813            return Err(ManifestError::RepositorioEmpty);
4814        }
4815        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
4816            repositorio: s.to_string(),
4817            reason,
4818        })
4819    }
4820
4821    /// Reject `:descricao` values that are the empty string. The flat
4822    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
4823    /// free-form-prose homepage axis every kind carries — the
4824    /// substrate routes the same string through two load-bearing
4825    /// consumers in the [`caixa-helm`] renderer:
4826    ///
4827    ///   - `build_chart_yaml` folds it verbatim into the rendered
4828    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
4829    ///     field (`caixa-helm/src/lib.rs:232-235`).
4830    ///   - `build_readme` folds it verbatim into the rendered chart
4831    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
4832    ///
4833    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4834    /// substitute a `caixa.nome`-derived placeholder when the slot is
4835    /// absent (`None` → the fallback fires); a `Some("")` *skips the
4836    /// fallback* and silently passes the empty string through to
4837    /// `Chart.yaml description: ""` / a blank chart `README.md`
4838    /// header. Helm's chart spec requires a non-empty `description:`
4839    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
4840    /// `WARNING [chart.metadata.description]: description is required`),
4841    /// so the empty `Some("")` silently lands in the rendered
4842    /// artifacts and breaks at `helm lint` / `helm install` time far
4843    /// from the source `caixa.lisp`, with no field naming the
4844    /// offending `:descricao`.
4845    ///
4846    /// `None` (the canonical "omit the slot to defer to the renderer's
4847    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
4848    /// the gate is a no-op when the author didn't declare a value.
4849    /// `Some("")` is gated by the narrower
4850    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
4851    /// shape every peer per-axis empty gate uses
4852    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4853    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4854    /// [`ManifestError::RepositorioEmpty`]).
4855    ///
4856    /// Universal-axis (every kind carries `:descricao`), so wired at
4857    /// the caixa-build gate alongside the peer universal gates
4858    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4859    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4860    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4861    /// [`Self::validate_code_paths`] — before the kind-coherence
4862    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4863    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4864    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4865    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4866    /// specific slot sets.
4867    ///
4868    /// Past the empty arm the gate enforces the chart-description
4869    /// shape predicate via [`crate::render::is_chart_description_shape`]:
4870    /// the structural single-line UTF-8 floor every realistic chart
4871    /// description in the wild matches — 1..=512 bytes, no leading
4872    /// or trailing whitespace, no ASCII control characters anywhere
4873    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
4874    /// carriage return, and every other control byte), Unicode
4875    /// continuation bytes accepted (the canonical fixtures carry
4876    /// `→` and `—`). Closes the canonical paste-from-doc footguns
4877    /// the bare empty-arm gate left open: paste-from-aligned-doc
4878    /// leading / trailing whitespace (`" Checkout flow."`,
4879    /// `"Checkout flow. "`), paste-from-multiline-doc newline
4880    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
4881    /// (`"Checkout\rflow."`), tab-from-aligned-doc
4882    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
4883    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
4884    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
4885    /// [`Self::validate_edicao`] establish past their own empty arms
4886    /// on the sibling universal-axis `Option<String>` Caixa-level
4887    /// value-shape surfaces.
4888    ///
4889    /// The empty-first cascade discipline mirrors every peer per-axis
4890    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
4891    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
4892    /// diagnostic surfaces on `Some("")` rather than the broader
4893    /// shape-predicate diagnostic — peer with how
4894    /// [`ManifestError::LicencaEmpty`] runs before
4895    /// [`ManifestError::LicencaInvalid`],
4896    /// [`ManifestError::EdicaoEmpty`] runs before
4897    /// [`ManifestError::EdicaoInvalid`],
4898    /// [`ManifestError::RepositorioEmpty`] runs before
4899    /// [`ManifestError::RepositorioInvalid`].
4900    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
4901        let Some(s) = self.descricao() else {
4902            return Ok(());
4903        };
4904        if s.is_empty() {
4905            return Err(ManifestError::DescricaoEmpty);
4906        }
4907        crate::render::is_chart_description_shape(s).map_err(|reason| {
4908            ManifestError::DescricaoInvalid {
4909                descricao: s.to_string(),
4910                reason,
4911            }
4912        })?;
4913        Ok(())
4914    }
4915
4916    /// Reject `:licenca` values that are the empty string. The flat
4917    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
4918    /// SPDX-shaped license-expression axis every kind carries — the
4919    /// substrate routes the same string through the [`caixa-helm`]
4920    /// renderer's `build_readme` which folds it verbatim into the
4921    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
4922    /// section (`caixa-helm/src/lib.rs:361`) via
4923    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
4924    /// fallback only fires on `None`; a `Some("")` *skips the
4925    /// fallback* and silently passes the empty string through to a
4926    /// chart `README.md` whose `License` section renders as the bare
4927    /// trailing period (`.\n`) — peer footgun with the
4928    /// `Some("")`-skips-`unwrap_or_else` shape the
4929    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
4930    /// gates close on the sibling free-form-prose and git-URL axes.
4931    ///
4932    /// `None` (the canonical "omit the slot to defer to the
4933    /// renderer's `MIT` fallback" shape every existing fixture
4934    /// carries) is accepted trivially — the gate is a no-op when the
4935    /// author didn't declare a value. `Some("")` is gated by the
4936    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
4937    /// empty-arm shape every peer per-axis empty gate uses
4938    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4939    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4940    /// [`ManifestError::RepositorioEmpty`],
4941    /// [`ManifestError::DescricaoEmpty`]).
4942    ///
4943    /// Universal-axis (every kind carries `:licenca`), so wired at
4944    /// the caixa-build gate alongside the peer universal gates
4945    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4946    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4947    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4948    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
4949    /// — before the kind-coherence gates
4950    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4951    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4952    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4953    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4954    /// specific slot sets.
4955    ///
4956    /// Past the empty arm the gate enforces the SPDX-expression shape
4957    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
4958    /// structural alphabet floor every realistic SPDX expression in
4959    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
4960    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
4961    /// single ASCII space (token separator). Closes the canonical
4962    /// paste-from-doc footguns the bare empty-arm gate left open:
4963    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
4964    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
4965    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
4966    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
4967    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
4968    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
4969    /// Apache-2.0"`), and semicolon-list-separator confusion
4970    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
4971    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
4972    /// establish past their own empty arms.
4973    ///
4974    /// The empty-first cascade discipline mirrors every peer per-axis
4975    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
4976    /// [`ManifestError::LicencaInvalid`], so the narrower empty
4977    /// diagnostic surfaces on `Some("")` rather than the broader
4978    /// shape-predicate diagnostic — peer with how
4979    /// [`ManifestError::EdicaoEmpty`] runs before
4980    /// [`ManifestError::EdicaoInvalid`],
4981    /// [`ManifestError::RepositorioEmpty`] runs before
4982    /// [`ManifestError::RepositorioInvalid`].
4983    ///
4984    /// A future tightening on this axis can extend the alphabet
4985    /// floor into a full SPDX expression parser + license-id
4986    /// allowlist (rejecting alphabet-valid values that don't name a
4987    /// real SPDX license identifier — e.g., `"NotAReal"` is
4988    /// alphabet-valid but no `NotAReal` license-id exists). That
4989    /// parser only becomes meaningful past a real SPDX-spec
4990    /// dependency; this gate establishes the structural floor by
4991    /// refusing every non-SPDX-alphabet value at validate time.
4992    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
4993        let Some(s) = self.licenca() else {
4994            return Ok(());
4995        };
4996        if s.is_empty() {
4997            return Err(ManifestError::LicencaEmpty);
4998        }
4999        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
5000            ManifestError::LicencaInvalid {
5001                licenca: s.to_string(),
5002                reason,
5003            }
5004        })?;
5005        Ok(())
5006    }
5007
5008    /// Reject `:edicao` values that are the empty string. The flat
5009    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
5010    /// language-edition axis every kind carries — it determines the
5011    /// tatara-lisp macro surface + compatibility flags the substrate
5012    /// applies when building a caixa, and lands verbatim in the
5013    /// `Caixa::template` author-time scaffold (the canonical
5014    /// `:edicao "2026"` line every `feira init` emits via
5015    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
5016    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
5017    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
5018    /// `caixa-core/src/render.rs:2510`) via
5019    /// `edicao: Some("2026".into())`.
5020    ///
5021    /// `None` (the canonical "omit the slot to defer to the
5022    /// substrate's default edition" shape every existing
5023    /// [`caixa-resolver`] integration test fixture carries via
5024    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5025    /// is accepted trivially — the gate is a no-op when the author
5026    /// didn't declare a value. `Some("")` is gated by the narrower
5027    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
5028    /// shape every peer per-axis empty gate uses
5029    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5030    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5031    /// [`ManifestError::RepositorioEmpty`],
5032    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
5033    ///
5034    /// Universal-axis (every kind carries `:edicao`), so wired at
5035    /// the caixa-build gate alongside the peer universal gates
5036    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5037    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5038    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5039    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
5040    /// [`Self::validate_code_paths`] — before the kind-coherence
5041    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5042    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5043    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5044    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5045    /// specific slot sets.
5046    ///
5047    /// Past the empty arm the gate enforces the canonical year-shape
5048    /// predicate: every documented tatara-lisp edition is a 4-digit
5049    /// ASCII decimal year (`"2026"` is the only edition currently
5050    /// minted; future-introduced siblings will follow the same
5051    /// shape, peer with Cargo's `[package] edition` grammar which
5052    /// every value Cargo has ever accepted matches — `"2015"`,
5053    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
5054    /// 4 ASCII decimal bytes is rejected with the narrower
5055    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
5056    /// shape-predicate cascade [`Self::validate_repositorio`]
5057    /// establishes past its own empty arm
5058    /// ([`ManifestError::RepositorioEmpty`] →
5059    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
5060    /// paste-from-doc footguns the bare empty-arm gate left open:
5061    ///
5062    ///   - leading / trailing whitespace from a paste-from-doc
5063    ///     (`"2026 "`, `" 2026"`)
5064    ///   - control characters / CRLF from a paste-from-multiline-doc
5065    ///     (`"2026\n"`)
5066    ///   - non-ASCII look-alikes from a fullwidth keyboard
5067    ///     (`"2026"`) which would silently land as a non-ASCII
5068    ///     string in the rendered caixa.lisp
5069    ///   - free-form non-year values (`"x"`, `"latest"`,
5070    ///     `"nightly"`) that have no operational meaning on the
5071    ///     substrate's build-time edition selector
5072    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
5073    ///     `"r2026"`) — common version-tag idioms that don't apply
5074    ///     to the year-shaped edition axis
5075    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
5076    ///     edition is a year, not a fractional version
5077    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
5078    ///     `"00026"`) that don't name a year
5079    ///
5080    /// `None` (the canonical "omit the slot to defer to the
5081    /// substrate's default edition" shape every existing
5082    /// [`caixa-resolver`] integration test fixture carries via
5083    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5084    /// is accepted trivially — the gate is a no-op when the author
5085    /// didn't declare a value. The empty-first cascade discipline
5086    /// mirrors every peer per-axis identity gate:
5087    /// [`ManifestError::EdicaoEmpty`] runs before
5088    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
5089    /// diagnostic surfaces on `Some("")` rather than the broader
5090    /// shape-predicate diagnostic — peer with how
5091    /// [`ManifestError::NomeEmpty`] runs before
5092    /// [`ManifestError::NomeInvalid`],
5093    /// [`ManifestError::VersaoEmpty`] runs before
5094    /// [`ManifestError::VersaoInvalid`],
5095    /// [`ManifestError::RepositorioEmpty`] runs before
5096    /// [`ManifestError::RepositorioInvalid`].
5097    ///
5098    /// A future tightening on this axis can extend the shape
5099    /// predicate into a known-edition allowlist (rejecting
5100    /// year-shaped values that don't name a tatara-lisp edition
5101    /// the substrate actually understands — e.g., `"1999"` is
5102    /// year-shaped but no `1999` edition exists). That allowlist
5103    /// only becomes meaningful past the introduction of a sibling
5104    /// edition to `"2026"`; this gate establishes the structural
5105    /// floor by refusing every non-year-shaped value at validate
5106    /// time.
5107    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
5108        let Some(s) = self.edicao() else {
5109            return Ok(());
5110        };
5111        if s.is_empty() {
5112            return Err(ManifestError::EdicaoEmpty);
5113        }
5114        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
5115            return Err(ManifestError::EdicaoInvalid {
5116                edicao: s.to_string(),
5117                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
5118            });
5119        }
5120        Ok(())
5121    }
5122
5123    /// Compose the supervisor-related flat slots into a single
5124    /// [`SupervisorSpec`] for validation. Returns `None` when the
5125    /// caixa isn't a `:kind Supervisor`.
5126    ///
5127    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
5128    /// simple (one form, no nested `:supervisor (…)` block); this view
5129    /// is the "typed shape" the operator + supervisor reconciler
5130    /// consume.
5131    #[must_use]
5132    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
5133        if !self.kind().is_supervisor() {
5134            return None;
5135        }
5136        // Fold through the shared `supervisor::duration_codec::parse`
5137        // — the same parser the serde-routed `with = "duration_codec"`
5138        // on `SupervisorSpec::restart_window`, the `:politicas
5139        // :timeout` codec, and the `:politicas :circuit-breaker
5140        // :window` codec all consume. The prior inline f64-shaped
5141        // duplicate (`parse_window_inline`) admitted every magnitude
5142        // the integer-magnitude gate (1c55a2a) rejects on the three
5143        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
5144        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
5145        // `None` (i.e. "no reset"), divergent from the shared codec's
5146        // integer-magnitude discipline by construction. The fold
5147        // closes the divergence: every value the typed
5148        // `SupervisorSpec` carries past `supervisor_view` is in the
5149        // shared codec's accepted set. The `.ok()` here preserves the
5150        // existing soft-swallow shape on this view-construction path;
5151        // the new [`Caixa::validate_restart_window`] (sibling of
5152        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
5153        // the offending raw string at build time so authoring tools
5154        // (`feira lint`, the future layout-side wire-up) surface a
5155        // self-locating diagnostic instead of a silently dropped
5156        // window.
5157        let restart_window = self
5158            .restart_window()
5159            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
5160        Some(SupervisorSpec {
5161            estrategia: self.estrategia().unwrap_or_default(),
5162            max_restarts: self.max_restarts().unwrap_or(5),
5163            restart_window,
5164            children: self.children().to_vec(),
5165        })
5166    }
5167
5168    /// A minimal starter manifest emitted by `feira init`.
5169    #[must_use]
5170    pub fn template(nome: &str) -> String {
5171        format!(
5172            "(defcaixa\n  \
5173               :nome        {nome:?}\n  \
5174               :versao      \"0.1.0\"\n  \
5175               :kind        Biblioteca\n  \
5176               :edicao      \"2026\"\n  \
5177               :descricao   \"FIXME — describe this caixa\"\n  \
5178               :autores     ()\n  \
5179               :etiquetas   ()\n  \
5180               :deps        ()\n  \
5181               :deps-dev    ()\n  \
5182               :bibliotecas (\"lib/{nome}.lisp\"))\n"
5183        )
5184    }
5185
5186    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
5187    /// back after mutation (e.g. `feira add`).
5188    ///
5189    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
5190    /// The derive-macro `compile_from_sexp` path is the inverse, so any
5191    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
5192    #[must_use]
5193    pub fn to_lisp(&self) -> String {
5194        let json = serde_json::to_value(self).expect("Caixa serialize");
5195        let sexp = tatara_lisp::domain::json_to_sexp(&json);
5196        let tatara_lisp::Sexp::List(items) = sexp else {
5197            return format!("(defcaixa {sexp})\n");
5198        };
5199        let mut out = String::from("(defcaixa");
5200        let mut i = 0;
5201        while i + 1 < items.len() {
5202            out.push_str("\n  ");
5203            out.push_str(&items[i].to_string());
5204            out.push(' ');
5205            out.push_str(&items[i + 1].to_string());
5206            i += 2;
5207        }
5208        out.push_str(")\n");
5209        out
5210    }
5211}
5212
5213/// Errors raised by top-level [`Caixa`] validators that don't fit
5214/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
5215/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
5216/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
5217/// through every substrate-side artifact's `metadata.name` /
5218/// version derivation.
5219///
5220/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
5221/// doc-comment anticipates) can hold one of each per-axis error
5222/// family without reshaping individual diagnostics; this enum is
5223/// the first such per-Caixa-identity family.
5224#[derive(Debug, Error, PartialEq, Eq)]
5225pub enum ManifestError {
5226    #[error(
5227        ":nome is empty (every caixa must name itself; the value flows \
5228         into every K8s artifact's `metadata.name` derivation and into \
5229         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
5230    )]
5231    NomeEmpty,
5232    #[error(
5233        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
5234         apiserver enforces this rule on every `metadata.name` the \
5235         caixa's substrate-side renderers derive from `:nome` — the \
5236         `lareira-<nome>` Helm chart name, the programs.yaml entry \
5237         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
5238         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
5239         name; use a lowercase alphanumeric + hyphen identifier like \
5240         `\"checkout\"` or `\"cart-v2\"`)"
5241    )]
5242    NomeInvalid { nome: String, reason: String },
5243    #[error(
5244        ":nome {nome:?} overflows the joint-length budget on the canonical \
5245         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
5246         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
5247         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
5248         `chart:` slot, `caixa-tatara`'s `release_name` + \
5249         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
5250         joint name through the canonical `lareira_chart_name` helper, and \
5251         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
5252         DNS-1123 label cap on every chart-name-derived `metadata.name` \
5253         reject any joint name exceeding 63 bytes; the narrower \
5254         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
5255         arm gates the chart-name budget downstream renderers inherit)"
5256    )]
5257    NomeChartNameBudgetExceeded { nome: String, reason: String },
5258    #[error(
5259        ":versao is empty (every caixa must pin its own version; the value flows \
5260         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
5261         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
5262         `:latest` tags, the lacre closure's `concrete_versao`, and the \
5263         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
5264    )]
5265    VersaoEmpty,
5266    #[error(
5267        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
5268         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
5269         with optional `-prerelease` and `+build` — across every artifact derived \
5270         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
5271         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
5272         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
5273         and the `:upgrade-from :from` peers that match against this exact shape; \
5274         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
5275         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
5276         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
5277    )]
5278    VersaoInvalid { versao: String, reason: String },
5279    #[error(
5280        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
5281         substrate consumes this string through the shared \
5282         `supervisor::duration_codec` — the same parser routed via `with = \
5283         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
5284         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
5285         the canonical authoring form is `<integer><unit>` where the unit is one \
5286         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
5287         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
5288         Without this gate a malformed `:restart-window` silently produced a \
5289         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
5290         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
5291         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
5292         layer with the offending value named verbatim. Omit the slot entirely to \
5293         express \"no reset\"; carry a positive integer duration to express the \
5294         sliding window)"
5295    )]
5296    RestartWindowMalformed {
5297        restart_window: String,
5298        reason: String,
5299    },
5300    #[error(
5301        "{slot} entry is an empty path string — every {slot} entry must name \
5302         a file relative to the caixa root; omit the entry to omit the file \
5303         (the layout checker's `root.join(\"\")` resolves to the caixa root \
5304         itself, so an empty entry silently aliases the project root as a \
5305         declared {slot} file, then fails downstream at parse / existence \
5306         time with a diagnostic that names the root rather than the offending \
5307         entry)"
5308    )]
5309    CodePathEmpty { slot: &'static str },
5310    #[error(
5311        "{slot} entry {} is an absolute path — entries must be relative to \
5312         the caixa root, since `Path::join` replaces the base with an absolute \
5313         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
5314         outside the caixa root sandbox; rewrite the entry as a relative path \
5315         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
5316         `\"servicos/<name>.computeunit.yaml\"`)",
5317        path.display()
5318    )]
5319    CodePathAbsolute { slot: &'static str, path: PathBuf },
5320    #[error(
5321        "{slot} entry {} contains a `..` component — entries must not traverse \
5322         above the caixa root (the layout's `starts_with(<dir>)` fence on \
5323         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
5324         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
5325         has no such fence, so a leading `..` escapes unconditionally if the \
5326         resolved target happens to exist)",
5327        path.display()
5328    )]
5329    CodePathParentEscape { slot: &'static str, path: PathBuf },
5330    #[error(
5331        "{slot} entry {} does not terminate in the `.lisp` extension — every \
5332         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
5333         loop reads through `tatara_lisp::read` at parse time, so any other \
5334         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
5335         structurally a parser error far from the source caixa.lisp, with \
5336         no field naming the offending `:bibliotecas` entry. Pin a relative \
5337         path under the caixa root whose terminating extension is \
5338         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
5339         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
5340         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
5341         (33cc830) axes already carry through the same lifted \
5342         `is_lisp_extension` predicate",
5343        path.display()
5344    )]
5345    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
5346    #[error(
5347        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
5348         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
5349         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
5350         through `serde_yaml::from_str` at chart / FluxCD bundle render \
5351         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
5352         off-by-one-segment `.computeunit-yaml`, the editor-backup \
5353         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
5354         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
5355         source caixa.lisp, with no field naming the offending `:servicos` \
5356         entry. Pin a relative path under the caixa root whose terminating \
5357         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
5358         `\"servicos/<name>.computeunit.yaml\"`, \
5359         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
5360         contract the sibling `:bibliotecas` axis (64772a9) already carries \
5361         on the tatara-lisp-source axis through the peer lifted \
5362         `is_lisp_extension` predicate, here on the compound-suffix axis \
5363         `Path::extension` can't express on its own through the lifted \
5364         `is_computeunit_yaml_extension` predicate",
5365        path.display()
5366    )]
5367    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
5368    #[error(
5369        "{slot} entry {} appears more than once (the code-path list is \
5370         a set, not a multiset; every peer Vec-shaped author-supplied \
5371         list past validate is set-not-multiset — `:membros :caixa`, \
5372         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5373         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
5374         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
5375         code-path lists are the last Vec-shaped author-supplied slots on \
5376         the typed Caixa surface still admitting a duplicate entry. \
5377         `:bibliotecas` duplicates re-parse the same file at \
5378         `feira build` time and silently mask the author's intent to \
5379         declare a *second* biblioteca; `:exe` duplicates collide on the \
5380         flake `packages.<name>` derivation key at the future \
5381         `caixa-flake` materializer; `:servicos` duplicates surface as the \
5382         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
5383         rejection far from the source `caixa.lisp`. Drop the duplicate \
5384         or rename it to the actual second file intended)",
5385        path.display()
5386    )]
5387    CodePathDuplicate { slot: &'static str, path: PathBuf },
5388    #[error(
5389        ":etiquetas entry is empty (every tag must carry a non-empty \
5390         registry-search identifier; the empty entry has no operational \
5391         meaning — it indexes nothing in the future caixa-registry search \
5392         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
5393         with a no-op tag; omit the entry to express \"no tag on this \
5394         position\")"
5395    )]
5396    EtiquetaEmpty,
5397    #[error(
5398        ":etiquetas entry {etiqueta:?} appears more than once (the \
5399         registry-search tag set is a set, not a multiset; duplicate \
5400         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
5401         at chart render — a \"second wins / one silently disappears\" \
5402         shape divergent from every peer typed-graph set gate \
5403         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
5404         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
5405         duplicate or rename it to the actual tag intended)"
5406    )]
5407    EtiquetaDuplicate { etiqueta: String },
5408    #[error(
5409        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
5410         {reason} (the substrate consumes this string through the shared \
5411         `crate::render::is_chart_keyword_shape` predicate — the same \
5412         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
5413         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
5414         continuation. The canonical authoring shapes are short kebab-case \
5415         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
5416         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
5417         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
5418         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
5419         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
5420         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
5421         `\"mesh,http,grpc\"` — the author meant to author three separate \
5422         list entries; path-separator confusion `\"caixa/servico\"`; \
5423         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
5424         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
5425         `\"café\"` — every legitimate search tag is strict ASCII; \
5426         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
5427         passed `from_lisp` + `validate_etiquetas` + \
5428         `StandardLayout::verify` and landed in the rendered \
5429         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
5430         malformed search tag — Artifact Hub's keyword index + the future \
5431         caixa-registry's keyword index would either silently drop the \
5432         tag or fail to index it far from the source caixa.lisp; the gate \
5433         moves the diagnostic to the manifest layer with the offending \
5434         value named verbatim)"
5435    )]
5436    EtiquetaInvalid { etiqueta: String, reason: String },
5437    #[error(
5438        ":autores entry is empty (every maintainer must carry a non-empty \
5439         identifier; the empty entry has no operational meaning — it \
5440         identifies no one in the substrate's authorship index and renders \
5441         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
5442         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
5443         omit the entry to express \"no maintainer on this position\")"
5444    )]
5445    AutorEmpty,
5446    #[error(
5447        ":autores entry {autor:?} appears more than once (the maintainer \
5448         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
5449         `maintainers:` rendering does *no* dedup — duplicate entries \
5450         stack verbatim in `Chart.yaml` as two identical \
5451         `Maintainer {{ name, email: None }}` records, divergent from every \
5452         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
5453         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5454         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
5455         rename it to the actual author intended)"
5456    )]
5457    AutorDuplicate { autor: String },
5458    #[error(
5459        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
5460         {reason} (the substrate consumes this string through the shared \
5461         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
5462         single-line-UTF-8 floor every realistic chart maintainer name carries: \
5463         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
5464         characters anywhere, Unicode bytes accepted. The canonical authoring \
5465         shapes are short single-line identifiers like `\"pleme-io\"`, \
5466         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
5467         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
5468         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
5469         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
5470         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
5471         records into one entry instead of splitting into one entry per author; \
5472         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
5473         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
5474         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5475         `validate_autores` + `StandardLayout::verify` and landed in the \
5476         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
5477         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
5478         round-trip — every chart-aware UI (`helm list`, `helm search`, \
5479         Artifact Hub maintainer index) would render the maintainer name in a \
5480         single-line column far from the source caixa.lisp; the gate moves the \
5481         diagnostic to the manifest layer with the offending value named \
5482         verbatim)"
5483    )]
5484    AutorInvalid { autor: String, reason: String },
5485    #[error(
5486        ":repositorio is the empty string (every published caixa names its \
5487         git source via a non-empty `:repositorio` locator — the value \
5488         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
5489         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
5490         `GitRepository.spec.url` via `caixa-flux`'s \
5491         `ClusterBundleOpts::for_caixa`; both consumers' \
5492         `Option::unwrap_or_else` fallbacks only fire when the slot is \
5493         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
5494         `url: \"\"` in the rendered artifacts and breaks at `helm \
5495         template` / FluxCD source-controller reconcile time far from the \
5496         source caixa.lisp; omit the slot entirely to defer to the \
5497         renderer's `https://github.com/pleme-io/<nome>` / \
5498         `caixa.nome`-derived fallback, or carry a canonical authoring \
5499         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
5500         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
5501         `\"file:///path\"`)"
5502    )]
5503    RepositorioEmpty,
5504    #[error(
5505        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
5506         (the substrate consumes this string through the shared \
5507         `crate::render::is_git_repo_url` predicate — the same parser the \
5508         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
5509         value through via `DepSource::validate`; the canonical authoring \
5510         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
5511         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
5512         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
5513         scp-style SSH form. Without this gate a malformed `:repositorio` \
5514         (whitespace from a paste-from-doc; control characters / CRLF \
5515         from a paste-from-multiline-doc; a leading `-` from a \
5516         CLI-argument-injection footgun; a missing `:` separator from a \
5517         bare `org/repo` shape git treats as a relative filesystem path) \
5518         silently landed in the rendered `Chart.yaml home:` and the \
5519         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
5520         FluxCD reconcile time far from the source caixa.lisp; the gate \
5521         moves the diagnostic to the manifest layer with the offending \
5522         value named verbatim)"
5523    )]
5524    RepositorioInvalid { repositorio: String, reason: String },
5525    #[error(
5526        ":descricao is the empty string (every published caixa names \
5527         its purpose via a non-empty `:descricao` summary — the value \
5528         flows verbatim into the rendered `lareira-<nome>` Helm \
5529         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
5530         `build_chart_yaml` and into the chart `README.md` header via \
5531         `build_readme`; both consumers' `Option::unwrap_or_else` \
5532         `caixa.nome`-derived fallbacks only fire when the slot is \
5533         `None`, so an empty `Some(\"\")` silently lands as \
5534         `description: \"\"` / a blank `README.md` header in the \
5535         rendered artifacts and breaks at `helm lint` time \
5536         (`WARNING [chart.metadata.description]: description is \
5537         required` on `apiVersion: v2` charts) far from the source \
5538         caixa.lisp; omit the slot entirely to defer to the \
5539         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
5540         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
5541         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
5542         Servico.\"`)"
5543    )]
5544    DescricaoEmpty,
5545    #[error(
5546        ":descricao {descricao:?} is not a valid chart-description shape: \
5547         {reason} (the substrate consumes this string through the shared \
5548         `crate::render::is_chart_description_shape` predicate — the same \
5549         single-line-UTF-8 floor every realistic chart description carries: \
5550         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
5551         characters anywhere, Unicode prose bytes accepted. The canonical \
5552         authoring shapes are short single-line summaries like `\"Canonical \
5553         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
5554         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
5555         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
5556         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
5557         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
5558         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
5559         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
5560         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5561         `validate_descricao` + `StandardLayout::verify` and landed in the \
5562         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
5563         field + `README.md` header paragraph as a YAML-illegal multi-line \
5564         scalar or a silently-trimmed whitespace round-trip — every \
5565         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
5566         render the description in a single-line column far from the source \
5567         caixa.lisp; the gate moves the diagnostic to the manifest layer \
5568         with the offending value named verbatim)"
5569    )]
5570    DescricaoInvalid { descricao: String, reason: String },
5571    #[error(
5572        ":licenca is the empty string (every published caixa names \
5573         its license via a non-empty `:licenca` SPDX expression — the \
5574         value flows verbatim into the rendered `lareira-<nome>` Helm \
5575         chart's `README.md` `## License` section via `caixa-helm`'s \
5576         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
5577         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
5578         only fires when the slot is `None`, so an empty `Some(\"\")` \
5579         silently lands as a bare trailing period in the rendered \
5580         chart `README.md` `License` section far from the source \
5581         caixa.lisp; omit the slot entirely to defer to the \
5582         renderer's `MIT` fallback, or carry a canonical SPDX \
5583         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
5584         `\"Apache-2.0 OR MIT\"`)"
5585    )]
5586    LicencaEmpty,
5587    #[error(
5588        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
5589         (the substrate consumes this string through the shared \
5590         `crate::render::is_spdx_expression_shape` predicate — the same \
5591         alphabet-floor parser every peer per-axis value-shape gate routes \
5592         its value through; the canonical authoring shapes are single \
5593         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
5594         compound expressions like `\"Apache-2.0 OR MIT\"`, \
5595         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
5596         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
5597         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
5598         like `\"LicenseRef-MyLicense\"` / \
5599         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
5600         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
5601         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
5602         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
5603         a smart-quote paste; underscore-instead-of-hyphen typo \
5604         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
5605         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
5606         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
5607         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
5608         `README.md` `## License` section + a future SPDX-aware \
5609         `Chart.yaml license:` emitter would refuse the value at \
5610         `helm lint` time far from the source caixa.lisp; the gate moves \
5611         the diagnostic to the manifest layer with the offending value \
5612         named verbatim)"
5613    )]
5614    LicencaInvalid { licenca: String, reason: String },
5615    #[error(
5616        ":edicao is the empty string (every published caixa names \
5617         its language edition via a non-empty `:edicao` value — the \
5618         edition determines the tatara-lisp macro surface + \
5619         compatibility flags the substrate applies when building \
5620         the caixa; the canonical `Caixa::template` scaffold every \
5621         `feira init` emits carries `:edicao \"2026\"` verbatim and \
5622         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
5623         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
5624         construction, so an empty `Some(\"\")` silently lands as a \
5625         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
5626         a future renderer-side consumer that folds it through \
5627         `Option::unwrap_or_else` will skip the fallback and pass the \
5628         empty edition through to the substrate's build-time edition \
5629         selector far from the source caixa.lisp; omit the slot \
5630         entirely to defer to the substrate's default edition, or \
5631         carry a canonical edition like `\"2026\"`)"
5632    )]
5633    EdicaoEmpty,
5634    #[error(
5635        ":edicao {edicao:?} is not a valid edition: {reason} (every \
5636         documented tatara-lisp edition is a 4-digit ASCII decimal \
5637         year — `\"2026\"` is the only edition currently minted; \
5638         future-introduced siblings will follow the same shape, peer \
5639         with Cargo's `[package] edition` grammar which every value \
5640         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
5641         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
5642         paste-from-doc footguns silently passed: a trailing space \
5643         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
5644         from a paste-from-multiline-doc, a fullwidth-keyboard \
5645         look-alike (`\"2026\"`), a free-form non-year value \
5646         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
5647         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
5648         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
5649         wrong-length numeric value (`\"26\"`, `\"202\"`, \
5650         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
5651         rendered caixa.lisp and broke at the substrate's \
5652         build-time edition selector far from the source caixa.lisp; \
5653         omit the slot entirely to defer to the substrate's default \
5654         edition, or carry a canonical 4-digit ASCII decimal year \
5655         like `\"2026\"`)"
5656    )]
5657    EdicaoInvalid { edicao: String, reason: String },
5658}
5659
5660#[cfg(test)]
5661mod tests {
5662    use super::*;
5663
5664    #[test]
5665    fn template_round_trips() {
5666        let src = Caixa::template("demo");
5667        let c = Caixa::from_lisp(&src).expect("template must parse");
5668        assert_eq!(c.nome, "demo");
5669        assert_eq!(c.versao, "0.1.0");
5670        assert_eq!(c.kind, CaixaKind::Biblioteca);
5671        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
5672        assert!(c.deps.is_empty());
5673        assert!(c.deps_dev.is_empty());
5674    }
5675
5676    #[test]
5677    fn register_populates_registry() {
5678        Caixa::register().expect("first register call in this test process must succeed");
5679        let kws = tatara_lisp::domain::registered_keywords();
5680        assert!(kws.contains(&"defcaixa"));
5681    }
5682
5683    #[test]
5684    fn to_lisp_round_trips() {
5685        let src = Caixa::template("demo");
5686        let c1 = Caixa::from_lisp(&src).unwrap();
5687        let emitted = c1.to_lisp();
5688        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
5689        assert_eq!(c1, c2);
5690    }
5691
5692    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
5693    //
5694    // The compounding pin: the variant stores only the typed
5695    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
5696    // (canonical keyword, description, consumer) routes through the enum's
5697    // own accessors at Display time. Prior to that closure the variant
5698    // carried each accessor's return value as a stored `&'static str`
5699    // snapshot alongside `dialeto`; a caller could construct the variant
5700    // with a snapshot that drifted from what `dialeto`'s accessors would
5701    // return, and every downstream user-facing projection would silently
5702    // disagree with the classification. Storing only the axis makes the
5703    // drift structurally impossible.
5704
5705    #[test]
5706    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
5707        // Single-field construction is the whole compounding shape — a
5708        // future re-introduction of a snapshot field (a `palavra_canonica:
5709        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
5710        // would re-open the drift surface and this construction would fail
5711        // to compile with "missing field" until every snapshot was seeded
5712        // at the call site again. The compile-time guarantee is the
5713        // invariant; the assertion below only witnesses that the
5714        // construction is well-formed after the closure.
5715        let err = LeituraError::DialetoEstrangeiro {
5716            dialeto: crate::dialeto::CaixaDialeto::Molde,
5717        };
5718        assert!(matches!(
5719            err,
5720            LeituraError::DialetoEstrangeiro {
5721                dialeto: crate::dialeto::CaixaDialeto::Molde,
5722            }
5723        ));
5724    }
5725
5726    #[test]
5727    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
5728        // For every foreign-dialect classification the variant surfaces —
5729        // [`crate::dialeto::CaixaDialeto::Molde`] and
5730        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
5731        // variants [`Caixa::from_lisp`] raises this error for — the
5732        // rendered [`std::fmt::Display`] byte-string must interpolate each
5733        // typed accessor's return verbatim. A future re-introduction of a
5734        // stored `&'static str` snapshot alongside `dialeto` that Display
5735        // read instead of the accessor would fail this pin as soon as the
5736        // two disagreed; a future accessor rebrand (a per-dialect
5737        // consumer rename, a canonical-keyword shift once the substrate
5738        // migration named in [`crate::dialeto`] completes) reaches every
5739        // consumer through one typed dispatch and this pin verifies the
5740        // display path is one of them.
5741        for d in [
5742            crate::dialeto::CaixaDialeto::Molde,
5743            crate::dialeto::CaixaDialeto::MoldePosicional,
5744        ] {
5745            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
5746            assert!(
5747                rendered.contains(d.palavra_canonica()),
5748                "Display must interpolate `dialeto.palavra_canonica()` \
5749                 verbatim — a stored snapshot would silently drift from \
5750                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
5751            );
5752            assert!(
5753                rendered.contains(d.descricao()),
5754                "Display must interpolate `dialeto.descricao()` verbatim. \
5755                 dialect: {d}, rendered: {rendered:?}"
5756            );
5757            assert!(
5758                rendered.contains(d.consumidor()),
5759                "Display must interpolate `dialeto.consumidor()` verbatim. \
5760                 dialect: {d}, rendered: {rendered:?}"
5761            );
5762        }
5763    }
5764
5765    #[test]
5766    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
5767        // The end-to-end pin the compounding closure defends: a
5768        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
5769        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
5770        // rendered Display byte-string names the Molde accessors'
5771        // returns verbatim. Any future path that constructed the variant
5772        // with a mismatched snapshot (a stored `palavra_canonica:
5773        // "defcaixa"` on a `Molde` classification) would land Display
5774        // pointing at `defcaixa` while the typed axis said `Molde` — the
5775        // exact drift the closure removes.
5776        let src = r#"
5777          (defcaixa
5778            :name "x"
5779            :kind :Biblioteca
5780            :ecosystem :rust-single-crate
5781            :package {:name "x" :version "0.1.0"})
5782        "#;
5783        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
5784        match err {
5785            LeituraError::DialetoEstrangeiro { dialeto } => {
5786                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
5787                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5788                assert!(rendered.contains(dialeto.palavra_canonica()));
5789                assert!(rendered.contains(dialeto.consumidor()));
5790                assert!(rendered.contains(dialeto.descricao()));
5791            }
5792            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
5793        }
5794    }
5795
5796    #[test]
5797    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
5798        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
5799        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
5800        // positional-arity `defmolde` form written under a `(defcaixa …)`
5801        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
5802        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
5803        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
5804        // so no test exercised the positional-arity path through
5805        // `Caixa::from_lisp` specifically; the sibling
5806        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
5807        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
5808        // two arms route through the lifted
5809        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
5810        // typed predicate — the same predicate the pre-lift `foreign =>`
5811        // wildcard resolved to today — and this pin makes the
5812        // positional-arity arm's byte-shape at the gate explicit rather
5813        // than implied by wildcard-absorption. A future regression that
5814        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
5815        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
5816        // from the two-arity closure) would fail this pin at caixa-core
5817        // test time rather than surfacing far from the change as a
5818        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
5819        // …)` silently parsing past the derive.
5820        let src = r#"
5821          (defcaixa todoku-go
5822            :kind :Biblioteca
5823            :ecosystem :go
5824            :package {:name "todoku-go" :version "0.3.0"})
5825        "#;
5826        let err =
5827            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
5828        match err {
5829            LeituraError::DialetoEstrangeiro { dialeto } => {
5830                assert_eq!(
5831                    dialeto,
5832                    crate::dialeto::CaixaDialeto::MoldePosicional,
5833                    "DialetoEstrangeiro must carry the MoldePosicional \
5834                     variant verbatim — the positional-arity `defmolde` \
5835                     form under a `(defcaixa …)` head is the \
5836                     `MoldePosicional` arm's canonical byte-shape"
5837                );
5838                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5839                assert!(
5840                    rendered.contains(dialeto.palavra_canonica()),
5841                    "Display must interpolate `dialeto.palavra_canonica()` \
5842                     verbatim on the MoldePosicional arm; rendered: \
5843                     {rendered:?}"
5844                );
5845                assert!(
5846                    rendered.contains(dialeto.consumidor()),
5847                    "Display must interpolate `dialeto.consumidor()` \
5848                     verbatim on the MoldePosicional arm; rendered: \
5849                     {rendered:?}"
5850                );
5851                assert!(
5852                    rendered.contains(dialeto.descricao()),
5853                    "Display must interpolate `dialeto.descricao()` \
5854                     verbatim on the MoldePosicional arm; rendered: \
5855                     {rendered:?}"
5856                );
5857            }
5858            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
5859        }
5860    }
5861
5862    #[test]
5863    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
5864        // Load-bearing byte-parity pin: for every arm in
5865        // [`crate::dialeto::CaixaDialeto::ALL`], the
5866        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
5867        // partition must agree with the lifted
5868        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
5869        // typed predicate — i.e. from_lisp raises
5870        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
5871        // `d.is_molde_family()` returns `true`, and does NOT raise
5872        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
5873        // predicate returns `false` (the arm's source falls through to
5874        // the derive — parses cleanly on
5875        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
5876        // [`LeituraError::Leitura`] on
5877        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
5878        //
5879        // Pre-lift the gate hand-rolled a three-arm match
5880        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
5881        // whose `foreign =>` wildcard expressed no compile-time link
5882        // back to the substrate primitive's arm-family; a future fifth
5883        // dialect the [`crate::dialeto`] module doc's "third dialect"
5884        // hazard actualises would fall silently onto the wildcard
5885        // regardless of whether it belonged to the `defmolde` family or
5886        // to a distinct `defcaixa`-family. Post-lift the partition
5887        // resolves through
5888        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
5889        // typed dispatch, and this pin refuses any future regression
5890        // that silently split the from_lisp partition from the typed
5891        // predicate — the two paths now migrate as one on any future
5892        // arm addition.
5893        //
5894        // Sibling in shape to the peer
5895        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
5896        // (e9d2315) that pins the same byte-parity between
5897        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
5898        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
5899        // `== "defmolde"` classifier — extends the discipline from the
5900        // two paths within the [`crate::dialeto`] primitive onto the
5901        // third external consumer of the `defmolde`-family partition
5902        // (the [`Caixa::from_lisp`] gate that raises
5903        // [`LeituraError::DialetoEstrangeiro`]).
5904        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
5905            (
5906                crate::dialeto::CaixaDialeto::Pacote,
5907                r#"
5908                  (defcaixa
5909                    :nome   "checkout"
5910                    :versao "0.1.0"
5911                    :kind   Biblioteca
5912                    :edicao "2026"
5913                    :descricao "canonical Pacote source"
5914                    :autores ()
5915                    :etiquetas ()
5916                    :deps ()
5917                    :deps-dev ()
5918                    :bibliotecas ("lib/checkout.lisp"))
5919                "#,
5920            ),
5921            (
5922                crate::dialeto::CaixaDialeto::Molde,
5923                r#"
5924                  (defcaixa
5925                    :name "base64"
5926                    :kind :Biblioteca
5927                    :ecosystem :rust-single-crate
5928                    :package {:name "base64" :version "0.22.1"}
5929                    :workflows [:auto-release])
5930                "#,
5931            ),
5932            (
5933                crate::dialeto::CaixaDialeto::MoldePosicional,
5934                r#"
5935                  (defcaixa todoku-go
5936                    :kind :Biblioteca
5937                    :ecosystem :go
5938                    :package {:name "todoku-go" :version "0.3.0"})
5939                "#,
5940            ),
5941            (
5942                crate::dialeto::CaixaDialeto::Desconhecido,
5943                r#"(defcaixa :licenca "MIT")"#,
5944            ),
5945        ];
5946
5947        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
5948        // must appear in the fixture table so the pin's arm-set stays
5949        // synchronised with the enum's arm-set. Fails at test time if a
5950        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
5951        // (with a corresponding `is_molde_family` return) forgot to
5952        // extend this fixture table with a canonical source for the new
5953        // arm — the pin cannot cover an arm it has no source for.
5954        for &expected in crate::dialeto::CaixaDialeto::ALL {
5955            assert!(
5956                fixtures.iter().any(|(d, _)| *d == expected),
5957                "fixture table must carry a canonical source for every \
5958                 CaixaDialeto arm; missing: {expected:?}"
5959            );
5960        }
5961
5962        for &(expected_dialect, src) in fixtures {
5963            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
5964                panic!(
5965                    "fixture source for {expected_dialect:?} must classify \
5966                     cleanly, got err: {err:?}"
5967                )
5968            });
5969            assert_eq!(
5970                classified, expected_dialect,
5971                "fixture source for {expected_dialect:?} must classify as \
5972                 {expected_dialect:?} (drift here defeats the byte-parity \
5973                 pin below — a source labelled for one arm but classifying \
5974                 as another would silently satisfy or violate the pin for \
5975                 the wrong reason)"
5976            );
5977
5978            let outcome = Caixa::from_lisp(src);
5979            match (expected_dialect.is_molde_family(), &outcome) {
5980                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
5981                    assert_eq!(
5982                        *dialeto, expected_dialect,
5983                        "DialetoEstrangeiro must carry the same typed arm \
5984                         the classifier returned — a drift here would let \
5985                         from_lisp raise the error while pointing at the \
5986                         wrong dialect (e.g. rejecting a \
5987                         MoldePosicional source as Molde). arm: \
5988                         {expected_dialect:?}"
5989                    );
5990                }
5991                (true, other) => panic!(
5992                    "arm {expected_dialect:?} has is_molde_family() = true \
5993                     so from_lisp must raise DialetoEstrangeiro carrying \
5994                     {expected_dialect:?}; got: {other:?}"
5995                ),
5996                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
5997                    "arm {expected_dialect:?} has is_molde_family() = false \
5998                     so from_lisp must NOT raise DialetoEstrangeiro; got \
5999                     one carrying: {dialeto:?}. This means the typed \
6000                     predicate and the from_lisp partition disagree on \
6001                     this arm — exactly the drift this pin refuses."
6002                ),
6003                (false, _) => {
6004                    // A non-molde arm's source falls through to the
6005                    // derive: Pacote sources parse to Ok(_); Desconhecido
6006                    // sources surface as LeituraError::Leitura from the
6007                    // derive's own unknown-keyword rejection. Either
6008                    // shape is acceptable here — the pin's promise is
6009                    // narrower: "no DialetoEstrangeiro on
6010                    // is_molde_family() == false".
6011                }
6012            }
6013        }
6014    }
6015
6016    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
6017
6018    #[test]
6019    fn limits_round_trip_via_json() {
6020        use crate::LimitsSpec;
6021        use std::time::Duration;
6022        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6023        c.limits = Some(LimitsSpec {
6024            memory: Some(64 * 1024 * 1024),
6025            fuel: Some(1_000_000),
6026            wall_clock: Some(Duration::from_secs(30)),
6027            cpu: Some(500),
6028        });
6029        let json = serde_json::to_string(&c).unwrap();
6030        assert!(json.contains("\"limits\""));
6031        assert!(json.contains("\"64MiB\""));
6032        assert!(json.contains("\"30s\""));
6033        assert!(json.contains("\"500m\""));
6034        let back: Caixa = serde_json::from_str(&json).unwrap();
6035        assert_eq!(c.limits, back.limits);
6036    }
6037
6038    #[test]
6039    fn behavior_round_trip_via_json() {
6040        use crate::BehaviorSpec;
6041        use std::path::PathBuf;
6042        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6043        c.behavior = Some(BehaviorSpec {
6044            on_init: Some(PathBuf::from("lib/init.lisp")),
6045            on_call: Some(PathBuf::from("lib/handlers.lisp")),
6046            ..Default::default()
6047        });
6048        let json = serde_json::to_string(&c).unwrap();
6049        let back: Caixa = serde_json::from_str(&json).unwrap();
6050        assert_eq!(c.behavior, back.behavior);
6051    }
6052
6053    #[test]
6054    fn upgrade_from_round_trip_via_json() {
6055        use crate::{UpgradeFromEntry, UpgradeInstruction};
6056        use std::path::PathBuf;
6057        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6058        c.upgrade_from = vec![UpgradeFromEntry {
6059            from: "0.1.0".into(),
6060            instructions: vec![
6061                UpgradeInstruction::LoadModule {
6062                    module: "demo".into(),
6063                },
6064                UpgradeInstruction::StateChange {
6065                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
6066                },
6067                UpgradeInstruction::SoftPurge {
6068                    module: "demo-old".into(),
6069                },
6070            ],
6071        }];
6072        let json = serde_json::to_string(&c).unwrap();
6073        let back: Caixa = serde_json::from_str(&json).unwrap();
6074        assert_eq!(c.upgrade_from, back.upgrade_from);
6075    }
6076
6077    #[test]
6078    fn supervisor_view_returns_typed_shape() {
6079        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
6080        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
6081        c.kind = CaixaKind::Supervisor;
6082        c.bibliotecas.clear();
6083        c.estrategia = Some(RestartStrategy::OneForOne);
6084        c.max_restarts = Some(5);
6085        c.restart_window = Some("60s".into());
6086        c.children = vec![ChildSpec {
6087            caixa: "worker".into(),
6088            versao: "^0.1".into(),
6089            restart: RestartPolicy::Permanent,
6090        }];
6091        let view = c.supervisor_view().expect("Supervisor kind has a view");
6092        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
6093        assert_eq!(view.max_restarts, 5);
6094        assert_eq!(
6095            view.restart_window,
6096            Some(std::time::Duration::from_secs(60))
6097        );
6098        assert_eq!(view.children.len(), 1);
6099        view.validate().unwrap();
6100    }
6101
6102    #[test]
6103    fn supervisor_view_none_for_non_supervisor_kinds() {
6104        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6105        assert!(c.supervisor_view().is_none());
6106    }
6107
6108    #[test]
6109    fn declared_mesh_slots_empty_for_bare_caixa() {
6110        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6111        assert!(c.declared_mesh_slots().is_empty());
6112    }
6113
6114    #[test]
6115    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
6116        use crate::{Entrada, Membro};
6117        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6118        // Set a non-adjacent pair (:membros + :entrada) to pin that the
6119        // canonical declaration order is preserved regardless of which
6120        // subset is populated.
6121        c.membros = vec![Membro {
6122            caixa: "a".into(),
6123            versao: "^0.1".into(),
6124        }];
6125        c.entrada = Some(Entrada {
6126            host: "x.example.com".into(),
6127            para: "a".into(),
6128            paths: vec![],
6129            port: 8080,
6130        });
6131        assert_eq!(
6132            c.declared_mesh_slots(),
6133            vec![
6134                crate::render::M3_AUTHOR_KEY_MEMBROS,
6135                crate::render::M3_AUTHOR_KEY_ENTRADA,
6136            ]
6137        );
6138    }
6139
6140    #[test]
6141    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6142        // Scalar-value pin: the five author-facing kebab-case labels the
6143        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
6144        // mesh slot axis, one arm per typed slot. Mirrors the peer
6145        // scalar-value pin the sibling
6146        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
6147        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
6148        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
6149        // carry (f49c8b0), so both altitudes of the typed-slot algebra
6150        // (per-Servico M2 + per-Aplicacao M3) share the same
6151        // "one canonical byte-string per arm" discipline. A future
6152        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
6153        // `:politicas` → `:policies`, `:placement` → `:distribution`,
6154        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
6155        // and every consumer that reaches for the label picks it up at
6156        // build time rather than at runtime as a downstream mismatch.
6157        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
6158        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
6159        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
6160        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
6161        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
6162    }
6163
6164    #[test]
6165    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
6166        // Production-through-const pin: the five per-arm labels the
6167        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
6168        // `Vec` route through the lifted
6169        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
6170        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
6171        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
6172        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
6173        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
6174        // declaration order. A future re-order or drift at the tagger
6175        // (a rename that reaches the tagger but not the const, or vice
6176        // versa) surfaces here at build time rather than at runtime as
6177        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
6178        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6179        // commit. Mirror of the peer
6180        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6181        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
6182        // axis.
6183        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
6184        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6185        c.membros = vec![Membro {
6186            caixa: "a".into(),
6187            versao: "^0.1".into(),
6188        }];
6189        c.contratos = vec![WitContract {
6190            de: "a".into(),
6191            para: "a".into(),
6192            wit: "wasi:http/proxy".into(),
6193            endpoint: Some("/x".into()),
6194            subject: None,
6195            slot: None,
6196        }];
6197        c.politicas = Some(MeshPolicy::default());
6198        c.placement = Some(Placement {
6199            estrategia: PlacementStrategy::Replicated,
6200            clusters: vec!["rio".into()],
6201            affinity: None,
6202            shard_key: None,
6203        });
6204        c.entrada = Some(Entrada {
6205            host: "x.example.com".into(),
6206            para: "a".into(),
6207            paths: vec![],
6208            port: 8080,
6209        });
6210        assert_eq!(
6211            c.declared_mesh_slots(),
6212            vec![
6213                crate::render::M3_AUTHOR_KEY_MEMBROS,
6214                crate::render::M3_AUTHOR_KEY_CONTRATOS,
6215                crate::render::M3_AUTHOR_KEY_POLITICAS,
6216                crate::render::M3_AUTHOR_KEY_PLACEMENT,
6217                crate::render::M3_AUTHOR_KEY_ENTRADA,
6218            ]
6219        );
6220    }
6221
6222    #[test]
6223    fn declared_supervisor_slots_empty_for_bare_caixa() {
6224        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6225        assert!(c.declared_supervisor_slots().is_empty());
6226    }
6227
6228    #[test]
6229    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
6230        use crate::RestartStrategy;
6231        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6232        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
6233        // that the canonical declaration order is preserved regardless
6234        // of which subset is populated.
6235        c.estrategia = Some(RestartStrategy::OneForOne);
6236        c.restart_window = Some("60s".into());
6237        assert_eq!(
6238            c.declared_supervisor_slots(),
6239            vec![
6240                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6241                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6242            ]
6243        );
6244    }
6245
6246    #[test]
6247    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6248        // Scalar-value pin: the four author-facing kebab-case labels the
6249        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
6250        // supervision-tree slot axis, one arm per typed slot. Mirrors the
6251        // peer scalar-value pins the sibling
6252        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
6253        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
6254        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
6255        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
6256        // top-level M3 slot consts carry, so all three kind-scoped
6257        // typed-slot-family author-facing-label axes route through one
6258        // canonical per-arm declaration. A future rebrand
6259        // (`:estrategia` → `:strategy` for English uniformity,
6260        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
6261        // `MaxIntensity` name, `:restart-window` → `:period` matching
6262        // OTP's `Period` name, `:children` → `:workers` matching Elixir
6263        // idiom) lands as an edit to exactly one const, and every
6264        // consumer that reaches for the label picks it up at build time
6265        // rather than at runtime as a downstream mismatch.
6266        assert_eq!(
6267            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6268            ":estrategia"
6269        );
6270        assert_eq!(
6271            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6272            ":max-restarts"
6273        );
6274        assert_eq!(
6275            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6276            ":restart-window"
6277        );
6278        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
6279    }
6280
6281    #[test]
6282    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
6283        // Production-through-const pin: the four per-arm labels the
6284        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
6285        // return `Vec` route through the lifted
6286        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
6287        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
6288        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
6289        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
6290        // canonical declaration order. A future re-order or drift at the
6291        // tagger (a rename that reaches the tagger but not the const, or
6292        // vice versa) surfaces here at build time rather than at runtime
6293        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
6294        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6295        // commit. Mirror of the peer
6296        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6297        // (f49c8b0) and
6298        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
6299        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
6300        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
6301        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6302        c.estrategia = Some(RestartStrategy::OneForOne);
6303        c.max_restarts = Some(5);
6304        c.restart_window = Some("60s".into());
6305        c.children = vec![ChildSpec {
6306            caixa: "worker".into(),
6307            versao: "^0.1".into(),
6308            restart: RestartPolicy::Permanent,
6309        }];
6310        assert_eq!(
6311            c.declared_supervisor_slots(),
6312            vec![
6313                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6314                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6315                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6316                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
6317            ]
6318        );
6319    }
6320
6321    #[test]
6322    fn declared_servico_slots_empty_for_bare_caixa() {
6323        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6324        assert!(c.declared_servico_slots().is_empty());
6325    }
6326
6327    #[test]
6328    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
6329        use crate::{UpgradeFromEntry, UpgradeInstruction};
6330        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6331        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
6332        // the canonical declaration order is preserved regardless of
6333        // which subset is populated.
6334        c.limits = Some(crate::LimitsSpec {
6335            fuel: Some(1_000_000),
6336            ..Default::default()
6337        });
6338        c.upgrade_from = vec![UpgradeFromEntry {
6339            from: "0.1.0".into(),
6340            instructions: vec![UpgradeInstruction::Restart],
6341        }];
6342        assert_eq!(
6343            c.declared_servico_slots(),
6344            vec![
6345                crate::render::M2_AUTHOR_KEY_LIMITS,
6346                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6347            ]
6348        );
6349    }
6350
6351    #[test]
6352    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6353        // Scalar-value pin: the three author-facing kebab-case labels
6354        // the `(defcaixa … :<slot> (…))` surface admits on the M2
6355        // top-level slot axis, one arm per typed slot. Mirrors the peer
6356        // scalar-value pin the sibling renderer-side
6357        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
6358        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
6359        // consts carry, so both halves of the M2 top-level slot dual
6360        // axis (author-facing kebab-case label + renderer-side
6361        // camelCase overlay-container wire key) route through one
6362        // canonical per-arm declaration. A future rebrand
6363        // (`:limits` → `:sandbox` matching Lunatic per-process
6364        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
6365        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
6366        // matching Erlang's verbatim appup name) lands as an edit to
6367        // exactly one const, and every consumer that reaches for the
6368        // label picks it up at build time rather than at runtime as a
6369        // downstream mismatch.
6370        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
6371        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
6372        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
6373    }
6374
6375    #[test]
6376    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
6377        // Production-through-const pin: the three per-arm labels the
6378        // [`Caixa::declared_servico_slots`] tagger pushes onto its
6379        // return `Vec` route through the lifted
6380        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
6381        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
6382        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
6383        // declaration order. A future re-order or drift at the tagger
6384        // (a rename that reaches the tagger but not the const, or vice
6385        // versa) surfaces here at build time rather than at runtime as
6386        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
6387        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6388        // commit. Mirror of the peer
6389        // [`crate::behavior::BehaviorSpec::declared_slots`] production
6390        // tagger pin (889dc18) on the sibling per-callback axis.
6391        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
6392        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6393        c.limits = Some(crate::LimitsSpec {
6394            fuel: Some(1_000_000),
6395            ..Default::default()
6396        });
6397        c.behavior = Some(BehaviorSpec {
6398            on_init: Some(PathBuf::from("lib/init.lisp")),
6399            ..Default::default()
6400        });
6401        c.upgrade_from = vec![UpgradeFromEntry {
6402            from: "0.1.0".into(),
6403            instructions: vec![UpgradeInstruction::Restart],
6404        }];
6405        assert_eq!(
6406            c.declared_servico_slots(),
6407            vec![
6408                crate::render::M2_AUTHOR_KEY_LIMITS,
6409                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
6410                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6411            ]
6412        );
6413    }
6414
6415    #[test]
6416    fn existing_manifests_unaffected_by_new_optional_slots() {
6417        // Regression test: a caixa.lisp authored before M2 typed slots
6418        // should still parse + serialize cleanly. The bare `defcaixa`
6419        // emitted by `Caixa::template` has none of the new fields.
6420        let src = Caixa::template("legacy");
6421        let c = Caixa::from_lisp(&src).unwrap();
6422        assert!(c.limits.is_none());
6423        assert!(c.behavior.is_none());
6424        assert!(c.upgrade_from.is_empty());
6425        assert!(c.estrategia.is_none());
6426        assert!(c.children.is_empty());
6427
6428        // And to_lisp emits a manifest with the new slots in the
6429        // empty/default state — round-trippable.
6430        let emitted = c.to_lisp();
6431        let back = Caixa::from_lisp(&emitted).unwrap();
6432        assert_eq!(c, back);
6433    }
6434
6435    #[test]
6436    fn validate_deps_accepts_canonical_caixa() {
6437        // Positive control: the bare template — zero deps, zero
6438        // deps_dev — passes the gate trivially. A future axis added to
6439        // `Dep::validate` mustn't regress an empty-deps caixa to a
6440        // build error.
6441        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6442        c.validate_deps().unwrap();
6443    }
6444
6445    #[test]
6446    fn validate_deps_rejects_invalid_versao_in_deps() {
6447        // Fail-before-pass-after pin: a malformed `:deps :versao`
6448        // surfaces at validate_deps() time, not at lacre-resolve time.
6449        // Mirrors `rejects_invalid_membro_versao_requirement` and
6450        // `validate_rejects_invalid_child_versao_requirement` on the
6451        // other two `:versao` axes.
6452        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6453        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
6454        let err = c.validate_deps().unwrap_err();
6455        assert!(
6456            matches!(
6457                err,
6458                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6459                    if nome == "caixa-teia" && versao == "^bad-version"
6460            ),
6461            "got {err:?}"
6462        );
6463    }
6464
6465    #[test]
6466    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
6467        // Parity pin: `:deps-dev` must run through the same per-entry
6468        // validator as `:deps` — a typo in either axis surfaces the
6469        // same diagnostic. Without this leg, `:deps-dev` would be a
6470        // second-class citizen of the typed surface and an author
6471        // could land a build that passes validate_deps but fails at
6472        // `feira lock`-time when the dev-dep is resolved for a test
6473        // build.
6474        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6475        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
6476        let err = c.validate_deps().unwrap_err();
6477        assert!(
6478            matches!(
6479                err,
6480                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6481                    if nome == "tatara-check" && versao == "^^0.1"
6482            ),
6483            "got {err:?}"
6484        );
6485    }
6486
6487    #[test]
6488    fn validate_deps_runs_deps_before_deps_dev() {
6489        // Order pin: when both lists carry typos, the `:deps`
6490        // diagnostic surfaces first. The author's mental model is
6491        // "runtime deps are load-bearing; dev deps are scaffolding";
6492        // surfacing the runtime axis first matches that hierarchy.
6493        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6494        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
6495        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
6496        let err = c.validate_deps().unwrap_err();
6497        assert!(
6498            matches!(
6499                err,
6500                crate::dep::DepError::VersaoInvalid { ref nome, .. }
6501                    if nome == "runtime-dep"
6502            ),
6503            "expected `:deps` typo to surface first, got {err:?}"
6504        );
6505    }
6506
6507    #[test]
6508    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
6509        // Positive control sweep across both lists. Pin every
6510        // canonical Cargo-shaped form so a future tightening of the
6511        // accepted set surfaces here as a test failure (parity with
6512        // `accepts_canonical_membro_versao_forms` and
6513        // `validate_accepts_canonical_child_versao_forms`).
6514        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6515        c.deps = vec![
6516            Dep::simple("caret", "^0.1"),
6517            Dep::simple("tilde", "~0.1.2"),
6518            Dep::simple("exact", "0.1.0"),
6519            Dep::simple("wildcard", "*"),
6520            Dep::simple("multi-range", ">=0.1, <2"),
6521        ];
6522        c.deps_dev = vec![
6523            Dep::simple("dev-caret", "^0.1"),
6524            Dep::simple("dev-wildcard", "*"),
6525        ];
6526        c.validate_deps().unwrap();
6527    }
6528
6529    #[test]
6530    fn validate_deps_diagnostic_carries_offending_dep() {
6531        // Diagnostic-shape pin: the error names the offending entry's
6532        // `:nome` + `:versao` verbatim and carries a non-empty
6533        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
6534        // run can render the diagnostic without re-parsing.
6535        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6536        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
6537        let err = c.validate_deps().unwrap_err();
6538        let crate::dep::DepError::VersaoInvalid {
6539            nome,
6540            versao,
6541            reason,
6542        } = err
6543        else {
6544            panic!("expected VersaoInvalid, got other variant");
6545        };
6546        assert_eq!(nome, "caixa-teia");
6547        assert_eq!(versao, "not-a-req");
6548        assert!(
6549            !reason.is_empty(),
6550            "VersaoInvalid `reason` must carry the parser's wording verbatim"
6551        );
6552    }
6553
6554    #[test]
6555    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
6556        // Cross-axis pin: `validate_deps` walks both :deps and
6557        // :deps-dev through `Dep::validate`, and the new fonte gate
6558        // (`:tag` + `:branch` both set — the canonical "pin drift"
6559        // footgun) must surface from the :deps-dev arm with the
6560        // offending entry's :nome named. Pin the :deps-dev arm
6561        // explicitly so a future shortcut that only walks :deps
6562        // surfaces here as a regression.
6563        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6564        c.deps_dev = vec![Dep {
6565            nome: "dev-only".into(),
6566            versao: "^0.1".into(),
6567            fonte: Some(crate::DepSource::Git {
6568                repo: "github:p/x".into(),
6569                tag: Some("v1".into()),
6570                rev: None,
6571                branch: Some("main".into()),
6572            }),
6573            opcional: false,
6574            caracteristicas: vec![],
6575        }];
6576        let err = c.validate_deps().unwrap_err();
6577        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
6578            panic!("expected FontePinAmbiguous from :deps-dev walk");
6579        };
6580        assert_eq!(nome, "dev-only");
6581        assert!(pins.contains(":tag") && pins.contains(":branch"));
6582    }
6583
6584    #[test]
6585    fn validate_deps_rejects_empty_repo_in_deps() {
6586        // Parity pin on the :deps arm: an empty :repo on the runtime
6587        // deps list surfaces the same FonteRepoEmpty diagnostic the
6588        // dep.rs per-entry tests pin, naming the offending entry.
6589        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6590        c.deps = vec![Dep {
6591            nome: "runtime".into(),
6592            versao: "^0.1".into(),
6593            fonte: Some(crate::DepSource::Git {
6594                repo: String::new(),
6595                tag: Some("v1".into()),
6596                rev: None,
6597                branch: None,
6598            }),
6599            opcional: false,
6600            caracteristicas: vec![],
6601        }];
6602        let err = c.validate_deps().unwrap_err();
6603        assert!(
6604            matches!(
6605                err,
6606                crate::dep::DepError::FonteRepoEmpty { ref nome }
6607                    if nome == "runtime"
6608            ),
6609            "got {err:?}"
6610        );
6611    }
6612
6613    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
6614
6615    #[test]
6616    fn validate_deps_rejects_duplicate_nome_in_deps() {
6617        // Fail-before-pass-after pin: two `:deps` entries naming the same
6618        // caixa carry two `:versao` / `:fonte` / feature triples that the
6619        // caixa-resolver's lacre pipeline collapses (the second silently
6620        // overwrites the first at `concrete_versao`-resolve time). The
6621        // gate surfaces the duplicate at validate-time, naming the
6622        // offending caixa + the list, before the resolver-side silent
6623        // drop. Mirrors the peer typed-graph duplicate gates
6624        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
6625        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6626        c.deps = vec![
6627            Dep::simple("caixa-teia", "^0.1"),
6628            Dep::simple("caixa-teia", "^0.2"),
6629        ];
6630        let err = c.validate_deps().unwrap_err();
6631        assert!(
6632            matches!(
6633                err,
6634                crate::dep::DepError::DuplicateNome { ref nome, list }
6635                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6636            ),
6637            "got {err:?}"
6638        );
6639    }
6640
6641    #[test]
6642    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
6643        // Parity pin: `:deps-dev` runs through the same per-list
6644        // duplicate check as `:deps` — neither axis is a second-class
6645        // citizen of the set-not-multiset discipline.
6646        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6647        c.deps_dev = vec![
6648            Dep::simple("tatara-check", "*"),
6649            Dep::simple("tatara-check", "^0.1"),
6650        ];
6651        let err = c.validate_deps().unwrap_err();
6652        assert!(
6653            matches!(
6654                err,
6655                crate::dep::DepError::DuplicateNome { ref nome, list }
6656                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
6657            ),
6658            "got {err:?}"
6659        );
6660    }
6661
6662    #[test]
6663    fn validate_deps_accepts_cross_list_same_nome() {
6664        // The Cargo `[dependencies]` + `[dev-dependencies]` override
6665        // convention is preserved: a name appearing in *both* lists is
6666        // valid (the dev-pin overrides at test/dev time). Only
6667        // within-list duplicates are structurally incoherent — pin the
6668        // permissive cross-list semantics so a future shortcut that
6669        // collapses the two seen-sets into one surfaces here as a test
6670        // failure.
6671        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6672        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
6673        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
6674        c.validate_deps().unwrap();
6675    }
6676
6677    #[test]
6678    fn validate_deps_accepts_distinct_nome_in_both_lists() {
6679        // Positive control: distinct names within each list pass — the
6680        // gate's identity element on the canonical authoring shape.
6681        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6682        c.deps = vec![
6683            Dep::simple("caixa-teia", "^0.1"),
6684            Dep::simple("pleme-mesh", "*"),
6685        ];
6686        c.deps_dev = vec![
6687            Dep::simple("tatara-check", "*"),
6688            Dep::simple("dev-shim", "^0.1"),
6689        ];
6690        c.validate_deps().unwrap();
6691    }
6692
6693    #[test]
6694    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
6695        // Diagnostic-precedence pin: a malformed `:versao` on the
6696        // duplicating entry surfaces its narrower `VersaoInvalid`
6697        // diagnostic first, before the cross-entry duplicate gate fires
6698        // — the canonical "per-entry shape before cross-entry uniqueness"
6699        // precedence every peer set-not-multiset gate establishes
6700        // (`*_invalid_fires_before_duplicate_check` pins on
6701        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
6702        // `validate_upgrade_from`).
6703        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6704        c.deps = vec![
6705            Dep::simple("caixa-teia", "^0.1"),
6706            Dep::simple("caixa-teia", "^bad-version"),
6707        ];
6708        let err = c.validate_deps().unwrap_err();
6709        assert!(
6710            matches!(
6711                err,
6712                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6713                    if nome == "caixa-teia" && versao == "^bad-version"
6714            ),
6715            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
6716        );
6717    }
6718
6719    #[test]
6720    fn validate_deps_duplicate_diagnostic_names_first_collision() {
6721        // First-collision determinism pin: with three entries naming the
6722        // same caixa, the first colliding pair surfaces — not the last.
6723        // Mirrors the peer first-collision posture on every
6724        // duplicate-target gate
6725        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
6726        // — the second entry is the first collision; this gate uses the
6727        // same shape: the second entry's `:nome` lands in the diagnostic
6728        // because `seen.insert(first.nome)` already populated the set).
6729        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6730        c.deps = vec![
6731            Dep::simple("caixa-teia", "^0.1"),
6732            Dep::simple("caixa-teia", "^0.2"),
6733            Dep::simple("caixa-teia", "^0.3"),
6734        ];
6735        let err = c.validate_deps().unwrap_err();
6736        // The diagnostic carries the offending caixa name; the
6737        // implementation surfaces on the *second* entry (the first
6738        // collision), so the test pins the `:nome` value.
6739        assert!(
6740            matches!(
6741                err,
6742                crate::dep::DepError::DuplicateNome { ref nome, list }
6743                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6744            ),
6745            "got {err:?}"
6746        );
6747    }
6748
6749    #[test]
6750    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
6751        // Cross-list precedence pin: when both lists carry duplicates,
6752        // the `:deps` diagnostic surfaces first — same author-mental-
6753        // model ordering the `validate_deps_runs_deps_before_deps_dev`
6754        // pin establishes for malformed `:versao` (runtime axis before
6755        // dev axis).
6756        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6757        c.deps = vec![
6758            Dep::simple("runtime-dep", "^0.1"),
6759            Dep::simple("runtime-dep", "^0.2"),
6760        ];
6761        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
6762        let err = c.validate_deps().unwrap_err();
6763        assert!(
6764            matches!(
6765                err,
6766                crate::dep::DepError::DuplicateNome { ref nome, list }
6767                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6768            ),
6769            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
6770        );
6771    }
6772
6773    #[test]
6774    fn validate_deps_empty_lists_pass_duplicate_gate() {
6775        // Empty-set identity pin: the bare template (zero deps, zero
6776        // deps_dev) passes the duplicate gate as the gate's identity
6777        // element. A future tighten that conflates "empty" with
6778        // "missing" would regress this baseline.
6779        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6780        c.validate_deps().unwrap();
6781    }
6782
6783    #[test]
6784    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
6785        // Diagnostic-shape pin: the `list:` field tags which list the
6786        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
6787        // `feira lint` run can route the author to the right block in
6788        // their caixa.lisp without re-deriving the list from context.
6789        // Same self-locating shape every peer per-axis diagnostic
6790        // already exposes.
6791        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6792        c.deps_dev = vec![
6793            Dep::simple("dev-thing", "*"),
6794            Dep::simple("dev-thing", "^0.1"),
6795        ];
6796        let err = c.validate_deps().unwrap_err();
6797        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
6798            panic!("expected DuplicateNome from :deps-dev walk");
6799        };
6800        assert_eq!(nome, "dev-thing");
6801        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
6802    }
6803
6804    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
6805
6806    #[test]
6807    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
6808        // Thread-through pin on `:deps`: the per-entry
6809        // `Dep::validate_caracteristicas` gate fires inside
6810        // `Caixa::validate_deps`'s linear walk, so a malformed feature
6811        // list on any `:deps` entry surfaces as a `DepError` from
6812        // `validate_deps` — the same reachability shape every per-entry
6813        // `Dep::validate` arm threads through. Without this pin a future
6814        // shortcut that skips the per-entry `Dep::validate` call on the
6815        // cross-entry-uniqueness path would mask the within-entry
6816        // `:caracteristicas` gates.
6817        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6818        c.deps = vec![Dep {
6819            nome: "caixa-teia".into(),
6820            versao: "^0.1".into(),
6821            fonte: None,
6822            opcional: false,
6823            caracteristicas: vec!["http".into(), "http".into()],
6824        }];
6825        let err = c.validate_deps().unwrap_err();
6826        let crate::dep::DepError::CaracteristicaDuplicate {
6827            nome,
6828            caracteristica,
6829        } = err
6830        else {
6831            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
6832        };
6833        assert_eq!(nome, "caixa-teia");
6834        assert_eq!(caracteristica, "http");
6835    }
6836
6837    #[test]
6838    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
6839        // Peer thread-through pin on `:deps-dev`: same reachability as
6840        // the `:deps` arm above, on the dev-only authoring axis. Pins
6841        // that the `validate_deps` walk visits both lists' per-entry
6842        // gates uniformly. The empty-feature arm carries here so both
6843        // new `:caracteristicas` arms are surfaced via at least one
6844        // `validate_deps` thread-through.
6845        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6846        c.deps_dev = vec![Dep {
6847            nome: "caixa-teia".into(),
6848            versao: "^0.1".into(),
6849            fonte: None,
6850            opcional: false,
6851            caracteristicas: vec![String::new()],
6852        }];
6853        let err = c.validate_deps().unwrap_err();
6854        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
6855            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
6856        };
6857        assert_eq!(nome, "caixa-teia");
6858    }
6859
6860    #[test]
6861    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
6862        // Thread-through pin on `:deps`: the per-entry
6863        // `Dep::validate_caracteristicas` value-shape gate (lifted via
6864        // `crate::render::is_cargo_feature_name`) fires inside
6865        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
6866        // a structurally invalid feature name on any `:deps` entry
6867        // surfaces as `DepError::CaracteristicaInvalid` from
6868        // `validate_deps` — the same reachability shape every per-entry
6869        // `Dep::validate` arm threads through. Without this pin a
6870        // future shortcut that skips the per-entry `Dep::validate` call
6871        // on the cross-entry-uniqueness path would mask the within-
6872        // entry `:caracteristicas` value-shape gate.
6873        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6874        c.deps = vec![Dep {
6875            nome: "caixa-teia".into(),
6876            versao: "^0.1".into(),
6877            fonte: None,
6878            opcional: false,
6879            caracteristicas: vec!["+http".into()],
6880        }];
6881        let err = c.validate_deps().unwrap_err();
6882        let crate::dep::DepError::CaracteristicaInvalid {
6883            nome,
6884            caracteristica,
6885            ..
6886        } = err
6887        else {
6888            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
6889        };
6890        assert_eq!(nome, "caixa-teia");
6891        assert_eq!(caracteristica, "+http");
6892    }
6893
6894    #[test]
6895    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
6896        // Peer thread-through pin on `:deps-dev`: same reachability as
6897        // the `:deps` arm above, on the dev-only authoring axis. The
6898        // `http/json` shape carries here so the segment-separator
6899        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
6900        // confusion footgun) is surfaced via the cross-entry walk too —
6901        // pinning that the `:deps-dev` list visits the same per-entry
6902        // value-shape gate as the `:deps` list.
6903        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6904        c.deps_dev = vec![Dep {
6905            nome: "caixa-teia".into(),
6906            versao: "^0.1".into(),
6907            fonte: None,
6908            opcional: false,
6909            caracteristicas: vec!["http/json".into()],
6910        }];
6911        let err = c.validate_deps().unwrap_err();
6912        let crate::dep::DepError::CaracteristicaInvalid {
6913            nome,
6914            caracteristica,
6915            ..
6916        } = err
6917        else {
6918            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
6919        };
6920        assert_eq!(nome, "caixa-teia");
6921        assert_eq!(caracteristica, "http/json");
6922    }
6923
6924    #[test]
6925    fn to_lisp_preserves_deps() {
6926        let src = r#"
6927(defcaixa
6928  :nome "x"
6929  :versao "0.1.0"
6930  :kind Biblioteca
6931  :deps ((:nome "a" :versao "^0.1")
6932         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
6933"#;
6934        let c1 = Caixa::from_lisp(src).unwrap();
6935        let emitted = c1.to_lisp();
6936        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
6937        assert_eq!(c1.deps, c2.deps);
6938    }
6939
6940    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
6941
6942    fn caixa_with_nome(nome: &str) -> Caixa {
6943        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
6944        c.nome = nome.to_string();
6945        c
6946    }
6947
6948    #[test]
6949    fn validate_nome_accepts_canonical_template() {
6950        // Positive control: the bare `feira init`-style template's
6951        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
6952        // not regress this baseline shape. A future tightening of the
6953        // accepted set surfaces here as a test failure first.
6954        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6955        c.validate_nome().unwrap();
6956    }
6957
6958    #[test]
6959    fn validate_nome_accepts_canonical_forms() {
6960        // Positive-set sweep: each realistic caixa-name shape the K8s
6961        // apiserver accepts as a `metadata.name` label must pass —
6962        // single-word, hyphen-joined, version-suffixed, single-char,
6963        // two-char, digit-start (DNS-1123 allows this; the stricter
6964        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
6965        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
6966        // the peer member-name axis.
6967        for nome in [
6968            "checkout",
6969            "cart-v2",
6970            "a",
6971            "db",
6972            "3rd-party-shim",
6973            "payment-retry",
6974            "0",
6975        ] {
6976            caixa_with_nome(nome)
6977                .validate_nome()
6978                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
6979        }
6980    }
6981
6982    #[test]
6983    fn validate_nome_rejects_empty() {
6984        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
6985        // an empty `:nome` (the derive macro stores the raw String);
6986        // the gate's empty arm names the offending axis with a narrower
6987        // diagnostic than the `NomeInvalid` parse arm would emit.
6988        let c = caixa_with_nome("");
6989        let err = c.validate_nome().unwrap_err();
6990        assert_eq!(err, ManifestError::NomeEmpty);
6991    }
6992
6993    #[test]
6994    fn validate_nome_rejects_uppercase() {
6995        // The canonical "I copied the TitleCase display name verbatim"
6996        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
6997        // admission on every derived artifact (Helm chart, ComputeUnit,
6998        // CNP, HTTPRoute, label values); the gate moves the diagnostic
6999        // to the source `caixa.lisp` and the reason suggests the
7000        // lowercased fix verbatim.
7001        let c = caixa_with_nome("MyApp");
7002        let err = c.validate_nome().unwrap_err();
7003        let ManifestError::NomeInvalid { nome, reason } = err else {
7004            panic!("expected NomeInvalid for uppercase :nome");
7005        };
7006        assert_eq!(nome, "MyApp");
7007        assert!(
7008            reason.contains("uppercase") && reason.contains("myapp"),
7009            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
7010        );
7011    }
7012
7013    #[test]
7014    fn validate_nome_rejects_underscore() {
7015        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
7016        // `_`; the apiserver rejects on admission across every derived
7017        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
7018        // and `:children :caixa` (31bfa43).
7019        let c = caixa_with_nome("my_app");
7020        let err = c.validate_nome().unwrap_err();
7021        assert!(
7022            matches!(
7023                err,
7024                ManifestError::NomeInvalid { ref nome, ref reason }
7025                    if nome == "my_app" && reason.contains('_')
7026            ),
7027            "got {err:?}"
7028        );
7029    }
7030
7031    #[test]
7032    fn validate_nome_rejects_dot() {
7033        // A `:nome` is a single DNS-1123 label, not a subdomain. The
7034        // "I want to namespace with `.`" footgun the gate redirects to
7035        // `-` via the shared predicate's reason wording.
7036        let c = caixa_with_nome("team.app");
7037        let err = c.validate_nome().unwrap_err();
7038        assert!(
7039            matches!(
7040                err,
7041                ManifestError::NomeInvalid { ref nome, ref reason }
7042                    if nome == "team.app" && reason.contains('.')
7043            ),
7044            "got {err:?}"
7045        );
7046    }
7047
7048    #[test]
7049    fn validate_nome_rejects_leading_hyphen() {
7050        // DNS-1123 boundary rule: the label must start with an ASCII
7051        // alphanumeric. Pin the leading-`-` arm explicitly.
7052        let c = caixa_with_nome("-app");
7053        let err = c.validate_nome().unwrap_err();
7054        assert!(
7055            matches!(
7056                err,
7057                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
7058            ),
7059            "got {err:?}"
7060        );
7061    }
7062
7063    #[test]
7064    fn validate_nome_rejects_trailing_hyphen() {
7065        // Symmetric arm of the boundary rule, pinned separately so a
7066        // future relaxation that only checks the leading position
7067        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
7068        // and `_with_trailing_hyphen` on the supervisor / aplicacao
7069        // axes.
7070        let c = caixa_with_nome("app-");
7071        let err = c.validate_nome().unwrap_err();
7072        assert!(
7073            matches!(
7074                err,
7075                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
7076            ),
7077            "got {err:?}"
7078        );
7079    }
7080
7081    #[test]
7082    fn validate_nome_rejects_unicode() {
7083        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
7084        // bytes are rejected by the K8s apiserver on every name axis.
7085        let c = caixa_with_nome("café");
7086        let err = c.validate_nome().unwrap_err();
7087        assert!(
7088            matches!(
7089                err,
7090                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
7091            ),
7092            "got {err:?}"
7093        );
7094    }
7095
7096    #[test]
7097    fn validate_nome_rejects_whitespace() {
7098        // The paste-from-sketch / paste-from-spec footgun. Internal
7099        // whitespace is rejected by every K8s name axis.
7100        let c = caixa_with_nome("my app");
7101        let err = c.validate_nome().unwrap_err();
7102        assert!(
7103            matches!(
7104                err,
7105                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
7106            ),
7107            "got {err:?}"
7108        );
7109    }
7110
7111    #[test]
7112    fn validate_nome_rejects_too_long() {
7113        // 64-byte boundary pin: the K8s apiserver rejects any
7114        // `metadata.name` over 63 bytes at admission; the diagnostic
7115        // names both the 63-byte cap and the actual length so the
7116        // author can shorten in one edit. Mirrors `_too_long` on the
7117        // peer member-/cluster-/child-name axes.
7118        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
7119        let c = caixa_with_nome(&over);
7120        let err = c.validate_nome().unwrap_err();
7121        let ManifestError::NomeInvalid { nome, reason } = err else {
7122            panic!("expected NomeInvalid for over-cap :nome");
7123        };
7124        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
7125        assert!(
7126            reason.contains("63") && reason.contains("64"),
7127            "diagnostic must name the cap + actual length, got {reason:?}"
7128        );
7129    }
7130
7131    #[test]
7132    fn nome_max_length_validates() {
7133        // The 63-byte cap exactly — the boundary-accepting case pinned
7134        // alongside `validate_nome_rejects_too_long` so a future cap
7135        // shift surfaces both arms simultaneously. Mirrors
7136        // `membro_caixa_max_length_validates`,
7137        // `placement_cluster_max_length_validates`,
7138        // `child_caixa_max_length_validates`.
7139        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
7140        caixa_with_nome(&at_cap).validate_nome().unwrap();
7141    }
7142
7143    #[test]
7144    fn nome_empty_takes_precedence_over_invalid() {
7145        // Order pin: the empty arm fires before the predicate is
7146        // consulted. Empty < invalid in self-locating-ness — the
7147        // narrower `NomeEmpty` diagnostic doesn't carry a useless
7148        // `nome: ""` reference into the parser-shaped reason. Mirrors
7149        // `membro_caixa_empty_takes_precedence_over_invalid` on the
7150        // peer axis (3f9d7a0).
7151        let c = caixa_with_nome("");
7152        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
7153    }
7154
7155    #[test]
7156    fn nome_invalid_diagnostic_carries_offending_nome() {
7157        // Diagnostic-shape pin: the error names the offending `:nome`
7158        // verbatim with a non-empty parser-shaped reason, so a `feira
7159        // lint` run can render the diagnostic without re-parsing.
7160        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
7161        let c = caixa_with_nome("MyApp");
7162        let err = c.validate_nome().unwrap_err();
7163        let ManifestError::NomeInvalid { nome, reason } = err else {
7164            panic!("expected NomeInvalid variant");
7165        };
7166        assert_eq!(nome, "MyApp");
7167        assert!(
7168            !reason.is_empty(),
7169            "NomeInvalid `reason` must carry the predicate's wording verbatim"
7170        );
7171    }
7172
7173    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
7174    //
7175    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
7176    // via DNS-1123; this second-axis gate caps the joint
7177    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
7178    // canonical [`crate::lareira_chart_name`] helper's doc comment
7179    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
7180    // "the M4 admission webhook will pin the joint-length invariant
7181    // when it lands". These tests pin it at the manifest-validate
7182    // layer instead, fail-before-pass-after on the 56-byte boundary.
7183
7184    #[test]
7185    fn validate_nome_chart_name_budget_accepts_canonical_template() {
7186        // Positive control: the bare `feira init`-style template's
7187        // `:nome` ("demo") sits far below the cap; the gate must not
7188        // regress this baseline. Same shape every peer
7189        // value-shape-gate baseline pin uses.
7190        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7191        c.validate_nome_chart_name_budget().unwrap();
7192    }
7193
7194    #[test]
7195    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
7196        // Positive-set sweep across the canonical author surface every
7197        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
7198        // `worker`, the `checkout-aplicacao` example members, the
7199        // `akeyless-attest` caixa-tatara fixture). Every value sits
7200        // far below the 55-byte per-`:nome` budget. Same shape every
7201        // peer per-axis baseline pin uses.
7202        for nome in [
7203            "hello-rio",
7204            "cart",
7205            "checkout",
7206            "worker",
7207            "akeyless-attest",
7208            "demo",
7209            "a",
7210        ] {
7211            caixa_with_nome(nome)
7212                .validate_nome_chart_name_budget()
7213                .unwrap_or_else(|e| {
7214                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
7215                });
7216        }
7217    }
7218
7219    #[test]
7220    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
7221        // Boundary-accepting case at the 55-byte per-`:nome` budget —
7222        // the joint chart name is exactly 63 bytes, the DNS-1123 label
7223        // cap. Pinned alongside the rejecting-arm test so a future cap
7224        // shift surfaces both arms simultaneously. Mirrors
7225        // `nome_max_length_validates` on the peer bare-`:nome` axis.
7226        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
7227        caixa_with_nome(&at_cap)
7228            .validate_nome_chart_name_budget()
7229            .unwrap();
7230    }
7231
7232    #[test]
7233    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
7234        // Fail-before-pass-after pin on the 56-byte boundary: the
7235        // smallest `:nome` length that overflows the joint chart-name
7236        // cap. The inner [`is_dns_1123_label`] gate
7237        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
7238        // this gate it silently passed the manifest-validate cascade
7239        // and surfaced as a `helm lint` / apiserver rejection on the
7240        // rendered chart name far from the source `caixa.lisp`, with
7241        // no field naming the overflow. With this gate the diagnostic
7242        // names the offending `:nome` verbatim alongside the rendered
7243        // chart name and the budget, so the author can shorten in one
7244        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
7245        // bare-`:nome` axis.
7246        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7247        let c = caixa_with_nome(&over);
7248        let err = c.validate_nome_chart_name_budget().unwrap_err();
7249        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7250            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
7251        };
7252        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7253        assert_eq!(nome, over);
7254        assert!(
7255            reason.contains("63") && reason.contains("64") && reason.contains("55"),
7256            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
7257             and the per-`:nome` budget (55), got {reason:?}"
7258        );
7259    }
7260
7261    #[test]
7262    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
7263        // The 63-byte `:nome` boundary — passes the bare-`:nome`
7264        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
7265        // joint chart name that overflows the DNS-1123 label cap
7266        // structurally. The most stringent fail-before-pass-after
7267        // surface: every `:nome` in the 56..=63-byte range passed the
7268        // prior cascade and broke at admission.
7269        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
7270        let c = caixa_with_nome(&bare_max);
7271        // The bare-`:nome` gate accepts the 63-byte length.
7272        c.validate_nome().unwrap();
7273        // The new joint-length gate rejects it.
7274        let err = c.validate_nome_chart_name_budget().unwrap_err();
7275        assert!(
7276            matches!(
7277                err,
7278                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
7279                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
7280            ),
7281            "got {err:?}"
7282        );
7283    }
7284
7285    #[test]
7286    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
7287        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
7288        // name appears verbatim in the diagnostic so the author sees
7289        // exactly the string the apiserver / `helm lint` would have
7290        // rejected — no re-derivation required to grep the source.
7291        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
7292        // on the bare-`:nome` axis.
7293        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
7294        let c = caixa_with_nome(&over);
7295        let err = c.validate_nome_chart_name_budget().unwrap_err();
7296        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7297            panic!("expected NomeChartNameBudgetExceeded variant");
7298        };
7299        assert_eq!(nome, over);
7300        let expected_chart = crate::lareira_chart_name(&over);
7301        assert!(
7302            reason.contains(&expected_chart),
7303            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
7304             got {reason:?}"
7305        );
7306        assert!(
7307            reason.contains("lareira-"),
7308            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
7309        );
7310    }
7311
7312    #[test]
7313    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
7314        // Order pin on the layout cascade: the narrower
7315        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
7316        // joint-length budget. A structurally-malformed `:nome` (here:
7317        // uppercase) surfaces its specific shape error rather than
7318        // the chart-name-budget error, even when the joint length
7319        // would also overflow — the narrower diagnostic is more
7320        // self-locating. Mirrors the cascade-precedence pins peer
7321        // gates already use (e.g. `EntradaParaEmpty` before
7322        // `EntradaParaInvalid`).
7323        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7324        let c = caixa_with_nome(&over);
7325        // The bare-shape gate fires first.
7326        let err = c.validate_nome().unwrap_err();
7327        assert!(
7328            matches!(err, ManifestError::NomeInvalid { .. }),
7329            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
7330        );
7331        // And the layout verify cascade surfaces that diagnostic, not
7332        // the budget arm. Inject a path-exists oracle so the cascade
7333        // gets past the manifest-presence check and into the
7334        // value-shape gates.
7335        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7336        let err = crate::LayoutInvariants::verify(
7337            &layout,
7338            &c,
7339            std::path::Path::new("/tmp/caixa-test-fake-root"),
7340        )
7341        .unwrap_err();
7342        let issue = err.to_string();
7343        assert!(
7344            issue.contains("DNS-1123") || issue.contains("uppercase"),
7345            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
7346             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
7347        );
7348    }
7349
7350    #[test]
7351    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
7352        // Cross-axis envelope pin: the layout cascade wraps both
7353        // bare-`:nome` and joint-length-`:nome` failures through the
7354        // same [`LayoutError::NomeViolation`] envelope, since both
7355        // arms are on the `:nome` axis. The user's diagnostic stays
7356        // self-locating ("which axis"), and a future consumer that
7357        // dispatches on the layout-error variant (e.g. a `feira lint`
7358        // exit-code mapping) sees a single per-axis envelope. The
7359        // wrapped `issue:` carries the full inner diagnostic.
7360        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7361        let c = caixa_with_nome(&over);
7362        // The bare-shape gate accepts.
7363        c.validate_nome().unwrap();
7364        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7365        let err = crate::LayoutInvariants::verify(
7366            &layout,
7367            &c,
7368            std::path::Path::new("/tmp/caixa-test-fake-root"),
7369        )
7370        .unwrap_err();
7371        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
7372            panic!("expected LayoutError::NomeViolation, got {err:?}");
7373        };
7374        assert_eq!(caixa, over);
7375        assert!(
7376            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
7377            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
7378        );
7379    }
7380
7381    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
7382
7383    fn caixa_with_versao(versao: &str) -> Caixa {
7384        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7385        c.versao = versao.to_string();
7386        c
7387    }
7388
7389    #[test]
7390    fn validate_versao_accepts_canonical_template() {
7391        // Positive control: the bare `feira init`-style template's
7392        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
7393        // must not regress this baseline shape. A future tightening of
7394        // the accepted set surfaces here as a test failure first.
7395        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7396        c.validate_versao().unwrap();
7397    }
7398
7399    #[test]
7400    fn validate_versao_accepts_canonical_forms() {
7401        // Positive-set sweep: each realistic SemVer-2 shape the
7402        // substrate's downstream consumers accept must pass — bare
7403        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
7404        // build metadata (`+build.42`), the combined form, and the
7405        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
7406        // the peer `:nome` axis (6c992f8).
7407        for versao in [
7408            "0.1.0",
7409            "0.0.0",
7410            "1.0.0",
7411            "0.2.0-rc.1",
7412            "1.0.0-alpha.0",
7413            "1.0.0+build.42",
7414            "1.0.0-rc.1+build.42",
7415            "10.20.30",
7416        ] {
7417            caixa_with_versao(versao)
7418                .validate_versao()
7419                .unwrap_or_else(|e| {
7420                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
7421                });
7422        }
7423    }
7424
7425    #[test]
7426    fn validate_versao_rejects_empty() {
7427        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
7428        // an empty `:versao` (the derive macro stores the raw String);
7429        // the gate's empty arm names the offending axis with a narrower
7430        // diagnostic than the `VersaoInvalid` parse arm would emit.
7431        // Mirrors `validate_nome_rejects_empty` (6c992f8).
7432        let c = caixa_with_versao("");
7433        let err = c.validate_versao().unwrap_err();
7434        assert_eq!(err, ManifestError::VersaoEmpty);
7435    }
7436
7437    #[test]
7438    fn validate_versao_rejects_git_tag_shape() {
7439        // The canonical "I copied the git tag verbatim" footgun —
7440        // `feira publish` *emits* `v<versao>` git tags, so a leaked
7441        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
7442        // shift every downstream consumer's version axis. `semver`
7443        // rejects the leading `v` at parse time; the gate moves the
7444        // diagnostic to the source `caixa.lisp`.
7445        let c = caixa_with_versao("v0.1.0");
7446        let err = c.validate_versao().unwrap_err();
7447        let ManifestError::VersaoInvalid { versao, reason } = err else {
7448            panic!("expected VersaoInvalid for git-tag-shape :versao");
7449        };
7450        assert_eq!(versao, "v0.1.0");
7451        assert!(
7452            !reason.is_empty(),
7453            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
7454        );
7455    }
7456
7457    #[test]
7458    fn validate_versao_rejects_missing_patch() {
7459        // The canonical "I shortened it" footgun — SemVer-2 requires
7460        // three parts. Cargo's `version =` field accepts the shortened
7461        // form as a requirement, conflating the two leaks across the
7462        // typed `:deps :versao` vs top-level `:versao` axes; the gate
7463        // pins the top-level axis to the strict three-part shape.
7464        let c = caixa_with_versao("0.1");
7465        let err = c.validate_versao().unwrap_err();
7466        assert!(
7467            matches!(
7468                err,
7469                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
7470            ),
7471            "got {err:?}"
7472        );
7473    }
7474
7475    #[test]
7476    fn validate_versao_rejects_requirement_shape() {
7477        // The canonical "I leaked a requirement into a version" footgun —
7478        // the typed `:deps :versao` / `:membros :versao` axes accept
7479        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
7480        // concrete `Version`. Without this gate the two typed surfaces
7481        // would silently overlap, and a top-level `^0.1` would surface
7482        // at `helm install` time as a Chart.yaml version rejection far
7483        // from the source `caixa.lisp`.
7484        let c = caixa_with_versao("^0.1");
7485        let err = c.validate_versao().unwrap_err();
7486        assert!(
7487            matches!(
7488                err,
7489                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
7490            ),
7491            "got {err:?}"
7492        );
7493    }
7494
7495    #[test]
7496    fn validate_versao_rejects_docker_tag_shape() {
7497        // The "I confused it with a docker tag" footgun — `latest`,
7498        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
7499        // SemVer rejects at parse time; the gate moves the diagnostic
7500        // to the source `caixa.lisp`.
7501        for bad in ["latest", "main", "stable"] {
7502            let c = caixa_with_versao(bad);
7503            let err = c.validate_versao().unwrap_err();
7504            assert!(
7505                matches!(
7506                    err,
7507                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
7508                ),
7509                "got {err:?} for {bad:?}"
7510            );
7511        }
7512    }
7513
7514    #[test]
7515    fn validate_versao_rejects_four_part_form() {
7516        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
7517        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
7518        // semver crate rejects the extra `.0` at parse time.
7519        let c = caixa_with_versao("0.1.0.0");
7520        let err = c.validate_versao().unwrap_err();
7521        assert!(
7522            matches!(
7523                err,
7524                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
7525            ),
7526            "got {err:?}"
7527        );
7528    }
7529
7530    #[test]
7531    fn versao_empty_takes_precedence_over_invalid() {
7532        // Order pin: the empty arm fires before the parser is consulted.
7533        // Empty < invalid in self-locating-ness — the narrower
7534        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
7535        // reference into the parser-shaped reason. Mirrors
7536        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
7537        // peer axis.
7538        let c = caixa_with_versao("");
7539        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
7540    }
7541
7542    #[test]
7543    fn versao_invalid_diagnostic_carries_offending_versao() {
7544        // Diagnostic-shape pin: the error names the offending `:versao`
7545        // verbatim with a non-empty parser-shaped reason, so a `feira
7546        // lint` run can render the diagnostic without re-parsing.
7547        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
7548        let c = caixa_with_versao("v0.1.0");
7549        let err = c.validate_versao().unwrap_err();
7550        let ManifestError::VersaoInvalid { versao, reason } = err else {
7551            panic!("expected VersaoInvalid variant");
7552        };
7553        assert_eq!(versao, "v0.1.0");
7554        assert!(
7555            !reason.is_empty(),
7556            "VersaoInvalid `reason` must carry the parser's wording verbatim"
7557        );
7558    }
7559
7560    #[test]
7561    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
7562        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
7563        // for `:upgrade-from :from` must also pass `validate_versao` —
7564        // the two `:versao`-typed surfaces (top-level `:versao`,
7565        // `:upgrade-from :from`) consume the *same* `semver::Version`
7566        // parser, so they must agree on the accepted set. Without this
7567        // pin, a future tightening of one axis could silently diverge
7568        // from the other. Mirrors the `:versao` requirement-axis
7569        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
7570        // commits established.
7571        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
7572            // From the canonical UpgradeFromEntry round-trip fixture
7573            // (`upgrade::tests::round_trip_load_module` peers).
7574            let entry = crate::UpgradeFromEntry {
7575                from: versao.to_string(),
7576                instructions: Vec::new(),
7577            };
7578            entry
7579                .validate()
7580                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
7581            caixa_with_versao(versao)
7582                .validate_versao()
7583                .unwrap_or_else(|e| {
7584                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
7585                });
7586        }
7587    }
7588
7589    // ── Caixa::validate_restart_window — supervisor restart-window
7590    //    folds through the shared `supervisor::duration_codec` ────────
7591
7592    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
7593        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
7594        c.kind = CaixaKind::Supervisor;
7595        c.restart_window = window.map(str::to_string);
7596        c
7597    }
7598
7599    #[test]
7600    fn validate_restart_window_accepts_none() {
7601        // The canonical "omit the slot to express no reset" shape — a
7602        // `None` raw string is the absence of the typed
7603        // `:restart-window` slot, which is exactly the SupervisorSpec
7604        // "never reset" semantics. The gate must be a no-op here; a
7605        // future tightening that rejected `None` would force every
7606        // supervisor caixa to authoring-time pin a window even when
7607        // the OTP semantics call for none.
7608        caixa_with_restart_window(None)
7609            .validate_restart_window()
7610            .unwrap();
7611    }
7612
7613    #[test]
7614    fn validate_restart_window_accepts_canonical_forms() {
7615        // Positive-set sweep across the canonical authoring units the
7616        // shared `supervisor::duration_codec::parse` accepts —
7617        // matches the codec-side `parse_accepts_integer_canonical_units`
7618        // pin in supervisor::tests so a future codec-side tightening
7619        // surfaces simultaneously on both axes.
7620        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
7621            caixa_with_restart_window(Some(window))
7622                .validate_restart_window()
7623                .unwrap_or_else(|e| {
7624                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
7625                });
7626        }
7627    }
7628
7629    #[test]
7630    fn validate_restart_window_rejects_fractional_seconds() {
7631        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
7632        // as f64 to 1.5 → renders back as `"1500ms"` on first
7633        // serialize). Prior to the fold + this gate, the inline
7634        // `parse_window_inline` accepted f64 magnitudes and silently
7635        // produced a `Duration::from_secs_f64(1.5)`, divergent from
7636        // the shared codec's integer-magnitude discipline on the
7637        // serde-routed siblings. The gate now surfaces a self-locating
7638        // diagnostic at the manifest layer.
7639        let err = caixa_with_restart_window(Some("1.5s"))
7640            .validate_restart_window()
7641            .unwrap_err();
7642        let ManifestError::RestartWindowMalformed {
7643            restart_window,
7644            reason,
7645        } = err
7646        else {
7647            panic!("expected RestartWindowMalformed for fractional seconds");
7648        };
7649        assert_eq!(restart_window, "1.5s");
7650        assert!(
7651            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
7652            "diagnostic must carry shared-codec wording, got {reason:?}"
7653        );
7654    }
7655
7656    #[test]
7657    fn validate_restart_window_rejects_decimal_shaped_integer() {
7658        // The `"1.0s"` class — numerically `1s` exactly, but the
7659        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
7660        // gets the same canonical-form diagnostic.
7661        let err = caixa_with_restart_window(Some("1.0s"))
7662            .validate_restart_window()
7663            .unwrap_err();
7664        assert!(
7665            matches!(
7666                err,
7667                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7668                    if restart_window == "1.0s"
7669            ),
7670            "got {err:?}"
7671        );
7672    }
7673
7674    #[test]
7675    fn validate_restart_window_rejects_half_unit_minute() {
7676        // `"0.5m"` is the unit-fraction footgun — author writes a
7677        // human-readable half-minute, the prior inline parser silently
7678        // produced `Duration::from_secs_f64(30.0)` and serde
7679        // re-emitted as `"30s"`, rewriting author intent. The gate
7680        // closes the loop at the manifest layer.
7681        let err = caixa_with_restart_window(Some("0.5m"))
7682            .validate_restart_window()
7683            .unwrap_err();
7684        let ManifestError::RestartWindowMalformed {
7685            restart_window,
7686            reason,
7687        } = err
7688        else {
7689            panic!("expected RestartWindowMalformed");
7690        };
7691        assert_eq!(restart_window, "0.5m");
7692        assert!(
7693            reason.contains("\"30s\""),
7694            "diagnostic must point at the canonical-form remediation, got {reason:?}"
7695        );
7696    }
7697
7698    #[test]
7699    fn validate_restart_window_rejects_leading_sign() {
7700        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
7701        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
7702        // and was caught by the `num < 0.0` arm which silently
7703        // returned `None`, dropping the author-supplied window). The
7704        // shared codec's digit-only gate rejects both with a unified
7705        // canonical-form diagnostic; the manifest-layer wrapper names
7706        // the offending value.
7707        for bad in ["+30s", "-30s"] {
7708            let err = caixa_with_restart_window(Some(bad))
7709                .validate_restart_window()
7710                .unwrap_err();
7711            assert!(
7712                matches!(
7713                    err,
7714                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
7715                        if restart_window == bad
7716                ),
7717                "got {err:?} for {bad:?}"
7718            );
7719        }
7720    }
7721
7722    #[test]
7723    fn validate_restart_window_rejects_unknown_unit() {
7724        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
7725        // unit dispatch surfaces an `unknown duration unit` reason;
7726        // the manifest-layer wrapper names the offending value.
7727        let err = caixa_with_restart_window(Some("30x"))
7728            .validate_restart_window()
7729            .unwrap_err();
7730        let ManifestError::RestartWindowMalformed {
7731            restart_window,
7732            reason,
7733        } = err
7734        else {
7735            panic!("expected RestartWindowMalformed for unknown unit");
7736        };
7737        assert_eq!(restart_window, "30x");
7738        assert!(
7739            reason.contains("unknown duration unit"),
7740            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
7741        );
7742    }
7743
7744    #[test]
7745    fn validate_restart_window_rejects_garbage() {
7746        // Pure non-numeric magnitude (`"abc"`) falls through to the
7747        // shared codec's narrower `"bad duration magnitude"` arm. Same
7748        // diagnostic shape as the codec-side
7749        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
7750        let err = caixa_with_restart_window(Some("abc"))
7751            .validate_restart_window()
7752            .unwrap_err();
7753        let ManifestError::RestartWindowMalformed {
7754            restart_window,
7755            reason,
7756        } = err
7757        else {
7758            panic!("expected RestartWindowMalformed for garbage");
7759        };
7760        assert_eq!(restart_window, "abc");
7761        assert!(
7762            reason.contains("bad duration magnitude"),
7763            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
7764        );
7765    }
7766
7767    #[test]
7768    fn validate_restart_window_rejects_empty_string() {
7769        // The empty-after-trim edge case — distinct from the `None`
7770        // canonical "omit the slot" shape. The shared codec's
7771        // digit-only gate refuses an empty magnitude; the manifest
7772        // layer names the offending `""` so the author can grep for
7773        // the literal empty value in their `caixa.lisp` and either
7774        // remove the slot (the canonical "no reset" shape) or pin a
7775        // positive duration.
7776        let err = caixa_with_restart_window(Some(""))
7777            .validate_restart_window()
7778            .unwrap_err();
7779        assert!(
7780            matches!(
7781                err,
7782                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7783                    if restart_window.is_empty()
7784            ),
7785            "got {err:?}"
7786        );
7787    }
7788
7789    #[test]
7790    fn validate_restart_window_diagnostic_carries_offending_value() {
7791        // Diagnostic-shape pin (peer with
7792        // `nome_invalid_diagnostic_carries_offending_nome` /
7793        // `versao_invalid_diagnostic_carries_offending_versao`): the
7794        // error names the offending raw `:restart-window` verbatim
7795        // with a non-empty shared-codec-shaped reason, so a `feira
7796        // lint` run can render the diagnostic without re-parsing.
7797        let err = caixa_with_restart_window(Some("1.5s"))
7798            .validate_restart_window()
7799            .unwrap_err();
7800        let ManifestError::RestartWindowMalformed {
7801            restart_window,
7802            reason,
7803        } = err
7804        else {
7805            panic!("expected RestartWindowMalformed variant");
7806        };
7807        assert_eq!(restart_window, "1.5s");
7808        assert!(
7809            !reason.is_empty(),
7810            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
7811        );
7812    }
7813
7814    #[test]
7815    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
7816        // Behavioral parity pin after the fold (`parse_window_inline`
7817        // deletion): the canonical `"60s"` still produces
7818        // `Duration::from_secs(60)` on the typed view — the fold is
7819        // semantically equivalent to the prior inline parser on the
7820        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
7821        // pin, narrowed to the parser-side contract.
7822        let c = caixa_with_restart_window(Some("60s"));
7823        let view = c.supervisor_view().expect("Supervisor kind has a view");
7824        assert_eq!(
7825            view.restart_window,
7826            Some(std::time::Duration::from_secs(60))
7827        );
7828    }
7829
7830    #[test]
7831    fn supervisor_view_soft_swallows_what_validate_rejects() {
7832        // Parity pin between the view-construction path and the
7833        // manifest-level validator: the same `"1.5s"` that surfaces
7834        // `RestartWindowMalformed` at `validate_restart_window` time
7835        // becomes `restart_window: None` on the typed view (the fold
7836        // preserves the existing best-effort shape of `supervisor_view`).
7837        // The contract is: a layout-verifier / `feira lint` flow that
7838        // cares about the malformed-window axis MUST consult
7839        // `validate_restart_window` — relying solely on the view's
7840        // `None` swallows the diagnostic silently. This pin makes the
7841        // expectation a typed invariant.
7842        let c = caixa_with_restart_window(Some("1.5s"));
7843        let view = c.supervisor_view().expect("Supervisor kind has a view");
7844        assert_eq!(
7845            view.restart_window, None,
7846            "view-construction path soft-swallows the parse error to None"
7847        );
7848        // And the manifest-level validator does NOT soft-swallow:
7849        assert!(
7850            matches!(
7851                c.validate_restart_window().unwrap_err(),
7852                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7853                    if restart_window == "1.5s"
7854            ),
7855            "validator must surface the offending value",
7856        );
7857    }
7858
7859    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
7860
7861    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
7862        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7863        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
7864        c.exe = exe.into_iter().map(String::from).collect();
7865        c.servicos = servicos.into_iter().map(String::from).collect();
7866        c
7867    }
7868
7869    #[test]
7870    fn validate_code_paths_accepts_canonical_template() {
7871        // The bare `Caixa::template` shape is the gate's identity element
7872        // on the canonical authoring shape — `:bibliotecas
7873        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
7874        // that the gate is non-disruptive against every existing caixa.
7875        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7876        c.validate_code_paths().unwrap();
7877    }
7878
7879    #[test]
7880    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
7881        // Positive control sweep: a canonical-shaped path on every slot
7882        // passes. Mirrors the peer
7883        // `behavior::validate_every_slot_relative_is_ok` pin.
7884        let c = caixa_with_code_paths(
7885            vec!["lib/demo.lisp", "lib/helpers.lisp"],
7886            vec!["exe/demo", "exe/tool"],
7887            vec!["servicos/demo.computeunit.yaml"],
7888        );
7889        c.validate_code_paths().unwrap();
7890    }
7891
7892    #[test]
7893    fn validate_code_paths_accepts_all_empty_lists() {
7894        // The empty-list identity element: every Caixa with no declared
7895        // code paths trivially passes (Supervisor / Aplicacao kinds rely
7896        // on this — the OwnCode gate already rejected them before the
7897        // path-shape gate runs in the layout, but the validator itself
7898        // must accept the empty shape).
7899        let c = caixa_with_code_paths(vec![], vec![], vec![]);
7900        c.validate_code_paths().unwrap();
7901    }
7902
7903    #[test]
7904    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
7905        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
7906        let err = c.validate_code_paths().unwrap_err();
7907        assert!(
7908            matches!(
7909                err,
7910                ManifestError::CodePathEmpty {
7911                    slot: ":bibliotecas"
7912                }
7913            ),
7914            "got {err:?}",
7915        );
7916    }
7917
7918    #[test]
7919    fn validate_code_paths_rejects_empty_exe_entry() {
7920        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
7921        let err = c.validate_code_paths().unwrap_err();
7922        assert!(
7923            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
7924            "got {err:?}",
7925        );
7926    }
7927
7928    #[test]
7929    fn validate_code_paths_rejects_empty_servicos_entry() {
7930        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
7931        let err = c.validate_code_paths().unwrap_err();
7932        assert!(
7933            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
7934            "got {err:?}",
7935        );
7936    }
7937
7938    #[test]
7939    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
7940        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
7941        // so an absolute path that resolves on disk silently passes the
7942        // layout's existence check — the canonical sandbox-escape on
7943        // the biblioteca axis.
7944        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7945        let err = c.validate_code_paths().unwrap_err();
7946        let ManifestError::CodePathAbsolute { slot, path } = err else {
7947            panic!("expected CodePathAbsolute, got {err:?}");
7948        };
7949        assert_eq!(slot, ":bibliotecas");
7950        assert_eq!(path, PathBuf::from("/etc/passwd"));
7951    }
7952
7953    #[test]
7954    fn validate_code_paths_rejects_absolute_exe_entry() {
7955        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
7956        let err = c.validate_code_paths().unwrap_err();
7957        let ManifestError::CodePathAbsolute { slot, path } = err else {
7958            panic!("expected CodePathAbsolute, got {err:?}");
7959        };
7960        assert_eq!(slot, ":exe");
7961        assert_eq!(path, PathBuf::from("/usr/bin/env"));
7962    }
7963
7964    #[test]
7965    fn validate_code_paths_rejects_absolute_servicos_entry() {
7966        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
7967        let err = c.validate_code_paths().unwrap_err();
7968        let ManifestError::CodePathAbsolute { slot, path } = err else {
7969            panic!("expected CodePathAbsolute, got {err:?}");
7970        };
7971        assert_eq!(slot, ":servicos");
7972        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
7973    }
7974
7975    #[test]
7976    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
7977        // Canonical "I want a lib from a sibling caixa" footgun on the
7978        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
7979        // downstream, so a leading `..` traverses to the parent of the
7980        // caixa root with no diagnostic at layout time if the resolved
7981        // target exists.
7982        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
7983        let err = c.validate_code_paths().unwrap_err();
7984        let ManifestError::CodePathParentEscape { slot, path } = err else {
7985            panic!("expected CodePathParentEscape, got {err:?}");
7986        };
7987        assert_eq!(slot, ":bibliotecas");
7988        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
7989    }
7990
7991    #[test]
7992    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
7993        // Mid-path `..` defeats the layout's component-aware
7994        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
7995        // `starts_with(<root>/exe)` is true, but the canonical resolution
7996        // lives outside the caixa root. Caught regardless of where the
7997        // `..` sits — mirrors the peer
7998        // `behavior::validate_rejects_parent_escape_mid_path` pin.
7999        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
8000        let err = c.validate_code_paths().unwrap_err();
8001        let ManifestError::CodePathParentEscape { slot, path } = err else {
8002            panic!("expected CodePathParentEscape, got {err:?}");
8003        };
8004        assert_eq!(slot, ":exe");
8005        assert_eq!(path, PathBuf::from("exe/../../escape"));
8006    }
8007
8008    #[test]
8009    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
8010        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
8011        let err = c.validate_code_paths().unwrap_err();
8012        let ManifestError::CodePathParentEscape { slot, path } = err else {
8013            panic!("expected CodePathParentEscape, got {err:?}");
8014        };
8015        assert_eq!(slot, ":servicos");
8016        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
8017    }
8018
8019    #[test]
8020    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
8021        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
8022        // `:servicos`. A manifest with malformed entries on all three
8023        // surfaces surfaces the `:bibliotecas` defect first, mirroring
8024        // the canonical declaration order
8025        // `Caixa::declared_foreign_code_slots` already establishes for
8026        // the foreign-code-slot diagnostic.
8027        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
8028        let err = c.validate_code_paths().unwrap_err();
8029        assert!(
8030            matches!(
8031                err,
8032                ManifestError::CodePathEmpty {
8033                    slot: ":bibliotecas"
8034                }
8035            ),
8036            "got {err:?}",
8037        );
8038    }
8039
8040    #[test]
8041    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
8042        // Within-slot precedence pin: empty → absolute → parent-escape,
8043        // matching the [`PathShapeViolation`] arm-ordering every peer
8044        // `is_sandboxed_relative_path` caller follows (b0c8389
8045        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
8046        // `:bibliotecas` list whose first entry is empty *and* whose
8047        // later entries are absolute/parent-escape surfaces the empty
8048        // arm first, on the lexicographically-earliest offending entry.
8049        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
8050        let err = c.validate_code_paths().unwrap_err();
8051        assert!(
8052            matches!(
8053                err,
8054                ManifestError::CodePathEmpty {
8055                    slot: ":bibliotecas"
8056                }
8057            ),
8058            "got {err:?}",
8059        );
8060    }
8061
8062    #[test]
8063    fn validate_code_paths_first_offender_per_slot_wins() {
8064        // Within a single slot, the first declaration-order offender
8065        // surfaces — pins that the gate is left-to-right deterministic
8066        // (peer of every `*_first_collision_*` pin on duplicate gates).
8067        let c = caixa_with_code_paths(
8068            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
8069            vec![],
8070            vec![],
8071        );
8072        let err = c.validate_code_paths().unwrap_err();
8073        let ManifestError::CodePathAbsolute { slot, path } = err else {
8074            panic!("expected CodePathAbsolute, got {err:?}");
8075        };
8076        assert_eq!(slot, ":bibliotecas");
8077        assert_eq!(path, PathBuf::from("/etc/escape"));
8078    }
8079
8080    #[test]
8081    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
8082        // Diagnostic-shape pin (peer with
8083        // `nome_invalid_diagnostic_carries_offending_nome` /
8084        // `versao_invalid_diagnostic_carries_offending_versao`): the
8085        // error's Display surfaces both the offending `:slot` tag and
8086        // the offending path verbatim, so a `feira lint` run can render
8087        // the diagnostic without re-parsing.
8088        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8089        let rendered = c.validate_code_paths().unwrap_err().to_string();
8090        assert!(
8091            rendered.contains(":bibliotecas"),
8092            "diagnostic must name the offending slot: {rendered}",
8093        );
8094        assert!(
8095            rendered.contains("/etc/passwd"),
8096            "diagnostic must quote the offending path: {rendered}",
8097        );
8098    }
8099
8100    #[test]
8101    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
8102        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
8103        // axis. Without the gate `feira build` re-parses the same lib
8104        // twice, wasting work and silently masking the author's intent
8105        // to declare a *second* biblioteca.
8106        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
8107        let err = c.validate_code_paths().unwrap_err();
8108        let ManifestError::CodePathDuplicate { slot, path } = err else {
8109            panic!("expected CodePathDuplicate, got {err:?}");
8110        };
8111        assert_eq!(slot, ":bibliotecas");
8112        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
8113    }
8114
8115    #[test]
8116    fn validate_code_paths_rejects_duplicate_exe_entry() {
8117        // Same footgun on the Binario surface. The future `caixa-flake`
8118        // emitter that materializes each `:exe` entry as a flake
8119        // `packages.<name>` derivation would collide on the duplicate
8120        // package key — surfaced here at the typed-validate layer with a
8121        // self-locating diagnostic instead.
8122        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
8123        let err = c.validate_code_paths().unwrap_err();
8124        let ManifestError::CodePathDuplicate { slot, path } = err else {
8125            panic!("expected CodePathDuplicate, got {err:?}");
8126        };
8127        assert_eq!(slot, ":exe");
8128        assert_eq!(path, PathBuf::from("exe/cli"));
8129    }
8130
8131    #[test]
8132    fn validate_code_paths_rejects_duplicate_servicos_entry() {
8133        // Same footgun on the Servico surface. The peer caixa-helm /
8134        // caixa-flux renderers refuse `:servicos.len() != 1` with the
8135        // narrower `UnsupportedServicoCount` diagnostic, but that
8136        // diagnostic surfaces "too many servicos" without naming
8137        // "duplicate entry" — the typed self-locating framing only lands
8138        // at this gate.
8139        let c = caixa_with_code_paths(
8140            vec![],
8141            vec![],
8142            vec![
8143                "servicos/demo.computeunit.yaml",
8144                "servicos/demo.computeunit.yaml",
8145            ],
8146        );
8147        let err = c.validate_code_paths().unwrap_err();
8148        let ManifestError::CodePathDuplicate { slot, path } = err else {
8149            panic!("expected CodePathDuplicate, got {err:?}");
8150        };
8151        assert_eq!(slot, ":servicos");
8152        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
8153    }
8154
8155    #[test]
8156    fn validate_code_paths_accepts_same_path_across_slots() {
8157        // Per-list scope pin: a `:bibliotecas` entry that happens to
8158        // collide with an `:exe` or `:servicos` entry as a *string* is
8159        // not a duplicate by this gate (each list gets its own HashSet),
8160        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
8161        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
8162        // shape on the dep axis). The structural `starts_with(<exe |
8163        // servicos>_dir)` fence at layout time prevents the realistic
8164        // cross-slot collision case from existing on disk, but the gate's
8165        // per-list scope is correct independent of that downstream fence.
8166        let c = caixa_with_code_paths(
8167            vec!["lib/x.lisp"],
8168            vec!["exe/x"],
8169            vec!["servicos/x.computeunit.yaml"],
8170        );
8171        c.validate_code_paths().unwrap();
8172    }
8173
8174    #[test]
8175    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
8176        // Within-slot ordering pin: structural defects (empty / absolute
8177        // / parent-escape) fire before the duplicate gate on the same
8178        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
8179        // surfaces the narrower `CodePathEmpty` for the empty entry
8180        // first, not the duplicate on the later pair — same arm-ordering
8181        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
8182        // `:autores` 86c769b, `:deps` 359fba5).
8183        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
8184        let err = c.validate_code_paths().unwrap_err();
8185        assert!(
8186            matches!(
8187                err,
8188                ManifestError::CodePathEmpty {
8189                    slot: ":bibliotecas"
8190                }
8191            ),
8192            "got {err:?}",
8193        );
8194    }
8195
8196    #[test]
8197    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
8198        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
8199        // duplicates surface before `:exe` duplicates, matching the
8200        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
8201        // order every peer per-slot diagnostic on this surface follows.
8202        let c = caixa_with_code_paths(
8203            vec!["lib/x.lisp", "lib/x.lisp"],
8204            vec!["exe/y", "exe/y"],
8205            vec![],
8206        );
8207        let err = c.validate_code_paths().unwrap_err();
8208        let ManifestError::CodePathDuplicate { slot, path } = err else {
8209            panic!("expected CodePathDuplicate, got {err:?}");
8210        };
8211        assert_eq!(slot, ":bibliotecas");
8212        assert_eq!(path, PathBuf::from("lib/x.lisp"));
8213    }
8214
8215    #[test]
8216    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
8217        // Diagnostic-shape pin (peer with
8218        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8219        // on the structural arm): the duplicate-arm Display surfaces both
8220        // the offending `:slot` tag and the offending path verbatim, so a
8221        // `feira lint` run can render the diagnostic without re-parsing.
8222        let c = caixa_with_code_paths(
8223            vec![],
8224            vec![],
8225            vec![
8226                "servicos/demo.computeunit.yaml",
8227                "servicos/demo.computeunit.yaml",
8228            ],
8229        );
8230        let rendered = c.validate_code_paths().unwrap_err().to_string();
8231        assert!(
8232            rendered.contains(":servicos"),
8233            "diagnostic must name the offending slot: {rendered}",
8234        );
8235        assert!(
8236            rendered.contains("servicos/demo.computeunit.yaml"),
8237            "diagnostic must quote the offending path: {rendered}",
8238        );
8239    }
8240
8241    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
8242    //
8243    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
8244    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
8245    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
8246    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
8247    // at parse time — the same downstream consumer the peer `:behavior
8248    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
8249    // `:upgrade-from :state-change :script` (33cc830,
8250    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
8251    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
8252    // nix-built executable surface (`"exe/<name>"` shape per the canonical
8253    // [`crate::LayoutError::ExeOutsideDir`] error message and every
8254    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
8255    // is the `.computeunit.yaml` ComputeUnit-CR axis.
8256
8257    #[test]
8258    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
8259        // Canonical "I dragged the wrong file from the workspace tree"
8260        // footgun on the biblioteca axis. Without the gate `feira build`
8261        // hands the extensionless path to `tatara_lisp::read` and fails
8262        // with a parser-shaped diagnostic far from the source caixa.lisp,
8263        // with no field naming the offending `:bibliotecas` entry.
8264        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
8265            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8266            let err = c.validate_code_paths().unwrap_err();
8267            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8268                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8269            };
8270            assert_eq!(slot, ":bibliotecas");
8271            assert_eq!(path, PathBuf::from(relpath));
8272        }
8273    }
8274
8275    #[test]
8276    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
8277        // Wrong-extension sweep across common authoring footguns. Same
8278        // sweep posture as the peer
8279        // `behavior::validate_rejects_wrong_extension` (c97815a) and
8280        // `upgrade::tests::state_change_rejects_wrong_extension_script`
8281        // (33cc830) cases.
8282        for relpath in [
8283            "lib/demo.rs",
8284            "lib/demo.txt",
8285            "lib/demo.md",
8286            "lib/demo.json",
8287            "lib/demo.yaml",
8288            "lib/demo.toml",
8289            "lib/demo.lisp.bak",
8290            "lib/demo.lispx",
8291            "lib/demo.lis",
8292        ] {
8293            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8294            let err = c.validate_code_paths().unwrap_err();
8295            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8296                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8297            };
8298            assert_eq!(slot, ":bibliotecas");
8299            assert_eq!(path, PathBuf::from(relpath));
8300        }
8301    }
8302
8303    #[test]
8304    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
8305        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
8306        // contract. An uppercase `.LISP` shape that the layout's existence
8307        // check would (case-insensitively, on case-insensitive volumes)
8308        // match the on-disk file still mismatches the canonical form the
8309        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
8310        // contract. Mirrors the peer
8311        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
8312        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
8313        // (33cc830) sweeps.
8314        for relpath in [
8315            "lib/demo.LISP",
8316            "lib/demo.Lisp",
8317            "lib/demo.LiSp",
8318            "lib/demo.lISP",
8319        ] {
8320            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8321            let err = c.validate_code_paths().unwrap_err();
8322            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8323                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8324            };
8325            assert_eq!(slot, ":bibliotecas");
8326            assert_eq!(path, PathBuf::from(relpath));
8327        }
8328    }
8329
8330    #[test]
8331    fn validate_code_paths_accepts_canonical_lisp_shapes() {
8332        // Positive-control sweep through every canonical authoring shape
8333        // every in-tree fixture and the `Caixa::template` scaffold use.
8334        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
8335        // (c97815a) and the lifted predicate's own
8336        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
8337        // (33cc830).
8338        for relpath in [
8339            "lib/demo.lisp",
8340            "lib/handlers.lisp",
8341            "lib/migrations/v01-to-v02.lisp",
8342            "demo.lisp",
8343            "a.lisp",
8344            "./lib/demo.lisp",
8345            "lib/./handlers.lisp",
8346            "lib/migrations/v.0.1.lisp",
8347        ] {
8348            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8349            c.validate_code_paths()
8350                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8351        }
8352    }
8353
8354    #[test]
8355    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
8356        // The file-type gate is per-slot — only `:bibliotecas` carries the
8357        // tatara-lisp-source contract. An extensionless `:exe` entry
8358        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
8359        // canonical shapes every in-tree fixture uses, and must continue
8360        // to pass validate. Pins that a future tightening that broadens
8361        // the `.lisp` gate to either axis surfaces as a test failure
8362        // rather than as a silent breaking change to existing valid
8363        // manifests.
8364        let c = caixa_with_code_paths(
8365            vec![],
8366            vec!["exe/demo", "exe/tool"],
8367            vec!["servicos/demo.computeunit.yaml"],
8368        );
8369        c.validate_code_paths().unwrap();
8370    }
8371
8372    #[test]
8373    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
8374        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
8375        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
8376        // sandbox-shape diagnostic first (the `.lisp` remediation would
8377        // be misleading when the offending path can never resolve under
8378        // the caixa root anyway). Mirrors the peer
8379        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
8380        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
8381        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
8382        // on `:upgrade-from :state-change :script` (33cc830).
8383        //
8384        // Empty wins (the strictly-smaller-scope structural arm).
8385        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
8386        assert!(
8387            matches!(
8388                c.validate_code_paths().unwrap_err(),
8389                ManifestError::CodePathEmpty {
8390                    slot: ":bibliotecas"
8391                }
8392            ),
8393            "empty must win over non-lisp-extension",
8394        );
8395        // Absolute wins (the path can't resolve under the caixa root).
8396        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8397        let err = c.validate_code_paths().unwrap_err();
8398        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8399            panic!("absolute must win over non-lisp-extension, got {err:?}");
8400        };
8401        assert_eq!(slot, ":bibliotecas");
8402        // ParentEscape wins (the path escapes the caixa root).
8403        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
8404        let err = c.validate_code_paths().unwrap_err();
8405        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8406            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
8407        };
8408        assert_eq!(slot, ":bibliotecas");
8409    }
8410
8411    #[test]
8412    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
8413        // Within-slot precedence pin: the per-entry file-type shape gate
8414        // fires before the cross-entry duplicate gate, so the narrower
8415        // structural defect dominates the uniqueness diagnostic. A
8416        // `("lib/x.txt" "lib/x.txt")` shape surfaces
8417        // `CodePathNonLispExtension` on the first entry rather than
8418        // `CodePathDuplicate` on the pair — same posture every per-entry
8419        // shape-gate-precedes-duplicate cascade follows on this surface
8420        // (the empty / absolute / parent-escape arms already precede the
8421        // duplicate arm; the lifted file-type arm joins that set).
8422        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
8423        let err = c.validate_code_paths().unwrap_err();
8424        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8425            panic!("expected CodePathNonLispExtension, got {err:?}");
8426        };
8427        assert_eq!(slot, ":bibliotecas");
8428        assert_eq!(path, PathBuf::from("lib/x.txt"));
8429    }
8430
8431    #[test]
8432    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
8433        // Diagnostic-shape pin (peer with
8434        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8435        // on the sandbox-shape arms and
8436        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
8437        // on the duplicate arm): the file-type-arm Display surfaces both
8438        // the offending `:slot` tag, the offending path verbatim, and the
8439        // expected `.lisp` extension named in the remediation text, so a
8440        // `feira lint` run can render the diagnostic without re-parsing.
8441        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
8442        let rendered = c.validate_code_paths().unwrap_err().to_string();
8443        assert!(
8444            rendered.contains(":bibliotecas"),
8445            "diagnostic must name the offending slot: {rendered}",
8446        );
8447        assert!(
8448            rendered.contains("lib/demo.rs"),
8449            "diagnostic must quote the offending path: {rendered}",
8450        );
8451        assert!(
8452            rendered.contains(".lisp"),
8453            "diagnostic must name the expected extension: {rendered}",
8454        );
8455    }
8456
8457    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
8458    //
8459    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
8460    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
8461    // contract. The peer caixa-helm / caixa-flux renderers consume each
8462    // `:servicos` entry through `serde_yaml::from_str` as a typed
8463    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
8464    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
8465    // axis `Path::extension` can't express on its own.
8466
8467    #[test]
8468    fn validate_code_paths_rejects_no_extension_servicos_entry() {
8469        // Canonical "I dragged the wrong file from the workspace tree"
8470        // footgun on the Servico axis. Without the gate the peer
8471        // caixa-helm / caixa-flux renderers hand the extensionless path
8472        // to `serde_yaml::from_str` and fail with a parser-shaped
8473        // diagnostic far from the source caixa.lisp, with no field
8474        // naming the offending `:servicos` entry.
8475        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
8476            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8477            let err = c.validate_code_paths().unwrap_err();
8478            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8479                panic!(
8480                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8481                     got {err:?}"
8482                );
8483            };
8484            assert_eq!(slot, ":servicos");
8485            assert_eq!(path, PathBuf::from(relpath));
8486        }
8487    }
8488
8489    #[test]
8490    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
8491        // Wrong-extension sweep across common authoring footguns on the
8492        // Servico axis. Bare `.yaml` is the canonical "I forgot the
8493        // `.computeunit` segment" typo; the off-by-one-segment shapes
8494        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
8495        // bare `Path::extension` view but mismatch the typed compound
8496        // suffix the renderers' `serde_yaml::from_str` consumer demands.
8497        // Same sweep-posture as the peer
8498        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
8499        // (64772a9) on the sibling tatara-lisp-source axis.
8500        for relpath in [
8501            "servicos/demo.yaml",
8502            "servicos/demo.yml",
8503            "servicos/demo.json",
8504            "servicos/demo.toml",
8505            "servicos/demo.txt",
8506            "servicos/demo.computeunit.yaml.bak",
8507            "servicos/demo.computeunit.yam",
8508            "servicos/demo.computeunit",
8509            "servicos/demo-computeunit.yaml",
8510            "servicos/demo_computeunit.yaml",
8511        ] {
8512            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8513            let err = c.validate_code_paths().unwrap_err();
8514            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8515                panic!(
8516                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8517                     got {err:?}"
8518                );
8519            };
8520            assert_eq!(slot, ":servicos");
8521            assert_eq!(path, PathBuf::from(relpath));
8522        }
8523    }
8524
8525    #[test]
8526    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
8527        // Case-sensitivity sweep — pins the strict lowercase
8528        // `.computeunit.yaml` contract. A case-folded shape that the
8529        // layout's existence check would (case-insensitively, on
8530        // case-insensitive volumes) match the on-disk file still
8531        // mismatches the canonical form the codec emits, breaking the
8532        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
8533        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
8534        // (64772a9) sweep on the sibling tatara-lisp-source axis.
8535        for relpath in [
8536            "servicos/demo.ComputeUnit.yaml",
8537            "servicos/demo.COMPUTEUNIT.yaml",
8538            "servicos/demo.computeunit.YAML",
8539            "servicos/demo.computeunit.Yaml",
8540            "servicos/demo.COMPUTEUNIT.YAML",
8541        ] {
8542            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8543            let err = c.validate_code_paths().unwrap_err();
8544            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8545                panic!(
8546                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8547                     got {err:?}"
8548                );
8549            };
8550            assert_eq!(slot, ":servicos");
8551            assert_eq!(path, PathBuf::from(relpath));
8552        }
8553    }
8554
8555    #[test]
8556    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
8557        // Degenerate hidden-file shape: a file name exactly equal to the
8558        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
8559        // the structural "Servico declared with no identity" footgun.
8560        // The substrate identifies each ComputeUnit by the file-stem
8561        // segment that precedes `.computeunit.yaml` (the rendered
8562        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
8563        // the M3 `:contratos` membership lookup), so an empty stem
8564        // leaves the Servico unidentifiable. Pinned at the typed-axis
8565        // level so a future regression that drops the `name.len() >
8566        // SUFFIX.len()` bound at the predicate surfaces here, not
8567        // piecemeal as a `lareira-` chart-name collision at render time.
8568        for relpath in ["servicos/.computeunit.yaml"] {
8569            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8570            let err = c.validate_code_paths().unwrap_err();
8571            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8572                panic!(
8573                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8574                     got {err:?}"
8575                );
8576            };
8577            assert_eq!(slot, ":servicos");
8578            assert_eq!(path, PathBuf::from(relpath));
8579        }
8580    }
8581
8582    #[test]
8583    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
8584        // Positive-control sweep through every canonical authoring shape
8585        // every in-tree fixture and the `Caixa::template` scaffold use.
8586        // Mirrors the peer
8587        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
8588        // and the lifted predicate's own
8589        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
8590        // render.rs.
8591        for relpath in [
8592            "servicos/demo.computeunit.yaml",
8593            "servicos/hello-rio.computeunit.yaml",
8594            "servicos/my-service.computeunit.yaml",
8595            "servicos/a.computeunit.yaml",
8596            "./servicos/demo.computeunit.yaml",
8597            "servicos/./demo.computeunit.yaml",
8598            "servicos/sub/nested.computeunit.yaml",
8599            "servicos/v0.1.computeunit.yaml",
8600        ] {
8601            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8602            c.validate_code_paths()
8603                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8604        }
8605    }
8606
8607    #[test]
8608    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
8609        // The file-type gate is per-slot — only `:servicos` carries the
8610        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
8611        // entry and an extensionless `:exe` entry are the canonical
8612        // shapes every in-tree fixture uses, and must continue to pass
8613        // validate. Peer of
8614        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
8615        // (64772a9) — together pin that the typed
8616        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
8617        // cross-axis leakage in either direction.
8618        let c = caixa_with_code_paths(
8619            vec!["lib/demo.lisp"],
8620            vec!["exe/demo", "exe/tool"],
8621            vec!["servicos/demo.computeunit.yaml"],
8622        );
8623        c.validate_code_paths().unwrap();
8624    }
8625
8626    #[test]
8627    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
8628        // Cross-arm precedence pin: a `:servicos` entry that is *both*
8629        // sandbox-escaping and wrong-extension surfaces the more
8630        // fundamental sandbox-shape diagnostic first (the
8631        // `.computeunit.yaml` remediation would be misleading when the
8632        // offending path can never resolve under the caixa root
8633        // anyway). Mirrors the peer
8634        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
8635        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
8636        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
8637        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
8638        // table establishes.
8639        //
8640        // Empty wins (the strictly-smaller-scope structural arm).
8641        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
8642        assert!(
8643            matches!(
8644                c.validate_code_paths().unwrap_err(),
8645                ManifestError::CodePathEmpty { slot: ":servicos" }
8646            ),
8647            "empty must win over non-computeunit-yaml-extension",
8648        );
8649        // Absolute wins (the path can't resolve under the caixa root).
8650        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
8651        let err = c.validate_code_paths().unwrap_err();
8652        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8653            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
8654        };
8655        assert_eq!(slot, ":servicos");
8656        // ParentEscape wins (the path escapes the caixa root).
8657        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
8658        let err = c.validate_code_paths().unwrap_err();
8659        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8660            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
8661        };
8662        assert_eq!(slot, ":servicos");
8663    }
8664
8665    #[test]
8666    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
8667        // Within-slot precedence pin: the per-entry file-type shape gate
8668        // fires before the cross-entry duplicate gate, so the narrower
8669        // structural defect dominates the uniqueness diagnostic. A
8670        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
8671        // `CodePathNonComputeUnitYamlExtension` on the first entry
8672        // rather than `CodePathDuplicate` on the pair — same posture
8673        // every per-entry shape-gate-precedes-duplicate cascade follows
8674        // on this surface, peer of the 64772a9 `:bibliotecas`
8675        // `("lib/x.txt" "lib/x.txt")` ordering.
8676        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
8677        let err = c.validate_code_paths().unwrap_err();
8678        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8679            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
8680        };
8681        assert_eq!(slot, ":servicos");
8682        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
8683    }
8684
8685    #[test]
8686    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
8687     {
8688        // Diagnostic-shape pin (peer with
8689        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
8690        // on the sibling tatara-lisp-source axis): the file-type-arm
8691        // Display surfaces both the offending `:slot` tag, the
8692        // offending path verbatim, and the expected
8693        // `.computeunit.yaml` compound suffix named in the remediation
8694        // text, so a `feira lint` run can render the diagnostic without
8695        // re-parsing.
8696        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
8697        let rendered = c.validate_code_paths().unwrap_err().to_string();
8698        assert!(
8699            rendered.contains(":servicos"),
8700            "diagnostic must name the offending slot: {rendered}",
8701        );
8702        assert!(
8703            rendered.contains("servicos/demo.yaml"),
8704            "diagnostic must quote the offending path: {rendered}",
8705        );
8706        assert!(
8707            rendered.contains(".computeunit.yaml"),
8708            "diagnostic must name the expected compound suffix: {rendered}",
8709        );
8710    }
8711
8712    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
8713
8714    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
8715        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8716        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
8717        c
8718    }
8719
8720    #[test]
8721    fn validate_etiquetas_accepts_empty_list() {
8722        // The empty-list identity: every caixa with no declared tags
8723        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
8724        // so the gate is non-disruptive against every existing manifest.
8725        let c = caixa_with_etiquetas(vec![]);
8726        c.validate_etiquetas().unwrap();
8727    }
8728
8729    #[test]
8730    fn validate_etiquetas_accepts_canonical_forms() {
8731        // Positive control sweep: a canonical-shaped non-empty distinct
8732        // tag list passes, mirroring the example checkout-aplicacao
8733        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
8734        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
8735        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
8736        c.validate_etiquetas().unwrap();
8737    }
8738
8739    #[test]
8740    fn validate_etiquetas_rejects_empty_entry() {
8741        // Canonical paste-from-blank-doc footgun. Without the gate the
8742        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
8743        // no-op tag indexing nothing in the future caixa-registry.
8744        let c = caixa_with_etiquetas(vec![""]);
8745        let err = c.validate_etiquetas().unwrap_err();
8746        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8747    }
8748
8749    #[test]
8750    fn validate_etiquetas_rejects_duplicate_entry() {
8751        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
8752        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
8753        // collect at chart render — a "second wins / one silently
8754        // disappears" shape divergent from every peer typed-graph set
8755        // gate. The duplicate-arm names the offending tag verbatim.
8756        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8757        let err = c.validate_etiquetas().unwrap_err();
8758        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8759            panic!("expected EtiquetaDuplicate, got {err:?}");
8760        };
8761        assert_eq!(etiqueta, "demo");
8762    }
8763
8764    #[test]
8765    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
8766        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
8767        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
8768        // structural "this entry has no value" defect dominates the
8769        // cross-entry uniqueness diagnostic. Mirrors the peer
8770        // empty-before-duplicate cascades on `:caracteristicas`
8771        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8772        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
8773        // `MembroDuplicate`).
8774        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
8775        let err = c.validate_etiquetas().unwrap_err();
8776        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8777    }
8778
8779    #[test]
8780    fn validate_etiquetas_duplicate_reports_first_collision() {
8781        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8782        // duplicate (the lexicographically-earliest offending position
8783        // — the second `"a"` at index 2 collides with the first `"a"`
8784        // at index 0), not the later `"b"` collision at index 3,
8785        // peer with every other first-collision diagnostic posture on
8786        // this surface (`validate_load_singularity_reports_first_collision`,
8787        // `validate_cleanup_singularity_reports_first_collision`).
8788        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
8789        let err = c.validate_etiquetas().unwrap_err();
8790        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8791            panic!("expected EtiquetaDuplicate, got {err:?}");
8792        };
8793        assert_eq!(etiqueta, "a");
8794    }
8795
8796    #[test]
8797    fn validate_etiquetas_case_sensitive() {
8798        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
8799        // mirroring the peer `:membros :caixa` / `:children :caixa`
8800        // exact-string-match discipline. The shape gate this routine
8801        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
8802        // grammar) accepts mixed case — crates.io's keyword rule is
8803        // "case-insensitive" at the index layer but admits mixed case
8804        // at the entry layer (the canonical Helm chart `keywords:`
8805        // shape is lowercase by convention, but the grammar admits
8806        // uppercase). Case-sensitivity at the duplicate-set layer
8807        // remains structural — two distinct strings are two distinct
8808        // entries.
8809        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
8810        c.validate_etiquetas().unwrap();
8811    }
8812
8813    #[test]
8814    fn validate_etiquetas_diagnostic_carries_offending_tag() {
8815        // Diagnostic-shape pin (peer with
8816        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
8817        // the error's Display surfaces the offending tag verbatim, so a
8818        // `feira lint` run can render the diagnostic without re-parsing
8819        // and the author can grep their caixa.lisp for the offending
8820        // value.
8821        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8822        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8823        assert!(
8824            rendered.contains(":etiquetas"),
8825            "diagnostic must name the offending slot: {rendered}",
8826        );
8827        assert!(
8828            rendered.contains("demo"),
8829            "diagnostic must quote the offending tag: {rendered}",
8830        );
8831    }
8832
8833    #[test]
8834    fn validate_etiquetas_rejects_leading_whitespace_entry() {
8835        // Canonical paste-from-aligned-doc footgun. Without the shape
8836        // gate `" mesh"` silently passed validate and landed as a
8837        // YAML plain-style scalar with leading whitespace in the
8838        // rendered Chart.yaml `keywords:` array — every YAML 1.2
8839        // dumper trims leading whitespace from plain-style scalars,
8840        // so the authored space round-tripped inconsistently back
8841        // through `caixa.lisp`. Mirrors the peer
8842        // `validate_autores_rejects_leading_whitespace_entry`.
8843        let c = caixa_with_etiquetas(vec![" mesh"]);
8844        let err = c.validate_etiquetas().unwrap_err();
8845        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8846            panic!("expected EtiquetaInvalid, got {err:?}");
8847        };
8848        assert_eq!(etiqueta, " mesh");
8849        assert!(reason.contains("whitespace"), "got: {reason}");
8850    }
8851
8852    #[test]
8853    fn validate_etiquetas_rejects_embedded_newline_entry() {
8854        // Canonical paste-from-multiline-doc footgun — the author
8855        // pasted a multi-tag block into one `:etiquetas` entry
8856        // instead of splitting into one entry per tag. Without the
8857        // shape gate `"mesh\nhttp"` silently passed validate and
8858        // landed as a YAML-illegal multi-line scalar in the rendered
8859        // Chart.yaml `keywords:` array.
8860        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8861        let err = c.validate_etiquetas().unwrap_err();
8862        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8863            panic!("expected EtiquetaInvalid, got {err:?}");
8864        };
8865        assert_eq!(etiqueta, "mesh\nhttp");
8866        assert!(reason.contains("newline"), "got: {reason}");
8867    }
8868
8869    #[test]
8870    fn validate_etiquetas_rejects_embedded_comma_entry() {
8871        // Canonical CSV-list-separator-confusion footgun: the author
8872        // confused the CSV-style separator convention with the
8873        // `:etiquetas` list grammar. Without the shape gate
8874        // `"mesh,http,grpc"` silently passed validate and landed as a
8875        // single malformed search tag in the rendered Chart.yaml
8876        // `keywords:` array — Artifact Hub's keyword index would
8877        // either silently drop the tag or index it as
8878        // `mesh,http,grpc` instead of three separate tags.
8879        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
8880        let err = c.validate_etiquetas().unwrap_err();
8881        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8882            panic!("expected EtiquetaInvalid, got {err:?}");
8883        };
8884        assert_eq!(etiqueta, "mesh,http,grpc");
8885        assert!(reason.contains('`'), "got: {reason}");
8886        assert!(reason.contains(','), "got: {reason}");
8887    }
8888
8889    #[test]
8890    fn validate_etiquetas_rejects_embedded_slash_entry() {
8891        // Canonical path-separator-confusion footgun: the author
8892        // confused namespace-path notation with the keyword grammar.
8893        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
8894        let err = c.validate_etiquetas().unwrap_err();
8895        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8896            panic!("expected EtiquetaInvalid, got {err:?}");
8897        };
8898        assert_eq!(etiqueta, "caixa/servico");
8899        assert!(reason.contains('/'), "got: {reason}");
8900    }
8901
8902    #[test]
8903    fn validate_etiquetas_rejects_leading_digit_entry() {
8904        // Canonical paste-from-numbered-list footgun: the author
8905        // copied `1. mesh` from a numbered doc and the `1` leaked
8906        // into the tag.
8907        let c = caixa_with_etiquetas(vec!["1mesh"]);
8908        let err = c.validate_etiquetas().unwrap_err();
8909        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8910            panic!("expected EtiquetaInvalid, got {err:?}");
8911        };
8912        assert_eq!(etiqueta, "1mesh");
8913        assert!(reason.contains("digit"), "got: {reason}");
8914    }
8915
8916    #[test]
8917    fn validate_etiquetas_rejects_leading_hyphen_entry() {
8918        // Canonical kebab-leak footgun.
8919        let c = caixa_with_etiquetas(vec!["-foo"]);
8920        let err = c.validate_etiquetas().unwrap_err();
8921        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8922            panic!("expected EtiquetaInvalid, got {err:?}");
8923        };
8924        assert_eq!(etiqueta, "-foo");
8925        assert!(reason.contains('-'), "got: {reason}");
8926    }
8927
8928    #[test]
8929    fn validate_etiquetas_rejects_non_ascii_entry() {
8930        // Canonical paste-from-Unicode-doc footgun. Every legitimate
8931        // search tag is strict ASCII; raw non-ASCII silently
8932        // round-trips inconsistently across NFC/NFD normalization on
8933        // APFS / case-folding filesystems and breaks the Artifact Hub
8934        // keyword search index lookup.
8935        let c = caixa_with_etiquetas(vec!["café"]);
8936        let err = c.validate_etiquetas().unwrap_err();
8937        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8938            panic!("expected EtiquetaInvalid, got {err:?}");
8939        };
8940        assert_eq!(etiqueta, "café");
8941        assert!(reason.contains("non-ASCII"), "got: {reason}");
8942    }
8943
8944    #[test]
8945    fn validate_etiquetas_rejects_period_entry() {
8946        // Canonical namespace-confusion / version-suffix footgun
8947        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
8948        // excludes `.` from the continuation set even though the
8949        // sibling `:caracteristicas` axis (Cargo's feature-name
8950        // grammar) admits it. Tighter than the sibling axis, peer
8951        // with Cargo's own crates.io keyword shape.
8952        let c = caixa_with_etiquetas(vec!["http.1"]);
8953        let err = c.validate_etiquetas().unwrap_err();
8954        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8955            panic!("expected EtiquetaInvalid, got {err:?}");
8956        };
8957        assert_eq!(etiqueta, "http.1");
8958        assert!(reason.contains('.'), "got: {reason}");
8959    }
8960
8961    #[test]
8962    fn validate_etiquetas_empty_takes_precedence_over_shape() {
8963        // Per-entry empty-first cascade pin: an entry that is both
8964        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
8965        // narrower "this entry has no value" structural defect
8966        // dominates the broader shape-predicate diagnostic). The
8967        // empty arm fires before the shape predicate is consulted,
8968        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
8969        // cascade established on the sibling universal-axis Vec<String>
8970        // surface.
8971        let c = caixa_with_etiquetas(vec![""]);
8972        let err = c.validate_etiquetas().unwrap_err();
8973        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8974    }
8975
8976    #[test]
8977    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
8978        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
8979        // entry that is malformed surfaces `EtiquetaInvalid` even when
8980        // a later entry would have collided on duplicate. The
8981        // per-entry shape arm fires inside the same loop iteration as
8982        // the empty arm, before the seen-set insert at end-of-iteration
8983        // — structural per-entry defects dominate the cross-entry
8984        // uniqueness diagnostic. Mirrors the peer
8985        // `validate_autores_shape_takes_precedence_over_duplicate`.
8986        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
8987        let err = c.validate_etiquetas().unwrap_err();
8988        assert!(
8989            matches!(err, ManifestError::EtiquetaInvalid { .. }),
8990            "got {err:?}",
8991        );
8992    }
8993
8994    #[test]
8995    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
8996        // Diagnostic-shape pin on the new shape arm (peer with
8997        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
8998        // the rendered Display surfaces both the offending slot name
8999        // and the offending value verbatim, so a `feira lint` run
9000        // points the author at the exact `:etiquetas` entry to fix.
9001        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
9002        let rendered = c.validate_etiquetas().unwrap_err().to_string();
9003        assert!(
9004            rendered.contains(":etiquetas"),
9005            "diagnostic must name the offending slot: {rendered}",
9006        );
9007        assert!(
9008            rendered.contains("mesh\\nhttp"),
9009            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9010        );
9011    }
9012
9013    #[test]
9014    fn validate_etiquetas_rejects_at_21_byte_boundary() {
9015        // The 20-byte cap pin — boundary-exceeding case rejected,
9016        // boundary-accepting case passes. Mirrors the peer
9017        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
9018        // side pin, surfaced at the per-axis caller so the cap
9019        // propagates through validate end-to-end. Constructed as a
9020        // single all-`a` token so only the cap arm fires.
9021        let max_ok = "a".repeat(20);
9022        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
9023        c.validate_etiquetas().unwrap();
9024        let too_long = "a".repeat(21);
9025        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
9026        let err = c.validate_etiquetas().unwrap_err();
9027        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
9028            panic!("expected EtiquetaInvalid, got {err:?}");
9029        };
9030        assert!(reason.contains("20"), "got: {reason}");
9031        assert!(reason.contains("21"), "got: {reason}");
9032    }
9033
9034    #[test]
9035    fn validate_etiquetas_accepts_canonical_shaped_forms() {
9036        // Positive control sweep: every canonical-shaped tag from the
9037        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
9038        // example fixtures plus the substrate-fixed tags caixa-helm
9039        // unions in at chart render. Drift between this list and the
9040        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
9041        // sweep surfaces here — one source of truth for the rule.
9042        let c = caixa_with_etiquetas(vec![
9043            "example",
9044            "aplicacao",
9045            "mesh",
9046            "ecommerce",
9047            "demo",
9048            "infrastructure",
9049            "aws",
9050            "akeyless",
9051            "pangea-native",
9052            "hello-world",
9053            "wasm",
9054            "rust",
9055            "tatara-lisp",
9056            "caixa-servico",
9057            "lareira",
9058        ]);
9059        c.validate_etiquetas().unwrap();
9060    }
9061
9062    // ── validate_autores — universal-axis maintainer shape ────────────
9063
9064    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
9065        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9066        c.autores = autores.into_iter().map(String::from).collect();
9067        c
9068    }
9069
9070    #[test]
9071    fn validate_autores_accepts_empty_list() {
9072        // The empty-list identity: `Caixa::template` emits `:autores ()`,
9073        // so the gate is non-disruptive against every existing manifest.
9074        let c = caixa_with_autores(vec![]);
9075        c.validate_autores().unwrap();
9076    }
9077
9078    #[test]
9079    fn validate_autores_accepts_canonical_forms() {
9080        // Positive control sweep: every canonical-shaped non-empty
9081        // distinct maintainer list passes — the hello-rio / checkout-
9082        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
9083        // multi-author shape downstream packaging surfaces emit.
9084        let c = caixa_with_autores(vec!["pleme-io"]);
9085        c.validate_autores().unwrap();
9086        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
9087        c.validate_autores().unwrap();
9088    }
9089
9090    #[test]
9091    fn validate_autores_rejects_empty_entry() {
9092        // Canonical paste-from-blank-doc footgun. Without the gate the
9093        // empty entry rendered as `maintainers: [{name: "", email: null}]`
9094        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
9095        // to.
9096        let c = caixa_with_autores(vec![""]);
9097        let err = c.validate_autores().unwrap_err();
9098        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9099    }
9100
9101    #[test]
9102    fn validate_autores_rejects_duplicate_entry() {
9103        // Canonical copy-paste-the-wrong-author footgun. Unlike the
9104        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
9105        // dedups the rendered `keywords:` array), the `maintainers:`
9106        // rendering has *no* dedup — duplicates stack verbatim. The
9107        // duplicate-arm names the offending author verbatim.
9108        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
9109        let err = c.validate_autores().unwrap_err();
9110        let ManifestError::AutorDuplicate { autor } = err else {
9111            panic!("expected AutorDuplicate, got {err:?}");
9112        };
9113        assert_eq!(autor, "pleme-io");
9114    }
9115
9116    #[test]
9117    fn validate_autores_empty_takes_precedence_over_duplicate() {
9118        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
9119        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
9120        // "this entry has no value" defect dominates the cross-entry
9121        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
9122        // cascades on `:etiquetas` (`EtiquetaEmpty` before
9123        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
9124        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
9125        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
9126        // `MembroDuplicate`).
9127        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
9128        let err = c.validate_autores().unwrap_err();
9129        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9130    }
9131
9132    #[test]
9133    fn validate_autores_duplicate_reports_first_collision() {
9134        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
9135        // duplicate (the lexicographically-earliest offending position
9136        // — the second `"a"` at index 2 collides with the first `"a"`
9137        // at index 0), not the later `"b"` collision at index 3,
9138        // peer with every other first-collision diagnostic posture on
9139        // this surface.
9140        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
9141        let err = c.validate_autores().unwrap_err();
9142        let ManifestError::AutorDuplicate { autor } = err else {
9143            panic!("expected AutorDuplicate, got {err:?}");
9144        };
9145        assert_eq!(autor, "a");
9146    }
9147
9148    #[test]
9149    fn validate_autores_case_sensitive() {
9150        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
9151        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
9152        // / `:children :caixa` exact-string-match discipline.
9153        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
9154        c.validate_autores().unwrap();
9155    }
9156
9157    #[test]
9158    fn validate_autores_diagnostic_carries_offending_author() {
9159        // Diagnostic-shape pin (peer with
9160        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
9161        // error's Display surfaces the offending author verbatim, so a
9162        // `feira lint` run can render the diagnostic without re-parsing
9163        // and the author can grep their caixa.lisp for the offending
9164        // value.
9165        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
9166        let rendered = c.validate_autores().unwrap_err().to_string();
9167        assert!(
9168            rendered.contains(":autores"),
9169            "diagnostic must name the offending slot: {rendered}",
9170        );
9171        assert!(
9172            rendered.contains("pleme-io"),
9173            "diagnostic must quote the offending author: {rendered}",
9174        );
9175    }
9176
9177    #[test]
9178    fn validate_autores_rejects_leading_whitespace_entry() {
9179        // Canonical paste-from-aligned-doc footgun. Without the shape
9180        // gate `" pleme-io"` silently passed validate and landed as a
9181        // YAML plain-style scalar with leading whitespace in the
9182        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
9183        // dumper trims leading whitespace from plain-style scalars, so
9184        // the authored space round-tripped inconsistently back through
9185        // `caixa.lisp`. Mirrors the peer
9186        // `validate_descricao_rejects_leading_whitespace`.
9187        let c = caixa_with_autores(vec![" pleme-io"]);
9188        let err = c.validate_autores().unwrap_err();
9189        let ManifestError::AutorInvalid { autor, reason } = err else {
9190            panic!("expected AutorInvalid, got {err:?}");
9191        };
9192        assert_eq!(autor, " pleme-io");
9193        assert!(reason.contains("whitespace"), "got: {reason}");
9194    }
9195
9196    #[test]
9197    fn validate_autores_rejects_trailing_whitespace_entry() {
9198        // Canonical paste-from-doc footgun.
9199        let c = caixa_with_autores(vec!["pleme-io "]);
9200        let err = c.validate_autores().unwrap_err();
9201        let ManifestError::AutorInvalid { autor, reason } = err else {
9202            panic!("expected AutorInvalid, got {err:?}");
9203        };
9204        assert_eq!(autor, "pleme-io ");
9205        assert!(reason.contains("whitespace"), "got: {reason}");
9206    }
9207
9208    #[test]
9209    fn validate_autores_rejects_embedded_newline_entry() {
9210        // Canonical paste-from-multiline-doc footgun — the author
9211        // pasted a multi-line block of author records into one
9212        // `:autores` entry instead of splitting into one entry per
9213        // author. Without the shape gate `"alice\nbob"` silently
9214        // passed validate and landed as a YAML-illegal multi-line
9215        // scalar in the rendered Chart.yaml `maintainers:` array.
9216        let c = caixa_with_autores(vec!["alice\nbob"]);
9217        let err = c.validate_autores().unwrap_err();
9218        let ManifestError::AutorInvalid { autor, reason } = err else {
9219            panic!("expected AutorInvalid, got {err:?}");
9220        };
9221        assert_eq!(autor, "alice\nbob");
9222        assert!(reason.contains("newline"), "got: {reason}");
9223    }
9224
9225    #[test]
9226    fn validate_autores_rejects_embedded_carriage_return_entry() {
9227        // Canonical paste-from-Windows-CRLF-doc footgun.
9228        let c = caixa_with_autores(vec!["alice\rbob"]);
9229        let err = c.validate_autores().unwrap_err();
9230        let ManifestError::AutorInvalid { autor, reason } = err else {
9231            panic!("expected AutorInvalid, got {err:?}");
9232        };
9233        assert_eq!(autor, "alice\rbob");
9234        assert!(reason.contains("carriage return"), "got: {reason}");
9235    }
9236
9237    #[test]
9238    fn validate_autores_rejects_embedded_tab_entry() {
9239        // Canonical tab-from-aligned-doc footgun.
9240        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
9241        let err = c.validate_autores().unwrap_err();
9242        let ManifestError::AutorInvalid { autor, reason } = err else {
9243            panic!("expected AutorInvalid, got {err:?}");
9244        };
9245        assert_eq!(autor, "Pleme\tContributors");
9246        assert!(reason.contains("tab"), "got: {reason}");
9247    }
9248
9249    #[test]
9250    fn validate_autores_rejects_embedded_control_bytes_entry() {
9251        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
9252        // surface the same control-byte arm.
9253        for entry in [
9254            "alice\x00bob",
9255            "alice\x07bob",
9256            "alice\x1bbob",
9257            "alice\x7fbob",
9258        ] {
9259            let c = caixa_with_autores(vec![entry]);
9260            let err = c.validate_autores().unwrap_err();
9261            let ManifestError::AutorInvalid { autor, reason } = err else {
9262                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
9263            };
9264            assert_eq!(autor, entry);
9265            assert!(
9266                reason.contains("control character"),
9267                "{entry:?} reason: {reason}",
9268            );
9269        }
9270    }
9271
9272    #[test]
9273    fn validate_autores_accepts_unicode_entry() {
9274        // Unicode positive control: realistic maintainer names carry
9275        // Unicode (`François`, `日本語`, `naïve`). The predicate must
9276        // round-trip Unicode losslessly, peer with the
9277        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
9278        // sweep.
9279        let c = caixa_with_autores(vec![
9280            "François Dupont",
9281            "日本語の名前",
9282            "naïve <naive@example.com>",
9283        ]);
9284        c.validate_autores().unwrap();
9285    }
9286
9287    #[test]
9288    fn validate_autores_empty_takes_precedence_over_shape() {
9289        // Per-entry empty-first cascade pin: an entry that is both
9290        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
9291        // "this entry has no value" structural defect dominates the
9292        // broader shape-predicate diagnostic). The empty arm fires
9293        // before the shape predicate is consulted, mirroring the peer
9294        // `validate_repositorio_empty_takes_precedence_over_shape`
9295        // cascade on the universal `Option<String>` siblings — and now
9296        // established on the Vec<String> per-entry surface.
9297        let c = caixa_with_autores(vec![""]);
9298        let err = c.validate_autores().unwrap_err();
9299        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9300    }
9301
9302    #[test]
9303    fn validate_autores_shape_takes_precedence_over_duplicate() {
9304        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
9305        // entry that is malformed surfaces `AutorInvalid` even when a
9306        // later entry would have collided on duplicate. The per-entry
9307        // shape arm fires inside the same loop iteration as the empty
9308        // arm, before the seen-set insert at end-of-iteration —
9309        // structural per-entry defects dominate the cross-entry
9310        // uniqueness diagnostic.
9311        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
9312        let err = c.validate_autores().unwrap_err();
9313        assert!(
9314            matches!(err, ManifestError::AutorInvalid { .. }),
9315            "got {err:?}",
9316        );
9317    }
9318
9319    #[test]
9320    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
9321        // Diagnostic-shape pin on the new shape arm (peer with
9322        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
9323        // the rendered Display surfaces both the offending slot name
9324        // and the offending value verbatim, so a `feira lint` run
9325        // points the author at the exact `:autores` entry to fix.
9326        let c = caixa_with_autores(vec!["alice\nbob"]);
9327        let rendered = c.validate_autores().unwrap_err().to_string();
9328        assert!(
9329            rendered.contains(":autores"),
9330            "diagnostic must name the offending slot: {rendered}",
9331        );
9332        assert!(
9333            rendered.contains("alice\\nbob"),
9334            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9335        );
9336    }
9337
9338    #[test]
9339    fn validate_autores_rejects_at_129_byte_boundary() {
9340        // The 128-byte cap pin — boundary-exceeding case rejected,
9341        // boundary-accepting case passes. Mirrors the peer
9342        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
9343        // substrate-side pin, surfaced at the per-axis caller so the
9344        // cap propagates through validate end-to-end. Constructed as
9345        // a single all-`a` token so only the cap arm fires.
9346        let max_ok = "a".repeat(128);
9347        let c = caixa_with_autores(vec![max_ok.as_str()]);
9348        c.validate_autores().unwrap();
9349        let too_long = "a".repeat(129);
9350        let c = caixa_with_autores(vec![too_long.as_str()]);
9351        let err = c.validate_autores().unwrap_err();
9352        let ManifestError::AutorInvalid { reason, .. } = err else {
9353            panic!("expected AutorInvalid, got {err:?}");
9354        };
9355        assert!(reason.contains("128"), "got: {reason}");
9356        assert!(reason.contains("129"), "got: {reason}");
9357    }
9358
9359    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
9360
9361    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
9362        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9363        c.repositorio = repositorio.map(String::from);
9364        c
9365    }
9366
9367    #[test]
9368    fn validate_repositorio_accepts_none() {
9369        // The omit-the-slot identity: `:repositorio` is optional. The
9370        // gate is a no-op when the author didn't declare a value —
9371        // every caixa without a `:repositorio` line trivially passes,
9372        // and the substrate-side renderers fall back to their
9373        // documented placeholder (`caixa-helm`'s `home: None`,
9374        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
9375        // URL). Mirrors the peer `validate_restart_window_accepts_none`
9376        // posture on the other `Option<String>` Caixa slot.
9377        let c = caixa_with_repositorio(None);
9378        c.validate_repositorio().unwrap();
9379    }
9380
9381    #[test]
9382    fn validate_repositorio_accepts_canonical_forms() {
9383        // Positive control sweep across every documented `:repositorio`
9384        // authoring shape — the same union the shared
9385        // `crate::render::is_git_repo_url` predicate accepts and the
9386        // peer `:deps :fonte :repo` axis already routes through.
9387        // Covers the `github:` shorthand (the canonical pleme-io
9388        // convention used in the `:repositorio` field of every
9389        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
9390        // `examples/`), the `https://…` URL the README quickstart uses,
9391        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
9392        // `file://` URL schemes the shared predicate documents.
9393        for repo in [
9394            "github:pleme-io/hello-rio",
9395            "github:pleme-io/checkout",
9396            "https://github.com/pleme-io/hello-rio",
9397            "ssh://git@github.com/pleme-io/hello-rio.git",
9398            "git://github.com/pleme-io/hello-rio.git",
9399            "git@github.com:pleme-io/hello-rio.git",
9400            "file:///srv/pleme/hello-rio",
9401        ] {
9402            let c = caixa_with_repositorio(Some(repo));
9403            c.validate_repositorio()
9404                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
9405        }
9406    }
9407
9408    #[test]
9409    fn validate_repositorio_rejects_empty_some() {
9410        // Canonical paste-from-blank-doc footgun. The narrower
9411        // [`ManifestError::RepositorioEmpty`] arm fires before the
9412        // shape predicate is consulted, mirroring the empty-first
9413        // cascade every peer per-axis identity gate uses
9414        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9415        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
9416        // the empty `Some("")` silently passed the renderer's
9417        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9418        // on `None`) and landed as `home: ""` in `Chart.yaml` /
9419        // `url: ""` in the FluxCD `GitRepository`.
9420        let c = caixa_with_repositorio(Some(""));
9421        let err = c.validate_repositorio().unwrap_err();
9422        assert!(
9423            matches!(err, ManifestError::RepositorioEmpty),
9424            "got {err:?}",
9425        );
9426    }
9427
9428    #[test]
9429    fn validate_repositorio_rejects_whitespace() {
9430        // Paste-from-doc whitespace footgun. The shared
9431        // `is_git_repo_url` predicate refuses any whitespace byte; a
9432        // trailing space in a `:repositorio` value silently broke
9433        // `git clone '<value> '` at clone time. The diagnostic names
9434        // the offending value verbatim.
9435        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
9436        let err = c.validate_repositorio().unwrap_err();
9437        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
9438            panic!("expected RepositorioInvalid, got {err:?}");
9439        };
9440        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
9441    }
9442
9443    #[test]
9444    fn validate_repositorio_rejects_control_char() {
9445        // Paste-from-multiline-doc CRLF footgun — control characters
9446        // at the URL boundary are a class of subprocess-arg injection
9447        // and break git's URL parser at every porcelain entry point.
9448        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
9449        let err = c.validate_repositorio().unwrap_err();
9450        assert!(
9451            matches!(err, ManifestError::RepositorioInvalid { .. }),
9452            "got {err:?}",
9453        );
9454    }
9455
9456    #[test]
9457    fn validate_repositorio_rejects_leading_dash() {
9458        // Canonical CLI-argument-injection footgun: `git clone <repo>`
9459        // interprets a leading `-` as a CLI flag, so a
9460        // `-upload-pack=…` value escapes the subprocess argument
9461        // boundary. The shared predicate refuses every leading-`-`
9462        // shape at validate time.
9463        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
9464        let err = c.validate_repositorio().unwrap_err();
9465        assert!(
9466            matches!(err, ManifestError::RepositorioInvalid { .. }),
9467            "got {err:?}",
9468        );
9469    }
9470
9471    #[test]
9472    fn validate_repositorio_rejects_missing_colon_separator() {
9473        // The bare `org/repo` ambiguity footgun — `git clone` reads
9474        // a no-`:` form as a relative filesystem path rather than the
9475        // GitHub-shorthand expansion the author probably intended.
9476        // The shared predicate refuses every shape without a `:`
9477        // separator.
9478        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9479        let err = c.validate_repositorio().unwrap_err();
9480        assert!(
9481            matches!(err, ManifestError::RepositorioInvalid { .. }),
9482            "got {err:?}",
9483        );
9484    }
9485
9486    #[test]
9487    fn validate_repositorio_rejects_fragment_anchor() {
9488        // Paste-from-browser-address-bar footgun on the
9489        // `:repositorio` axis — an author copies a GitHub permalink
9490        // to a README section / line-permalink and forgets to trim
9491        // the `#fragment` tail. The shared `is_git_repo_url`
9492        // predicate refuses the byte at the URL-grammar layer
9493        // (libcurl strips the fragment before opening the
9494        // transport, so the byte rides verbatim into the rendered
9495        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
9496        // fields but is silently dropped on the wire — two
9497        // manifest variants whose values differ only in their
9498        // fragment anchor lock to two distinct rendered artifacts
9499        // for the byte-identical clone, defeating the THEORY.md
9500        // §V.2 render-determinism contract on the `:repositorio`
9501        // axis the peer `:fonte :repo` axis already closes).
9502        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
9503        let err = c.validate_repositorio().unwrap_err();
9504        let ManifestError::RepositorioInvalid {
9505            repositorio,
9506            reason,
9507        } = err
9508        else {
9509            panic!("expected RepositorioInvalid, got {err:?}");
9510        };
9511        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
9512        assert!(
9513            reason.contains("must not contain `#`"),
9514            "reason must surface the fragment-`#` arm, got {reason:?}"
9515        );
9516    }
9517
9518    #[test]
9519    fn validate_repositorio_rejects_query_string() {
9520        // Paste-from-browser-address-bar footgun on the
9521        // `:repositorio` axis (peer with the a68f818 fragment-`#`
9522        // arm on the same axis). An author copies a GitHub tab
9523        // deep-link out of the address bar and forgets to trim
9524        // the `?tab=…` query tail. The shared `is_git_repo_url`
9525        // predicate refuses the byte at the URL-grammar layer
9526        // (GitHub / GitLab / Bitbucket silently ignore the
9527        // `?query` tail and serve the same repo regardless, so
9528        // the byte rides verbatim into the rendered `Chart.yaml`
9529        // `home:` and FluxCD `GitRepository` `url:` fields but
9530        // is silently masked at the wire — two manifest variants
9531        // whose values differ only in their query tail lock to
9532        // two distinct rendered artifacts for the byte-identical
9533        // clone, defeating the THEORY.md §V.2 render-determinism
9534        // contract on the `:repositorio` axis the peer `:fonte
9535        // :repo` axis already closes).
9536        let c = caixa_with_repositorio(Some(
9537            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
9538        ));
9539        let err = c.validate_repositorio().unwrap_err();
9540        let ManifestError::RepositorioInvalid {
9541            repositorio,
9542            reason,
9543        } = err
9544        else {
9545            panic!("expected RepositorioInvalid, got {err:?}");
9546        };
9547        assert_eq!(
9548            repositorio,
9549            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
9550        );
9551        assert!(
9552            reason.contains("must not contain `?`"),
9553            "reason must surface the query-`?` arm, got {reason:?}"
9554        );
9555    }
9556
9557    #[test]
9558    fn validate_repositorio_rejects_embedded_backslash() {
9559        // Windows-file-path-confusion footgun on the `:repositorio`
9560        // axis (peer with the prior fragment-`#` / query-`?` arms on
9561        // the same axis, and peer with the new dep-level `:fonte :repo`
9562        // backslash arm on the URL-grammar trajectory). An author
9563        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
9564        // hello-rio` into the `:repositorio` slot, expecting the
9565        // `lareira-<nome>` chart's `home:` field and the FluxCD
9566        // `GitRepository` `url:` field to render the canonical local
9567        // file-URI. The shared `is_git_repo_url` predicate refuses
9568        // the byte at the URL-grammar layer (libcurl silently
9569        // translates `\` → `/` on some platforms and refuses it on
9570        // others, so the byte rides verbatim into the rendered
9571        // artifacts but is silently rewritten or rejected at the wire
9572        // — two manifest variants whose values differ only in
9573        // backslash-vs-forward-slash lock to two distinct rendered
9574        // artifacts for the byte-identical clone, defeating the
9575        // THEORY.md §V.2 render-determinism contract on the
9576        // `:repositorio` axis the peer `:fonte :repo` axis already
9577        // closes).
9578        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
9579        let err = c.validate_repositorio().unwrap_err();
9580        let ManifestError::RepositorioInvalid {
9581            repositorio,
9582            reason,
9583        } = err
9584        else {
9585            panic!("expected RepositorioInvalid, got {err:?}");
9586        };
9587        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
9588        assert!(
9589            reason.contains("must not contain `\\`"),
9590            "reason must surface the backslash-`\\` arm, got {reason:?}"
9591        );
9592    }
9593
9594    #[test]
9595    fn validate_repositorio_rejects_uri_template_placeholder() {
9596        // URI Template (RFC 6570) placeholder footgun on the
9597        // `:repositorio` axis (peer with the prior fragment-`#` /
9598        // query-`?` / backslash-`\` arms on the same axis, and peer
9599        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
9600        // URL-grammar trajectory). An author pastes a quick-start
9601        // README snippet / OpenAPI `servers:` URL / Helm chart
9602        // `home:` template carrying unresolved `{org}` / `{repo}`
9603        // placeholders into the `:repositorio` slot, expecting the
9604        // substrate to resolve the placeholder downstream. The
9605        // shared `is_git_repo_url` predicate refuses the byte at the
9606        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
9607        // `%7B` / `%7D` on the wire, so the byte round-trips
9608        // inconsistently between the rendered `Chart.yaml home:` /
9609        // FluxCD `GitRepository url:` and the resolver's `git clone`
9610        // invocation, defeating the THEORY.md §V.2 render-
9611        // determinism contract on the `:repositorio` axis the peer
9612        // `:fonte :repo` axis already closes; every git porcelain
9613        // entry-point additionally fetches a nonexistent literal-
9614        // `{placeholder}`-named path far from the source caixa.lisp).
9615        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
9616        let err = c.validate_repositorio().unwrap_err();
9617        let ManifestError::RepositorioInvalid {
9618            repositorio,
9619            reason,
9620        } = err
9621        else {
9622            panic!("expected RepositorioInvalid, got {err:?}");
9623        };
9624        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
9625        assert!(
9626            reason.contains("must not contain `{`"),
9627            "reason must surface the open-brace `{{` arm, got {reason:?}"
9628        );
9629        assert!(
9630            reason.contains("URI Template") || reason.contains("RFC 6570"),
9631            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
9632        );
9633    }
9634
9635    #[test]
9636    fn validate_repositorio_empty_takes_precedence_over_shape() {
9637        // Empty-first cascade pin: the empty `Some("")` surfaces the
9638        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
9639        // `RepositorioInvalid`, mirroring the peer
9640        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9641        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
9642        // `is_git_repo_url` predicate also rejects the empty input
9643        // (defensively, with its own `"must not be empty"` reason),
9644        // but the manifest-layer empty arm runs first to surface the
9645        // narrower diagnostic verbatim.
9646        let c = caixa_with_repositorio(Some(""));
9647        let err = c.validate_repositorio().unwrap_err();
9648        assert!(
9649            matches!(err, ManifestError::RepositorioEmpty),
9650            "got {err:?}",
9651        );
9652    }
9653
9654    #[test]
9655    fn validate_repositorio_diagnostic_carries_offending_value() {
9656        // Diagnostic-shape pin (peer with
9657        // `validate_autores_diagnostic_carries_offending_author`): the
9658        // error's Display surfaces the offending value + slot name
9659        // verbatim, so a `feira lint` run can render the diagnostic
9660        // without re-parsing and the author can grep their caixa.lisp
9661        // for the offending `:repositorio` value.
9662        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9663        let rendered = c.validate_repositorio().unwrap_err().to_string();
9664        assert!(
9665            rendered.contains(":repositorio"),
9666            "diagnostic must name the offending slot: {rendered}",
9667        );
9668        assert!(
9669            rendered.contains("pleme-io/hello-rio"),
9670            "diagnostic must quote the offending value: {rendered}",
9671        );
9672    }
9673
9674    // ── validate_descricao — universal-axis Chart.yaml description shape ──
9675
9676    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
9677        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9678        c.descricao = descricao.map(String::from);
9679        c
9680    }
9681
9682    #[test]
9683    fn validate_descricao_accepts_none() {
9684        // The omit-the-slot identity: `:descricao` is optional. The
9685        // gate is a no-op when the author didn't declare a value —
9686        // every caixa without a `:descricao` line trivially passes,
9687        // and the substrate-side renderers fall back to their
9688        // documented `caixa.nome`-derived placeholder. Mirrors the
9689        // peer `validate_repositorio_accepts_none` posture on the
9690        // sibling `Option<String>` Caixa slot.
9691        let c = caixa_with_descricao(None);
9692        c.validate_descricao().unwrap();
9693    }
9694
9695    #[test]
9696    fn validate_descricao_accepts_canonical_summary() {
9697        // Positive control: the canonical pleme-io descricao shape —
9698        // a short free-form prose summary — passes the gate. Covers
9699        // the fixture shapes the `caixa-helm` / `caixa-flux` /
9700        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
9701        // wasip2 caixa Servico."`, `"Checkout flow."`).
9702        for desc in [
9703            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9704            "Checkout flow.",
9705            "AWS provider caixa for tatara-lisp",
9706            "FIXME — describe this caixa",
9707            "x",
9708        ] {
9709            let c = caixa_with_descricao(Some(desc));
9710            c.validate_descricao()
9711                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
9712        }
9713    }
9714
9715    #[test]
9716    fn validate_descricao_rejects_empty_some() {
9717        // Canonical paste-from-blank-doc footgun. Without this gate
9718        // the empty `Some("")` silently passed the renderer's
9719        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9720        // on `None`) and landed as `description: ""` in `Chart.yaml`
9721        // and a blank `README.md` header. Mirrors the peer
9722        // [`ManifestError::RepositorioEmpty`] empty-arm on the
9723        // sibling `Option<String>` Caixa slot.
9724        let c = caixa_with_descricao(Some(""));
9725        let err = c.validate_descricao().unwrap_err();
9726        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9727    }
9728
9729    #[test]
9730    fn validate_descricao_rejects_leading_whitespace() {
9731        // Paste-from-aligned-doc footgun: a leading ASCII space the
9732        // bare empty-arm gate accepted, the shape predicate now
9733        // refuses. The diagnostic carries the offending value
9734        // verbatim (with the leading space preserved) so the author
9735        // can grep their caixa.lisp for the exact `:descricao` line
9736        // and fix the round-trip-inconsistent leading whitespace.
9737        // Mirrors the peer
9738        // `validate_licenca_rejects_leading_whitespace` arm on the
9739        // sibling `:licenca` axis.
9740        let c = caixa_with_descricao(Some(" Checkout flow."));
9741        let err = c.validate_descricao().unwrap_err();
9742        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9743            panic!("expected DescricaoInvalid, got {err:?}");
9744        };
9745        assert_eq!(descricao, " Checkout flow.");
9746        assert!(reason.contains("whitespace"), "got: {reason:?}");
9747    }
9748
9749    #[test]
9750    fn validate_descricao_rejects_trailing_whitespace() {
9751        // Paste-from-doc footgun: a trailing ASCII space the bare
9752        // empty-arm gate accepted, the shape predicate now refuses.
9753        let c = caixa_with_descricao(Some("Checkout flow. "));
9754        let err = c.validate_descricao().unwrap_err();
9755        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9756            panic!("expected DescricaoInvalid, got {err:?}");
9757        };
9758        assert_eq!(descricao, "Checkout flow. ");
9759        assert!(reason.contains("whitespace"), "got: {reason:?}");
9760    }
9761
9762    #[test]
9763    fn validate_descricao_rejects_embedded_newline() {
9764        // Paste-from-multiline-doc footgun: an embedded LF the bare
9765        // empty-arm gate accepted, the shape predicate now refuses.
9766        // Without this gate the embedded newline silently landed in
9767        // the rendered Chart.yaml as a multi-line YAML block scalar,
9768        // and every chart-aware UI (`helm list`, `helm search`,
9769        // Artifact Hub) renders the description in a single-line
9770        // column so the embedded newline is silently dropped at
9771        // every downstream consumer.
9772        let c = caixa_with_descricao(Some("Checkout\nflow."));
9773        let err = c.validate_descricao().unwrap_err();
9774        assert!(
9775            matches!(err, ManifestError::DescricaoInvalid { .. }),
9776            "got {err:?}",
9777        );
9778        assert!(err.to_string().contains("newline"), "got {err}");
9779    }
9780
9781    #[test]
9782    fn validate_descricao_rejects_embedded_carriage_return() {
9783        // Paste-from-Windows-CRLF-doc footgun.
9784        let c = caixa_with_descricao(Some("Checkout\rflow."));
9785        let err = c.validate_descricao().unwrap_err();
9786        assert!(
9787            matches!(err, ManifestError::DescricaoInvalid { .. }),
9788            "got {err:?}",
9789        );
9790        assert!(err.to_string().contains("carriage return"), "got {err}");
9791    }
9792
9793    #[test]
9794    fn validate_descricao_rejects_embedded_tab() {
9795        // Tab-from-aligned-doc footgun.
9796        let c = caixa_with_descricao(Some("Checkout\tflow."));
9797        let err = c.validate_descricao().unwrap_err();
9798        assert!(
9799            matches!(err, ManifestError::DescricaoInvalid { .. }),
9800            "got {err:?}",
9801        );
9802        assert!(err.to_string().contains("tab"), "got {err}");
9803    }
9804
9805    #[test]
9806    fn validate_descricao_rejects_embedded_control_bytes() {
9807        // Paste-from-binary-blob footgun: every other control byte
9808        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
9809        // the peer SPDX-expression control-byte arm.
9810        for s in [
9811            "Checkout\x00flow.",
9812            "Checkout\x07flow.",
9813            "Checkout\x1bflow.",
9814            "Checkout\x7fflow.",
9815        ] {
9816            let c = caixa_with_descricao(Some(s));
9817            let err = c.validate_descricao().unwrap_err();
9818            assert!(
9819                matches!(err, ManifestError::DescricaoInvalid { .. }),
9820                "{s:?} got {err:?}",
9821            );
9822            assert!(
9823                err.to_string().contains("control character"),
9824                "{s:?} got {err}",
9825            );
9826        }
9827    }
9828
9829    #[test]
9830    fn validate_descricao_accepts_unicode_prose() {
9831        // Positive control: Unicode prose is accepted — the
9832        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
9833        // and `Caixa::template`'s `"FIXME — describe this caixa"`
9834        // scaffold every `feira init` emits must continue to pass.
9835        for s in [
9836            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9837            "FIXME — describe this caixa",
9838            "Caixa pour le projet tâche",
9839            "日本語の説明",
9840        ] {
9841            let c = caixa_with_descricao(Some(s));
9842            c.validate_descricao()
9843                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
9844        }
9845    }
9846
9847    #[test]
9848    fn validate_descricao_empty_takes_precedence_over_shape() {
9849        // Cascade pin: a `Some("")` surfaces the narrower
9850        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
9851        // shape-predicate arm. Mirrors the peer
9852        // `validate_licenca_empty_takes_precedence_over_shape` pin
9853        // on the sibling `:licenca` axis.
9854        let c = caixa_with_descricao(Some(""));
9855        let err = c.validate_descricao().unwrap_err();
9856        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9857    }
9858
9859    #[test]
9860    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
9861        // Diagnostic-shape pin: the error's Display surfaces both
9862        // the `:descricao` slot name and the offending value
9863        // verbatim, so a `feira lint` run can render the diagnostic
9864        // without re-parsing and the author can grep their caixa.lisp
9865        // for the offending `:descricao` line. Mirrors the peer
9866        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
9867        // pin (ee2e888) on the sibling `:licenca` axis.
9868        // The `{descricao:?}` Debug format escapes embedded control
9869        // bytes; the quoted offending value surfaces as
9870        // `"Checkout\nflow."` (literal backslash-n) in the rendered
9871        // diagnostic. The author can grep their caixa.lisp for the
9872        // literal `Checkout` summary prefix.
9873        let c = caixa_with_descricao(Some("Checkout\nflow."));
9874        let rendered = c.validate_descricao().unwrap_err().to_string();
9875        assert!(
9876            rendered.contains(":descricao"),
9877            "diagnostic must name the offending slot: {rendered}",
9878        );
9879        assert!(
9880            rendered.contains("Checkout\\nflow."),
9881            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9882        );
9883    }
9884
9885    #[test]
9886    fn validate_descricao_template_passes() {
9887        // Round-trip pin: the bare `Caixa::template` shape carries
9888        // `:descricao "FIXME — describe this caixa"` (a non-empty
9889        // sentinel), so the template-derived Caixa passes the gate by
9890        // construction. A future template-shape change that omits or
9891        // empties `:descricao` would surface here as a regression.
9892        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9893        c.validate_descricao().unwrap();
9894    }
9895
9896    #[test]
9897    fn validate_descricao_diagnostic_names_offending_slot() {
9898        // Diagnostic-shape pin (peer with
9899        // `validate_repositorio_diagnostic_carries_offending_value`):
9900        // the error's Display surfaces the `:descricao` slot name
9901        // verbatim, so a `feira lint` run can render the diagnostic
9902        // without re-parsing and the author can grep their caixa.lisp
9903        // for the offending `:descricao` line.
9904        let c = caixa_with_descricao(Some(""));
9905        let rendered = c.validate_descricao().unwrap_err().to_string();
9906        assert!(
9907            rendered.contains(":descricao"),
9908            "diagnostic must name the offending slot: {rendered}",
9909        );
9910    }
9911
9912    // ── validate_licenca — universal-axis chart README license shape ──
9913
9914    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
9915        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9916        c.licenca = licenca.map(String::from);
9917        c
9918    }
9919
9920    #[test]
9921    fn validate_licenca_accepts_none() {
9922        // The omit-the-slot identity: `:licenca` is optional. The
9923        // gate is a no-op when the author didn't declare a value —
9924        // every caixa without a `:licenca` line trivially passes,
9925        // and the substrate-side `caixa-helm` renderer falls back to
9926        // the documented `"MIT"` placeholder. Mirrors the peer
9927        // `validate_descricao_accepts_none` posture on the sibling
9928        // `Option<String>` Caixa slot.
9929        let c = caixa_with_licenca(None);
9930        c.validate_licenca().unwrap();
9931    }
9932
9933    #[test]
9934    fn validate_licenca_accepts_canonical_expressions() {
9935        // Positive control: every canonical SPDX expression shape
9936        // pleme-io carries in its existing fixtures + the canonical
9937        // SPDX dual-license / with-exception / `+`-suffix / grouped /
9938        // user-defined-reference shapes all pass the gate. Covers
9939        // the single-license, `OR`-compound, `AND`-compound,
9940        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
9941        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
9942        // production the SPDX 2.1 expression grammar admits that
9943        // sits within the alphabet floor the
9944        // `is_spdx_expression_shape` predicate enforces.
9945        for lic in [
9946            "MIT",
9947            "Apache-2.0",
9948            "Apache-2.0 OR MIT",
9949            "Apache-2.0 AND MIT",
9950            "BSD-3-Clause",
9951            "MPL-2.0",
9952            "GPL-3.0-or-later",
9953            "GPL-2.0+",
9954            "Apache-2.0 WITH LLVM-exception",
9955            "(MIT OR Apache-2.0) AND BSD-3-Clause",
9956            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
9957            "LicenseRef-MyLicense",
9958            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
9959            "x",
9960        ] {
9961            let c = caixa_with_licenca(Some(lic));
9962            c.validate_licenca()
9963                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
9964        }
9965    }
9966
9967    #[test]
9968    fn validate_licenca_rejects_trailing_whitespace() {
9969        // Paste-from-doc whitespace footgun. A trailing space in the
9970        // `:licenca` value would silently break a downstream SPDX
9971        // parser that splits on exact `AND` / `OR` / `WITH` keyword
9972        // boundaries. The shape predicate refuses every trailing
9973        // whitespace byte by construction. Peer with
9974        // `validate_repositorio_rejects_whitespace` and
9975        // `validate_edicao_rejects_trailing_whitespace`.
9976        let c = caixa_with_licenca(Some("MIT "));
9977        let err = c.validate_licenca().unwrap_err();
9978        let ManifestError::LicencaInvalid { licenca, .. } = err else {
9979            panic!("expected LicencaInvalid, got {err:?}");
9980        };
9981        assert_eq!(licenca, "MIT ");
9982    }
9983
9984    #[test]
9985    fn validate_licenca_rejects_leading_whitespace() {
9986        // Symmetric paste-from-doc whitespace footgun on the leading
9987        // boundary — the gate refuses every shape that starts with a
9988        // space byte by construction. Peer with
9989        // `validate_edicao_rejects_leading_whitespace`.
9990        let c = caixa_with_licenca(Some(" MIT"));
9991        let err = c.validate_licenca().unwrap_err();
9992        assert!(
9993            matches!(err, ManifestError::LicencaInvalid { .. }),
9994            "got {err:?}",
9995        );
9996    }
9997
9998    #[test]
9999    fn validate_licenca_rejects_control_char() {
10000        // Paste-from-multiline-doc CRLF footgun — control characters
10001        // at the value boundary land as a malformed line in the
10002        // rendered chart `README.md` `## License` section. Peer with
10003        // `validate_repositorio_rejects_control_char` and
10004        // `validate_edicao_rejects_control_char`.
10005        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
10006            let c = caixa_with_licenca(Some(lic));
10007            let err = c.validate_licenca().unwrap_err();
10008            assert!(
10009                matches!(err, ManifestError::LicencaInvalid { .. }),
10010                "expected LicencaInvalid on {lic:?}, got {err:?}",
10011            );
10012        }
10013    }
10014
10015    #[test]
10016    fn validate_licenca_rejects_tab() {
10017        // Tab-from-aligned-doc footgun — SPDX expressions use a
10018        // single ASCII space between tokens; a tab breaks every
10019        // downstream SPDX parser that splits on exact `" "`
10020        // boundaries.
10021        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
10022        let err = c.validate_licenca().unwrap_err();
10023        assert!(
10024            matches!(err, ManifestError::LicencaInvalid { .. }),
10025            "got {err:?}",
10026        );
10027    }
10028
10029    #[test]
10030    fn validate_licenca_rejects_non_ascii() {
10031        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
10032        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
10033        // ".")` production. The shape predicate refuses every
10034        // non-ASCII byte by construction; peer with
10035        // `validate_edicao_rejects_non_ascii_lookalike`.
10036        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
10037            let c = caixa_with_licenca(Some(lic));
10038            let err = c.validate_licenca().unwrap_err();
10039            assert!(
10040                matches!(err, ManifestError::LicencaInvalid { .. }),
10041                "expected LicencaInvalid on {lic:?}, got {err:?}",
10042            );
10043        }
10044    }
10045
10046    #[test]
10047    fn validate_licenca_rejects_underscore() {
10048        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
10049        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
10050        // snake-case identifier conventions that don't apply to the
10051        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
10052        // "-" / "."`). The shape predicate refuses every underscore
10053        // byte by construction.
10054        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
10055            let c = caixa_with_licenca(Some(lic));
10056            let err = c.validate_licenca().unwrap_err();
10057            assert!(
10058                matches!(err, ManifestError::LicencaInvalid { .. }),
10059                "expected LicencaInvalid on {lic:?}, got {err:?}",
10060            );
10061        }
10062    }
10063
10064    #[test]
10065    fn validate_licenca_rejects_comma_separator() {
10066        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
10067        // SPDX expressions compose multiple licenses via `AND` / `OR`
10068        // keywords, not the comma separator. The shape predicate
10069        // refuses every comma byte by construction.
10070        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
10071            let c = caixa_with_licenca(Some(lic));
10072            let err = c.validate_licenca().unwrap_err();
10073            assert!(
10074                matches!(err, ManifestError::LicencaInvalid { .. }),
10075                "expected LicencaInvalid on {lic:?}, got {err:?}",
10076            );
10077        }
10078    }
10079
10080    #[test]
10081    fn validate_licenca_rejects_slash_dual_license() {
10082        // Slash-dual-license colloquial idiom footgun — the
10083        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
10084        // `package.license` field but non-SPDX; the SPDX equivalent
10085        // is `MIT OR Apache-2.0`. The shape predicate refuses every
10086        // forward-slash byte by construction.
10087        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
10088            let c = caixa_with_licenca(Some(lic));
10089            let err = c.validate_licenca().unwrap_err();
10090            assert!(
10091                matches!(err, ManifestError::LicencaInvalid { .. }),
10092                "expected LicencaInvalid on {lic:?}, got {err:?}",
10093            );
10094        }
10095    }
10096
10097    #[test]
10098    fn validate_licenca_rejects_semicolon_separator() {
10099        // Semicolon-list-separator confusion footgun — adjacent to
10100        // the comma-separator idiom, every list-separator-belongs-
10101        // to-list-grammar confusion lands here.
10102        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
10103        let err = c.validate_licenca().unwrap_err();
10104        assert!(
10105            matches!(err, ManifestError::LicencaInvalid { .. }),
10106            "got {err:?}",
10107        );
10108    }
10109
10110    #[test]
10111    fn validate_licenca_empty_takes_precedence_over_shape() {
10112        // Empty-first cascade pin: the empty `Some("")` surfaces the
10113        // narrower `LicencaEmpty` not the shape-predicate-wrapped
10114        // `LicencaInvalid`, mirroring the peer
10115        // `validate_edicao_empty_takes_precedence_over_shape` and
10116        // `validate_repositorio_empty_takes_precedence_over_shape`
10117        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
10118        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
10119        // The shape predicate also refuses the empty input
10120        // (defensively — `"must not be empty"`), but the manifest-
10121        // layer empty arm runs first to surface the narrower
10122        // diagnostic verbatim.
10123        let c = caixa_with_licenca(Some(""));
10124        let err = c.validate_licenca().unwrap_err();
10125        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
10126    }
10127
10128    #[test]
10129    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
10130        // Diagnostic-shape pin on the shape-predicate arm (peer with
10131        // `validate_edicao_invalid_diagnostic_carries_offending_value`
10132        // and `validate_repositorio_diagnostic_carries_offending_value`):
10133        // the error's Display surfaces the offending value + slot
10134        // name verbatim, so a `feira lint` run can render the
10135        // diagnostic without re-parsing and the author can grep
10136        // their caixa.lisp for the offending `:licenca` value.
10137        let c = caixa_with_licenca(Some("Apache_2.0"));
10138        let rendered = c.validate_licenca().unwrap_err().to_string();
10139        assert!(
10140            rendered.contains(":licenca"),
10141            "diagnostic must name the offending slot: {rendered}",
10142        );
10143        assert!(
10144            rendered.contains("Apache_2.0"),
10145            "diagnostic must quote the offending value: {rendered}",
10146        );
10147    }
10148
10149    #[test]
10150    fn validate_licenca_rejects_empty_some() {
10151        // Canonical paste-from-blank-doc footgun. Without this gate
10152        // the empty `Some("")` silently passed the renderer's
10153        // `Option::unwrap_or_else(|| "MIT".into())` (which only
10154        // fires on `None`) and landed as a bare trailing period in
10155        // the rendered chart `README.md` `## License` section.
10156        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
10157        // arm on the sibling `Option<String>` Caixa slot.
10158        let c = caixa_with_licenca(Some(""));
10159        let err = c.validate_licenca().unwrap_err();
10160        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
10161    }
10162
10163    #[test]
10164    fn validate_licenca_template_passes() {
10165        // Round-trip pin: the bare `Caixa::template` shape (whether
10166        // it carries `:licenca` or omits it) passes the gate by
10167        // construction. A future template-shape change that
10168        // introduced `(:licenca "")` would surface here as a
10169        // regression. Mirrors the peer
10170        // `validate_descricao_template_passes` pin.
10171        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10172        c.validate_licenca().unwrap();
10173    }
10174
10175    #[test]
10176    fn validate_licenca_diagnostic_names_offending_slot() {
10177        // Diagnostic-shape pin (peer with
10178        // `validate_descricao_diagnostic_names_offending_slot`):
10179        // the error's Display surfaces the `:licenca` slot name
10180        // verbatim, so a `feira lint` run can render the diagnostic
10181        // without re-parsing and the author can grep their caixa.lisp
10182        // for the offending `:licenca` line.
10183        let c = caixa_with_licenca(Some(""));
10184        let rendered = c.validate_licenca().unwrap_err().to_string();
10185        assert!(
10186            rendered.contains(":licenca"),
10187            "diagnostic must name the offending slot: {rendered}",
10188        );
10189    }
10190
10191    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
10192
10193    #[test]
10194    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
10195        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
10196        // pin: [`Caixa::licenca`] must return the `:licenca` typed
10197        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10198        // raw `self.licenca.as_deref()` access across every
10199        // representative value in the accept-set — `None` (the "omit
10200        // the slot to defer to the caixa-helm renderer's `MIT`
10201        // fallback" arm every existing fixture without a `:licenca`
10202        // line carries), `Some("")` (a past-the-guard sentinel that
10203        // pins the accessor doesn't perform a silent
10204        // `Some("") → None` collapse on the empty arm — validate
10205        // rejects `Some("")` through `LicencaEmpty` but the accessor
10206        // must ship the raw slot verbatim so a validate-time gate
10207        // regression surfaces at the caixa-helm emit boundary rather
10208        // than being silently absorbed into the fallback), `Some("MIT")`
10209        // (the canonical single-license shape every `feira init`
10210        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
10211        // canonical `OR`-compound shape the peer
10212        // `validate_licenca_accepts_canonical_expressions` positive
10213        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
10214        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
10215        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
10216        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
10217        // guard sentinels — validate rejects each through
10218        // `LicencaInvalid` but the accessor must ship the raw slot
10219        // verbatim).
10220        //
10221        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
10222        // accessor pin on the substrate primitive — opens the "outer
10223        // [`Caixa`] `Option<&str>` scalar" projection pattern the
10224        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
10225        // future lifts fold on. Sibling in shape to the peer per-`:placement`
10226        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10227        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10228        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10229        // axes, extended onto the outer top-level [`Caixa`] universal-
10230        // axis surface. Pins against a future silent detour that
10231        // returned an owned `Option<String>` (which would type-check
10232        // but silently allocate on every accessor call, breaking the
10233        // zero-cost projection every peer sibling accessor carries), a
10234        // `Some("") → None` collapse (which would silently absorb the
10235        // `LicencaEmpty` refusal case at the accessor boundary and the
10236        // caixa-helm emit path would silently fall back to `"MIT"` on
10237        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
10238        // `None → Some("MIT")` collapse (which would silently reify
10239        // the caixa-helm renderer's `"MIT"` fallback at the accessor
10240        // boundary and every downstream consumer keying off the
10241        // `Option::is_none()` discriminator would lose the "author
10242        // omitted the slot" signal).
10243        for licenca in [
10244            None,
10245            Some(""),
10246            Some("MIT"),
10247            Some("Apache-2.0 OR MIT"),
10248            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
10249            Some("MIT "),
10250            Some(" MIT"),
10251            Some("MIT\n"),
10252            Some("Apache_2.0"),
10253            Some("MIT,Apache-2.0"),
10254        ] {
10255            let c = caixa_with_licenca(licenca);
10256            assert_eq!(
10257                c.licenca(),
10258                licenca,
10259                "Caixa::licenca must return :licenca verbatim (got {:?}, \
10260                 expected {licenca:?})",
10261                c.licenca(),
10262            );
10263            assert_eq!(
10264                c.licenca(),
10265                c.licenca.as_deref(),
10266                "Caixa::licenca must byte-equal the raw \
10267                 `self.licenca.as_deref()` field access across every \
10268                 value in the Option<&str> accept-set",
10269            );
10270        }
10271    }
10272
10273    #[test]
10274    fn validate_licenca_empty_arm_routes_through_accessor() {
10275        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
10276        // must key off [`Caixa::licenca`], not the raw
10277        // `self.licenca.as_deref()` field access. Structurally: a
10278        // `Caixa { licenca: Some(""), .. }` must surface the
10279        // `LicencaEmpty` refusal exactly, and a
10280        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
10281        // single-license form) must pass validate. The pair jointly
10282        // pins the accessor + validate-gate composition: any future
10283        // silent detour that had the accessor return `None` on the
10284        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10285        // silently absorb the `LicencaEmpty` refusal at the accessor
10286        // boundary and the validate gate would accept a struct-literal
10287        // `Caixa { licenca: Some(""), .. }` — the composition pin
10288        // catches that at caixa-core build time.
10289        //
10290        // Peer of the per-`:politicas :circuit-breaker`
10291        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
10292        // accessor-composition pin
10293        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
10294        // on the sibling per-M3-mesh-slot required-`u32` axis — same
10295        // "the validate / shape-gate predicate must route through the
10296        // substrate-primitive typed dispatch" discipline extended onto
10297        // the outer top-level [`Caixa`] universal-axis
10298        // `Option<&str>`-composition surface.
10299        let c = caixa_with_licenca(Some(""));
10300        assert!(
10301            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
10302            "validate_licenca must reject licenca == Some(\"\") with \
10303             LicencaEmpty — the accessor and the validate gate must \
10304             route through the same substrate-primitive typed dispatch \
10305             on the :licenca empty arm",
10306        );
10307        let c = caixa_with_licenca(Some("MIT"));
10308        assert!(
10309            c.validate_licenca().is_ok(),
10310            "validate_licenca must accept licenca == Some(\"MIT\") \
10311             (the canonical single-license SPDX shape)",
10312        );
10313    }
10314
10315    #[test]
10316    fn licenca_projects_option_str_by_borrow() {
10317        // The by-borrow pin: [`Caixa::licenca`] returns
10318        // `Option<&str>` by borrow — the `&str` borrows the underlying
10319        // `String` storage of the `Option<String>` slot and the
10320        // accessor must not allocate a fresh `String` on every call.
10321        // Peer of the per-`:placement`
10322        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10323        // borrow pin on the peer per-M3-mesh-slot
10324        // `Option<&str>`-return axis, extended onto the outer top-
10325        // level [`Caixa`] universal-axis `Option<&str>` shape — the
10326        // accessor's returned `&str` must borrow from `&self` (the
10327        // returned reference's lifetime is tied to `&self`), and
10328        // calling the accessor twice on the same [`Caixa`] must yield
10329        // the same `Option<&str>` verbatim (idempotent, no side
10330        // effects on `&self`).
10331        //
10332        // Pins against a future silent detour that returned an owned
10333        // `Option<String>` (which would type-check but silently
10334        // allocate on every call, breaking the zero-cost projection
10335        // every peer sibling accessor carries), or a one-arm-only
10336        // accessor that returned a saturating value on some sentinel
10337        // input (breaking the pass-through invariant the sibling
10338        // required-scalar accessors carry).
10339        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
10340            let c = caixa_with_licenca(licenca);
10341            let first = c.licenca();
10342            let second = c.licenca();
10343            assert_eq!(
10344                first, second,
10345                "Caixa::licenca must be idempotent — two successive \
10346                 calls on the same &self must return the same \
10347                 Option<&str>",
10348            );
10349            assert_eq!(
10350                first, licenca,
10351                "Caixa::licenca must return :licenca verbatim by \
10352                 borrow — got {first:?}, expected {licenca:?}",
10353            );
10354        }
10355    }
10356
10357    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
10358
10359    #[test]
10360    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
10361        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
10362        // pin: [`Caixa::repositorio`] must return the `:repositorio`
10363        // typed byte-string verbatim as an `Option<&str>`, byte-equal
10364        // to the raw `self.repositorio.as_deref()` access across every
10365        // representative value in the accept-set — `None` (the "omit
10366        // the slot to defer to the per-renderer placeholder" arm every
10367        // existing fixture without a `:repositorio` line carries),
10368        // `Some("")` (a past-the-guard sentinel that pins the accessor
10369        // doesn't perform a silent `Some("") → None` collapse on the
10370        // empty arm — validate rejects `Some("")` through
10371        // `RepositorioEmpty` but the accessor must ship the raw slot
10372        // verbatim so a validate-time gate regression surfaces at the
10373        // caixa-helm / caixa-flux emit boundary rather than being
10374        // silently absorbed into the per-renderer fallback),
10375        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
10376        // shorthand every existing manifest fixture across
10377        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
10378        // `Some("https://github.com/pleme-io/checkout")` (the canonical
10379        // `https://` URL the README quickstart uses),
10380        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
10381        // `Some("git://github.com/pleme-io/checkout.git")` /
10382        // `Some("git@github.com:pleme-io/checkout.git")` /
10383        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
10384        // github scheme the shared `is_git_repo_url` predicate
10385        // documents), and five past-the-guard sentinels for the
10386        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
10387        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
10388        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
10389        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
10390        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
10391        // sentinels pin the accessor doesn't silently absorb the
10392        // refusal cases into a fallback).
10393        //
10394        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
10395        // accessor pin on the substrate primitive — sibling of the peer
10396        // [`Caixa::licenca`] (6d5bc28) pin
10397        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
10398        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
10399        // projection pin pattern this pin folds on. Sibling in shape to
10400        // the peer per-`:placement`
10401        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10402        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10403        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10404        // axes, extended onto the outer top-level [`Caixa`] universal-
10405        // axis surface. Pins against a future silent detour that
10406        // returned an owned `Option<String>` (which would type-check
10407        // but silently allocate on every accessor call, breaking the
10408        // zero-cost projection every peer sibling accessor carries), a
10409        // `Some("") → None` collapse (which would silently absorb the
10410        // `RepositorioEmpty` refusal case at the accessor boundary and
10411        // the caixa-helm `Chart.yaml` `home:` fold would silently
10412        // render a `home: null` / omitted field on a struct-literal
10413        // `Caixa { repositorio: Some(""), .. }`), or a
10414        // `None → Some(<default>)` collapse (which would silently reify
10415        // the per-renderer fallback at the accessor boundary and every
10416        // downstream consumer keying off the `Option::is_none()`
10417        // discriminator would lose the "author omitted the slot"
10418        // signal).
10419        for repositorio in [
10420            None,
10421            Some(""),
10422            Some("github:pleme-io/hello-rio"),
10423            Some("https://github.com/pleme-io/checkout"),
10424            Some("ssh://git@github.com/pleme-io/checkout.git"),
10425            Some("git://github.com/pleme-io/checkout.git"),
10426            Some("git@github.com:pleme-io/checkout.git"),
10427            Some("file:///opt/mirrors/pleme-io/checkout"),
10428            Some("pleme-io/checkout"),
10429            Some("-upload-pack=evil"),
10430            Some("github:pleme-io/checkout?ref=main"),
10431            Some("github:pleme-io/checkout#main"),
10432            Some("github:pleme-io/{tpl}"),
10433        ] {
10434            let c = caixa_with_repositorio(repositorio);
10435            assert_eq!(
10436                c.repositorio(),
10437                repositorio,
10438                "Caixa::repositorio must return :repositorio verbatim \
10439                 (got {:?}, expected {repositorio:?})",
10440                c.repositorio(),
10441            );
10442            assert_eq!(
10443                c.repositorio(),
10444                c.repositorio.as_deref(),
10445                "Caixa::repositorio must byte-equal the raw \
10446                 `self.repositorio.as_deref()` field access across every \
10447                 value in the Option<&str> accept-set",
10448            );
10449        }
10450    }
10451
10452    #[test]
10453    fn validate_repositorio_empty_arm_routes_through_accessor() {
10454        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
10455        // gate must key off [`Caixa::repositorio`], not the raw
10456        // `self.repositorio.as_deref()` field access. Structurally: a
10457        // `Caixa { repositorio: Some(""), .. }` must surface the
10458        // `RepositorioEmpty` refusal exactly, and a
10459        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
10460        // (the canonical `github:` shorthand form) must pass validate.
10461        // The pair jointly pins the accessor + validate-gate
10462        // composition: any future silent detour that had the accessor
10463        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
10464        // collapse) would silently absorb the `RepositorioEmpty` refusal
10465        // at the accessor boundary and the validate gate would accept a
10466        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
10467        // composition pin catches that at caixa-core build time.
10468        //
10469        // Peer of the [`Caixa::licenca`] (6d5bc28)
10470        // `validate_licenca_empty_arm_routes_through_accessor`
10471        // composition pin on the sibling outer top-level [`Caixa`]
10472        // `Option<&str>` universal-axis surface — same "the validate /
10473        // shape-gate predicate must route through the substrate-
10474        // primitive typed dispatch" discipline extended onto the second
10475        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10476        // composition surface.
10477        let c = caixa_with_repositorio(Some(""));
10478        assert!(
10479            matches!(
10480                c.validate_repositorio(),
10481                Err(ManifestError::RepositorioEmpty),
10482            ),
10483            "validate_repositorio must reject repositorio == Some(\"\") \
10484             with RepositorioEmpty — the accessor and the validate gate \
10485             must route through the same substrate-primitive typed \
10486             dispatch on the :repositorio empty arm",
10487        );
10488        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
10489        assert!(
10490            c.validate_repositorio().is_ok(),
10491            "validate_repositorio must accept repositorio == \
10492             Some(\"github:pleme-io/hello-rio\") (the canonical \
10493             `github:` shorthand git-repo-URL shape)",
10494        );
10495    }
10496
10497    #[test]
10498    fn repositorio_projects_option_str_by_borrow() {
10499        // The by-borrow pin: [`Caixa::repositorio`] returns
10500        // `Option<&str>` by borrow — the `&str` borrows the underlying
10501        // `String` storage of the `Option<String>` slot and the
10502        // accessor must not allocate a fresh `String` on every call.
10503        // Peer of the per-`:placement`
10504        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
10505        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
10506        // `Option<&str>`-return axes, extended onto the second outer
10507        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
10508        // the accessor's returned `&str` must borrow from `&self` (the
10509        // returned reference's lifetime is tied to `&self`), and
10510        // calling the accessor twice on the same [`Caixa`] must yield
10511        // the same `Option<&str>` verbatim (idempotent, no side effects
10512        // on `&self`).
10513        //
10514        // Pins against a future silent detour that returned an owned
10515        // `Option<String>` (which would type-check but silently
10516        // allocate on every call, breaking the zero-cost projection
10517        // every peer sibling accessor carries), or a one-arm-only
10518        // accessor that returned a saturating value on some sentinel
10519        // input (breaking the pass-through invariant the sibling
10520        // required-scalar accessors carry).
10521        for repositorio in [
10522            None,
10523            Some(""),
10524            Some("github:pleme-io/hello-rio"),
10525            Some("https://github.com/pleme-io/checkout"),
10526        ] {
10527            let c = caixa_with_repositorio(repositorio);
10528            let first = c.repositorio();
10529            let second = c.repositorio();
10530            assert_eq!(
10531                first, second,
10532                "Caixa::repositorio must be idempotent — two successive \
10533                 calls on the same &self must return the same \
10534                 Option<&str>",
10535            );
10536            assert_eq!(
10537                first, repositorio,
10538                "Caixa::repositorio must return :repositorio verbatim by \
10539                 borrow — got {first:?}, expected {repositorio:?}",
10540            );
10541        }
10542    }
10543
10544    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
10545
10546    #[test]
10547    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
10548        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
10549        // return the author-declared `:repositorio` byte-string verbatim
10550        // on the `Some` arm — no scheme rewrite, no trailing-slash
10551        // canonicalization, no `github:` → `https://github.com/`
10552        // desugaring. The resolved-URL composer is the projection of
10553        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
10554        // the `String`-return arity every substrate-side field-fill
10555        // consumer keys off; on the `Some` arm the projection is
10556        // `str::to_owned` verbatim, so every accept-set value the
10557        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
10558        // across_permutations` pin covers (`https://…`, `github:…`,
10559        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
10560        // guard sentinel `pleme-io/…`) must survive the accessor
10561        // byte-equal. Pins against a future silent detour that rewrote
10562        // the `github:` shorthand to the `https://github.com/` full URL
10563        // at the accessor boundary (which would silently split the
10564        // resolved-URL surface from the raw [`Caixa::repositorio`]
10565        // accessor's documented pass-through invariant), or a trailing-
10566        // slash normalization (which would silently break the
10567        // FluxCD `GitRepository` `spec.url` byte-exact match every
10568        // downstream consumer keys the source-controller reconcile off).
10569        for repositorio in [
10570            "github:pleme-io/hello-rio",
10571            "https://github.com/pleme-io/checkout",
10572            "ssh://git@github.com/pleme-io/checkout.git",
10573            "git://github.com/pleme-io/checkout.git",
10574            "git@github.com:pleme-io/checkout.git",
10575            "file:///opt/mirrors/pleme-io/checkout",
10576        ] {
10577            let c = caixa_with_repositorio(Some(repositorio));
10578            assert_eq!(
10579                c.canonical_git_url(),
10580                repositorio,
10581                "Caixa::canonical_git_url on the Some arm must return \
10582                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
10583                c.canonical_git_url(),
10584            );
10585        }
10586    }
10587
10588    #[test]
10589    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
10590        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
10591        // `None` arm must emit the substrate's canonical pleme-org github
10592        // URL derived from `caixa.nome()` — `https://github.com/<org>/
10593        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
10594        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
10595        // is the exact byte-image of the prior inline
10596        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
10597        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
10598        // re-derived open-coded. Pins against a future silent detour
10599        // that migrated the `<org>` segment to a different constant (a
10600        // fork rebranding that split off a new
10601        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
10602        // to migrate onto), a scheme change (`https://` → `git://` or
10603        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
10604        // override (which would break the substrate-wide single-source-
10605        // of-truth guarantee this method encodes).
10606        let c = caixa_with_repositorio(None);
10607        let expected = format!(
10608            "https://github.com/{org}/{nome}",
10609            org = crate::DEFAULT_PLEME_GIT_ORG,
10610            nome = c.nome(),
10611        );
10612        assert_eq!(
10613            c.canonical_git_url(),
10614            expected,
10615            "Caixa::canonical_git_url on the None arm must fold through \
10616             the substrate's canonical pleme-org github URL fallback \
10617             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
10618             {:?}, expected {expected:?}",
10619            c.canonical_git_url(),
10620        );
10621    }
10622
10623    #[test]
10624    fn canonical_git_url_byte_matches_manual_composition() {
10625        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
10626        // byte-identically to the manual open-coded
10627        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
10628        //  format!("https://github.com/{org}/{nome}", ...))` composition
10629        // every prior substrate-side caller re-derived. Guards the
10630        // paired-site convergence just applied at caixa-flux's
10631        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
10632        // now routes through this accessor): a future implementation of
10633        // this method that reordered the format arguments, swapped the
10634        // `<org>` constant for a different one, or interposed a
10635        // canonicalization pass on the `Some` arm surfaces here as a
10636        // caixa-core build-time test failure rather than as a downstream
10637        // FluxCD `GitRepository` reconcile mismatch far from this
10638        // method's source.
10639        for repositorio in [
10640            None,
10641            Some("github:pleme-io/hello-rio"),
10642            Some("https://github.com/pleme-io/checkout"),
10643            Some("ssh://git@github.com/pleme-io/checkout.git"),
10644        ] {
10645            let c = caixa_with_repositorio(repositorio);
10646            let manual = c.repositorio().map_or_else(
10647                || {
10648                    format!(
10649                        "https://github.com/{org}/{nome}",
10650                        org = crate::DEFAULT_PLEME_GIT_ORG,
10651                        nome = c.nome(),
10652                    )
10653                },
10654                str::to_owned,
10655            );
10656            assert_eq!(
10657                c.canonical_git_url(),
10658                manual,
10659                "Caixa::canonical_git_url must byte-equal the manual \
10660                 open-coded `repositorio().map(str::to_owned)\
10661                 .unwrap_or_else(|| format!(...))` composition across \
10662                 every representative :repositorio input — got {:?}, \
10663                 expected {manual:?}",
10664                c.canonical_git_url(),
10665            );
10666        }
10667    }
10668
10669    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
10670
10671    #[test]
10672    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
10673        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
10674        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
10675        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
10676        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
10677        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
10678        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
10679        // the `0.0.0` boundary case. Every accept-set value the peer
10680        // validate gate lets through must survive the resolved-tag
10681        // projection byte-equal.
10682        for versao in [
10683            "0.1.0",
10684            "0.0.0",
10685            "1.0.0",
10686            "1.2.3-rc.1",
10687            "1.2.3+build.42",
10688            "1.2.3-rc.1+build.42",
10689        ] {
10690            let c = caixa_with_versao(versao);
10691            let expected = format!(
10692                "{prefix}{versao}",
10693                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
10694            );
10695            assert_eq!(
10696                c.publish_tag(),
10697                expected,
10698                "Caixa::publish_tag must compose \
10699                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
10700                 :versao ({versao:?}) verbatim — got {got:?}, \
10701                 expected {expected:?}",
10702                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
10703                got = c.publish_tag(),
10704            );
10705        }
10706    }
10707
10708    #[test]
10709    fn publish_tag_starts_with_default_publish_tag_prefix() {
10710        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
10711        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
10712        // byte-string on every input, guarding a hypothetical future
10713        // implementation that migrated the prefix segment to an inline
10714        // literal (`"v"`) that would silently drift from any rebrand of
10715        // the lifted constant. Peer to the sibling caixa-flux
10716        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
10717        // test which pins the same prefix invariant at the reader-side
10718        // `GitRefSpec::Tag` emit site.
10719        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
10720            let c = caixa_with_versao(versao);
10721            let tag = c.publish_tag();
10722            assert!(
10723                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
10724                "Caixa::publish_tag emission {tag:?} must start with \
10725                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
10726                 ({prefix:?})",
10727                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
10728            );
10729        }
10730    }
10731
10732    #[test]
10733    fn publish_tag_byte_matches_manual_composition() {
10734        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
10735        // identically to the manual open-coded
10736        // `format!("{prefix}{versao}", prefix =
10737        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
10738        //  caixa.versao())` composition every prior substrate-side
10739        // caller re-derived. Guards the paired-site convergence just
10740        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
10741        // `git_ref` composer (which now routes through this accessor):
10742        // a future implementation of this method that reordered the
10743        // format arguments, swapped the `<prefix>` constant for a
10744        // different one, or interposed a canonicalization pass on the
10745        // `:versao` axis surfaces here as a caixa-core build-time test
10746        // failure rather than as a downstream FluxCD `GitRepository`
10747        // reconcile mismatch far from this method's source.
10748        for versao in [
10749            "0.1.0",
10750            "0.0.0",
10751            "1.2.3-rc.1",
10752            "1.2.3+build.42",
10753            "1.2.3-rc.1+build.42",
10754        ] {
10755            let c = caixa_with_versao(versao);
10756            let manual = format!(
10757                "{prefix}{versao}",
10758                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
10759                versao = c.versao(),
10760            );
10761            assert_eq!(
10762                c.publish_tag(),
10763                manual,
10764                "Caixa::publish_tag must byte-equal the manual \
10765                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
10766                 composition across every representative :versao input \
10767                 — got {got:?}, expected {manual:?}",
10768                got = c.publish_tag(),
10769            );
10770        }
10771    }
10772
10773    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
10774
10775    #[test]
10776    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
10777        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
10778        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
10779        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
10780        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
10781        // set sweep documents — single-word, hyphen-joined, version-
10782        // suffixed, single-char, two-char, digit-start, retry-suffixed.
10783        // Every accept-set value the peer validate gate lets through must
10784        // survive the resolved-chart-name projection byte-equal.
10785        for nome in [
10786            "checkout",
10787            "cart-v2",
10788            "a",
10789            "db",
10790            "3rd-party-shim",
10791            "payment-retry",
10792            "0",
10793        ] {
10794            let c = caixa_with_nome(nome);
10795            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
10796            assert_eq!(
10797                c.lareira_chart_name(),
10798                expected,
10799                "Caixa::lareira_chart_name must compose \
10800                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
10801                 :nome ({nome:?}) verbatim — got {got:?}, \
10802                 expected {expected:?}",
10803                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
10804                got = c.lareira_chart_name(),
10805            );
10806        }
10807    }
10808
10809    #[test]
10810    fn lareira_chart_name_starts_with_lifted_prefix() {
10811        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
10812        // must begin with the canonical
10813        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
10814        // input, guarding a hypothetical future implementation that
10815        // migrated the prefix segment to an inline literal (`"lareira-"`)
10816        // that would silently drift from any rebrand of the lifted
10817        // constant. Peer to the sibling
10818        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
10819        // the co-resident resolved-publish-tag composer's prefix axis.
10820        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
10821            let c = caixa_with_nome(nome);
10822            let chart = c.lareira_chart_name();
10823            assert!(
10824                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
10825                "Caixa::lareira_chart_name emission {chart:?} must start \
10826                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
10827                 ({prefix:?})",
10828                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
10829            );
10830        }
10831    }
10832
10833    #[test]
10834    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
10835        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
10836        // byte-identically to the manual open-coded
10837        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
10838        // composition every prior substrate-side caller re-derived.
10839        // Guards the paired-site convergence just applied at caixa-helm's
10840        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
10841        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
10842        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
10843        // composer (all of which now route through this accessor): a
10844        // future implementation of this method that reordered the
10845        // composition arguments, swapped the `<prefix>` constant for a
10846        // different one, or interposed a canonicalization pass on the
10847        // `:nome` axis surfaces here as a caixa-core build-time test
10848        // failure rather than as a downstream Helm chart-render / FluxCD
10849        // reconcile / tatara Process-CR mismatch far from this method's
10850        // source.
10851        for nome in [
10852            "checkout",
10853            "cart-v2",
10854            "a",
10855            "db",
10856            "3rd-party-shim",
10857            "payment-retry",
10858        ] {
10859            let c = caixa_with_nome(nome);
10860            let manual = crate::lareira_chart_name(c.nome());
10861            assert_eq!(
10862                c.lareira_chart_name(),
10863                manual,
10864                "Caixa::lareira_chart_name must byte-equal the manual \
10865                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
10866                 composition across every representative :nome input — \
10867                 got {got:?}, expected {manual:?}",
10868                got = c.lareira_chart_name(),
10869            );
10870        }
10871    }
10872
10873    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
10874
10875    #[test]
10876    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
10877        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
10878        // pin: [`Caixa::descricao`] must return the `:descricao` typed
10879        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10880        // raw `self.descricao.as_deref()` access across every
10881        // representative value in the accept-set — `None` (the "omit
10882        // the slot to defer to the per-renderer `caixa.nome`-derived
10883        // fallback" arm every existing fixture without a `:descricao`
10884        // line carries), `Some("")` (a past-the-guard sentinel that
10885        // pins the accessor doesn't perform a silent `Some("") → None`
10886        // collapse on the empty arm — validate rejects `Some("")`
10887        // through `DescricaoEmpty` but the accessor must ship the raw
10888        // slot verbatim so a validate-time gate regression surfaces at
10889        // the caixa-helm / caixa-feira emit boundary rather than being
10890        // silently absorbed into the per-renderer `caixa.nome`-derived
10891        // fallback), `Some("Checkout flow.")` (the canonical one-line
10892        // prose descriptor the peer
10893        // `validate_descricao_accepts_canonical_value` positive sweep
10894        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
10895        // Servico.")` (the multi-byte Unicode continuation-byte shape
10896        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
10897        // multi-glyph Unicode shape the peer
10898        // `is_chart_description_shape` predicate accepts), and five
10899        // past-the-guard sentinels for the `DescricaoInvalid` refusal
10900        // cases (`Some(" Checkout flow.")` leading-whitespace,
10901        // `Some("Checkout flow. ")` trailing-whitespace,
10902        // `Some("Checkout\nflow.")` embedded-LF,
10903        // `Some("Checkout\tflow.")` embedded-TAB, and
10904        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
10905        // the accessor doesn't silently absorb the refusal cases into
10906        // a fallback).
10907        //
10908        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
10909        // accessor pin on the substrate primitive — sibling of the peer
10910        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
10911        // (cc7332d) pins that opened the "outer [`Caixa`]
10912        // `Option<&str>` scalar" projection pin pattern this pin folds
10913        // on. Sibling in shape to the peer per-`:placement`
10914        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10915        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10916        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10917        // axes, extended onto the outer top-level [`Caixa`] universal-
10918        // axis surface. Pins against a future silent detour that
10919        // returned an owned `Option<String>` (which would type-check
10920        // but silently allocate on every accessor call, breaking the
10921        // zero-cost projection every peer sibling accessor carries), a
10922        // `Some("") → None` collapse (which would silently absorb the
10923        // `DescricaoEmpty` refusal case at the accessor boundary and
10924        // the caixa-helm `Chart.yaml` `description:` fold would
10925        // silently render a `caixa.nome`-derived fallback on a
10926        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
10927        // `None → Some(<default>)` collapse (which would silently
10928        // reify the per-renderer `caixa.nome`-derived fallback at the
10929        // accessor boundary and every downstream consumer keying off
10930        // the `Option::is_none()` discriminator would lose the "author
10931        // omitted the slot" signal).
10932        for descricao in [
10933            None,
10934            Some(""),
10935            Some("Checkout flow."),
10936            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
10937            Some("→ — · ✓"),
10938            Some(" Checkout flow."),
10939            Some("Checkout flow. "),
10940            Some("Checkout\nflow."),
10941            Some("Checkout\tflow."),
10942            Some("Checkout\x00flow."),
10943        ] {
10944            let c = caixa_with_descricao(descricao);
10945            assert_eq!(
10946                c.descricao(),
10947                descricao,
10948                "Caixa::descricao must return :descricao verbatim (got \
10949                 {:?}, expected {descricao:?})",
10950                c.descricao(),
10951            );
10952            assert_eq!(
10953                c.descricao(),
10954                c.descricao.as_deref(),
10955                "Caixa::descricao must byte-equal the raw \
10956                 `self.descricao.as_deref()` field access across every \
10957                 value in the Option<&str> accept-set",
10958            );
10959        }
10960    }
10961
10962    #[test]
10963    fn validate_descricao_empty_arm_routes_through_accessor() {
10964        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
10965        // gate must key off [`Caixa::descricao`], not the raw
10966        // `self.descricao.as_deref()` field access. Structurally: a
10967        // `Caixa { descricao: Some(""), .. }` must surface the
10968        // `DescricaoEmpty` refusal exactly, and a
10969        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
10970        // canonical one-line-prose form) must pass validate. The pair
10971        // jointly pins the accessor + validate-gate composition: any
10972        // future silent detour that had the accessor return `None` on
10973        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10974        // silently absorb the `DescricaoEmpty` refusal at the accessor
10975        // boundary and the validate gate would accept a struct-literal
10976        // `Caixa { descricao: Some(""), .. }` — the composition pin
10977        // catches that at caixa-core build time.
10978        //
10979        // Peer of the [`Caixa::licenca`] (6d5bc28)
10980        // `validate_licenca_empty_arm_routes_through_accessor` and
10981        // [`Caixa::repositorio`] (cc7332d)
10982        // `validate_repositorio_empty_arm_routes_through_accessor`
10983        // composition pins on the sibling outer top-level [`Caixa`]
10984        // `Option<&str>` universal-axis surface — same "the validate /
10985        // shape-gate predicate must route through the substrate-
10986        // primitive typed dispatch" discipline extended onto the third
10987        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10988        // composition surface.
10989        let c = caixa_with_descricao(Some(""));
10990        assert!(
10991            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
10992            "validate_descricao must reject descricao == Some(\"\") \
10993             with DescricaoEmpty — the accessor and the validate gate \
10994             must route through the same substrate-primitive typed \
10995             dispatch on the :descricao empty arm",
10996        );
10997        let c = caixa_with_descricao(Some("Checkout flow."));
10998        assert!(
10999            c.validate_descricao().is_ok(),
11000            "validate_descricao must accept descricao == \
11001             Some(\"Checkout flow.\") (the canonical one-line-prose \
11002             chart-description shape)",
11003        );
11004    }
11005
11006    #[test]
11007    fn descricao_projects_option_str_by_borrow() {
11008        // The by-borrow pin: [`Caixa::descricao`] returns
11009        // `Option<&str>` by borrow — the `&str` borrows the underlying
11010        // `String` storage of the `Option<String>` slot and the
11011        // accessor must not allocate a fresh `String` on every call.
11012        // Peer of the [`Caixa::licenca`] (6d5bc28) and
11013        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
11014        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
11015        // the per-`:placement`
11016        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11017        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
11018        // return axis, extended onto the third outer top-level
11019        // [`Caixa`] universal-axis `Option<&str>` shape — the
11020        // accessor's returned `&str` must borrow from `&self` (the
11021        // returned reference's lifetime is tied to `&self`), and
11022        // calling the accessor twice on the same [`Caixa`] must yield
11023        // the same `Option<&str>` verbatim (idempotent, no side
11024        // effects on `&self`).
11025        //
11026        // Pins against a future silent detour that returned an owned
11027        // `Option<String>` (which would type-check but silently
11028        // allocate on every call, breaking the zero-cost projection
11029        // every peer sibling accessor carries), or a one-arm-only
11030        // accessor that returned a saturating value on some sentinel
11031        // input (breaking the pass-through invariant the sibling
11032        // required-scalar accessors carry).
11033        for descricao in [
11034            None,
11035            Some(""),
11036            Some("Checkout flow."),
11037            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
11038        ] {
11039            let c = caixa_with_descricao(descricao);
11040            let first = c.descricao();
11041            let second = c.descricao();
11042            assert_eq!(
11043                first, second,
11044                "Caixa::descricao must be idempotent — two successive \
11045                 calls on the same &self must return the same \
11046                 Option<&str>",
11047            );
11048            assert_eq!(
11049                first, descricao,
11050                "Caixa::descricao must return :descricao verbatim by \
11051                 borrow — got {first:?}, expected {descricao:?}",
11052            );
11053        }
11054    }
11055
11056    // ── validate_edicao — universal-axis language-edition shape ──
11057
11058    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
11059        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11060        c.edicao = edicao.map(String::from);
11061        c
11062    }
11063
11064    #[test]
11065    fn validate_edicao_accepts_none() {
11066        // The omit-the-slot identity: `:edicao` is optional. The
11067        // gate is a no-op when the author didn't declare a value —
11068        // every caixa without an `:edicao` line trivially passes,
11069        // and the substrate-side build pipeline falls back to the
11070        // documented default edition. Mirrors the peer
11071        // `validate_licenca_accepts_none` posture on the sibling
11072        // `Option<String>` Caixa slot.
11073        let c = caixa_with_edicao(None);
11074        c.validate_edicao().unwrap();
11075    }
11076
11077    #[test]
11078    fn validate_edicao_accepts_canonical_value() {
11079        // Positive control: the canonical `"2026"` edition every
11080        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
11081        // `caixa-mesh`) carries by construction passes the gate.
11082        // Future-introduced sibling editions (`"2027"`, `"2030"`,
11083        // `"2049"`) that match the same 4-digit ASCII decimal year
11084        // shape must also trivially pass — the structural shape
11085        // predicate accepts every well-formed year regardless of
11086        // whether the substrate yet understands the specific value
11087        // (a future known-edition allowlist tightens that).
11088        for ed in ["2026", "2027", "2030", "2049"] {
11089            let c = caixa_with_edicao(Some(ed));
11090            c.validate_edicao()
11091                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
11092        }
11093    }
11094
11095    #[test]
11096    fn validate_edicao_rejects_empty_some() {
11097        // Canonical paste-from-blank-doc footgun. Without this gate
11098        // the empty `Some("")` silently lands as `(:edicao "")` in
11099        // the rendered caixa.lisp and a future renderer-side
11100        // consumer's `Option::unwrap_or_else` (which only fires on
11101        // `None`) skips its fallback. Mirrors the peer
11102        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
11103        // `Option<String>` Caixa slot.
11104        let c = caixa_with_edicao(Some(""));
11105        let err = c.validate_edicao().unwrap_err();
11106        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
11107    }
11108
11109    #[test]
11110    fn validate_edicao_rejects_free_form_non_year() {
11111        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
11112        // `"nightly"` shapes carry no operational meaning on the
11113        // substrate's build-time edition selector. Until this gate
11114        // landed the bare empty-arm check let every such value
11115        // through and broke far from the source caixa.lisp. Peer
11116        // with the shape-predicate cascade
11117        // `validate_repositorio_rejects_missing_colon_separator`
11118        // establishes past its own empty arm.
11119        for ed in ["x", "latest", "nightly", "stable"] {
11120            let c = caixa_with_edicao(Some(ed));
11121            let err = c.validate_edicao().unwrap_err();
11122            assert!(
11123                matches!(err, ManifestError::EdicaoInvalid { .. }),
11124                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11125            );
11126        }
11127    }
11128
11129    #[test]
11130    fn validate_edicao_rejects_trailing_whitespace() {
11131        // Paste-from-doc whitespace footgun. A trailing space in
11132        // the `:edicao` value would silently break the substrate's
11133        // build-time edition match-table lookup at the rendered
11134        // artifact's edition-selector consumer. The shape predicate
11135        // refuses every whitespace byte by construction (any byte
11136        // outside `0-9` fails `is_ascii_digit`). Peer with
11137        // `validate_repositorio_rejects_whitespace`.
11138        let c = caixa_with_edicao(Some("2026 "));
11139        let err = c.validate_edicao().unwrap_err();
11140        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
11141            panic!("expected EdicaoInvalid, got {err:?}");
11142        };
11143        assert_eq!(edicao, "2026 ");
11144    }
11145
11146    #[test]
11147    fn validate_edicao_rejects_leading_whitespace() {
11148        // Symmetric paste-from-doc whitespace footgun on the leading
11149        // boundary — the gate refuses every shape with a non-digit
11150        // byte by construction.
11151        let c = caixa_with_edicao(Some(" 2026"));
11152        let err = c.validate_edicao().unwrap_err();
11153        assert!(
11154            matches!(err, ManifestError::EdicaoInvalid { .. }),
11155            "got {err:?}",
11156        );
11157    }
11158
11159    #[test]
11160    fn validate_edicao_rejects_control_char() {
11161        // Paste-from-multiline-doc CRLF footgun — control characters
11162        // at the value boundary break the substrate's build-time
11163        // edition-selector parser. Peer with
11164        // `validate_repositorio_rejects_control_char`.
11165        let c = caixa_with_edicao(Some("2026\n"));
11166        let err = c.validate_edicao().unwrap_err();
11167        assert!(
11168            matches!(err, ManifestError::EdicaoInvalid { .. }),
11169            "got {err:?}",
11170        );
11171    }
11172
11173    #[test]
11174    fn validate_edicao_rejects_non_ascii_lookalike() {
11175        // Fullwidth-keyboard look-alike footgun — `"2026"` is
11176        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
11177        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
11178        // edition selector wants an ASCII year, and the gate
11179        // refuses every non-ASCII shape by construction (length in
11180        // bytes is 12 ≠ 4, *and* every byte falls outside
11181        // `is_ascii_digit`'s `0-9` range).
11182        let c = caixa_with_edicao(Some("2026"));
11183        let err = c.validate_edicao().unwrap_err();
11184        assert!(
11185            matches!(err, ManifestError::EdicaoInvalid { .. }),
11186            "got {err:?}",
11187        );
11188    }
11189
11190    #[test]
11191    fn validate_edicao_rejects_version_tag_prefix() {
11192        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
11193        // / `"r2026"` are familiar shapes from git-tag / Rust
11194        // edition / release-tag conventions that don't apply to
11195        // the year-shaped edition axis. The shape predicate refuses
11196        // every leading non-digit prefix.
11197        for ed in ["v2026", "e2026", "r2026"] {
11198            let c = caixa_with_edicao(Some(ed));
11199            let err = c.validate_edicao().unwrap_err();
11200            assert!(
11201                matches!(err, ManifestError::EdicaoInvalid { .. }),
11202                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11203            );
11204        }
11205    }
11206
11207    #[test]
11208    fn validate_edicao_rejects_decimal_shape() {
11209        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
11210        // `"2026.0"` are familiar shapes from semver / float
11211        // conventions that don't apply to the year-shaped edition
11212        // axis. The shape predicate refuses every non-digit byte
11213        // (`.` falls outside `is_ascii_digit`).
11214        for ed in ["2026.1", "2026.0", "2026.0.1"] {
11215            let c = caixa_with_edicao(Some(ed));
11216            let err = c.validate_edicao().unwrap_err();
11217            assert!(
11218                matches!(err, ManifestError::EdicaoInvalid { .. }),
11219                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11220            );
11221        }
11222    }
11223
11224    #[test]
11225    fn validate_edicao_rejects_wrong_length_numeric() {
11226        // Wrong-length numeric footgun — `"26"` (truncated) /
11227        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
11228        // (zero-padded too wide) all parse as integers but don't
11229        // name a 4-digit year. The shape predicate refuses every
11230        // value whose length isn't exactly 4 bytes.
11231        for ed in ["26", "202", "20260", "00026", "9"] {
11232            let c = caixa_with_edicao(Some(ed));
11233            let err = c.validate_edicao().unwrap_err();
11234            assert!(
11235                matches!(err, ManifestError::EdicaoInvalid { .. }),
11236                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11237            );
11238        }
11239    }
11240
11241    #[test]
11242    fn validate_edicao_empty_takes_precedence_over_shape() {
11243        // Empty-first cascade pin: the empty `Some("")` surfaces
11244        // the narrower `EdicaoEmpty` not the shape-predicate-
11245        // wrapped `EdicaoInvalid`, mirroring the peer
11246        // `validate_repositorio_empty_takes_precedence_over_shape`
11247        // (`RepositorioEmpty` → `RepositorioInvalid`),
11248        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
11249        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
11250        // cascades. The shape predicate also refuses the empty
11251        // input (defensively — `s.len() != 4`), but the
11252        // manifest-layer empty arm runs first to surface the
11253        // narrower diagnostic verbatim.
11254        let c = caixa_with_edicao(Some(""));
11255        let err = c.validate_edicao().unwrap_err();
11256        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
11257    }
11258
11259    #[test]
11260    fn validate_edicao_template_passes() {
11261        // Round-trip pin: the bare `Caixa::template` shape (which
11262        // carries `:edicao "2026"` verbatim) passes the gate by
11263        // construction. A future template-shape change that
11264        // introduced `(:edicao "")` or a non-year value would
11265        // surface here as a regression. Mirrors the peer
11266        // `validate_licenca_template_passes` pin.
11267        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11268        c.validate_edicao().unwrap();
11269    }
11270
11271    #[test]
11272    fn validate_edicao_diagnostic_names_offending_slot() {
11273        // Diagnostic-shape pin (peer with
11274        // `validate_licenca_diagnostic_names_offending_slot`): the
11275        // error's Display surfaces the `:edicao` slot name verbatim,
11276        // so a `feira lint` run can render the diagnostic without
11277        // re-parsing and the author can grep their caixa.lisp for
11278        // the offending `:edicao` line.
11279        let c = caixa_with_edicao(Some(""));
11280        let rendered = c.validate_edicao().unwrap_err().to_string();
11281        assert!(
11282            rendered.contains(":edicao"),
11283            "diagnostic must name the offending slot: {rendered}",
11284        );
11285    }
11286
11287    #[test]
11288    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
11289        // Diagnostic-shape pin on the shape-predicate arm (peer
11290        // with `validate_repositorio_diagnostic_carries_offending_value`):
11291        // the error's Display surfaces the offending value + slot
11292        // name verbatim, so a `feira lint` run can render the
11293        // diagnostic without re-parsing and the author can grep
11294        // their caixa.lisp for the offending `:edicao` value.
11295        let c = caixa_with_edicao(Some("v2026"));
11296        let rendered = c.validate_edicao().unwrap_err().to_string();
11297        assert!(
11298            rendered.contains(":edicao"),
11299            "diagnostic must name the offending slot: {rendered}",
11300        );
11301        assert!(
11302            rendered.contains("v2026"),
11303            "diagnostic must quote the offending value: {rendered}",
11304        );
11305    }
11306
11307    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
11308
11309    #[test]
11310    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
11311        // The canonical per-`Caixa` `:edicao` language-edition scalar
11312        // pin: [`Caixa::edicao`] must return the `:edicao` typed
11313        // byte-string verbatim as an `Option<&str>`, byte-equal to the
11314        // raw `self.edicao.as_deref()` access across every representative
11315        // value in the accept-set — `None` (the "omit the slot to defer
11316        // to the substrate's default edition" arm every existing
11317        // [`caixa-resolver`] fixture without an `:edicao` line carries),
11318        // `Some("")` (a past-the-guard sentinel that pins the accessor
11319        // doesn't perform a silent `Some("") → None` collapse on the
11320        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
11321        // but the accessor must ship the raw slot verbatim so a
11322        // validate-time gate regression surfaces at any future edition-
11323        // aware consumer's boundary rather than being silently absorbed
11324        // into the substrate's default edition), `Some("2026")` (the
11325        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
11326        // template scaffolds via [`Caixa::template`] and every
11327        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
11328        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
11329        // carries by construction), `Some("2018")` / `Some("2021")` /
11330        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
11331        // peer with Cargo's `[package] edition` grammar every future-
11332        // introduced sibling to `"2026"` will follow), and eight
11333        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
11334        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
11335        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
11336        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
11337        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
11338        // length-numeric, `Some("latest")` free-form-non-year — the
11339        // sentinels pin the accessor doesn't silently absorb the
11340        // refusal cases into a substrate-default-edition fallback).
11341        //
11342        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
11343        // return scalar accessor pin on the substrate primitive —
11344        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
11345        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
11346        // (3f16e2f) pins that opened the "outer [`Caixa`]
11347        // `Option<&str>` scalar" projection pin pattern this pin folds
11348        // on. Sibling in shape to the peer per-`:placement`
11349        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11350        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11351        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11352        // axes, extended onto the outer top-level [`Caixa`] universal-
11353        // axis surface's last unlifted `Option<String>` slot. Pins
11354        // against a future silent detour that returned an owned
11355        // `Option<String>` (which would type-check but silently
11356        // allocate on every accessor call, breaking the zero-cost
11357        // projection every peer sibling accessor carries), a
11358        // `Some("") → None` collapse (which would silently absorb the
11359        // `EdicaoEmpty` refusal case at the accessor boundary and any
11360        // future edition-aware consumer would silently fall back to
11361        // the substrate's default edition on a struct-literal
11362        // `Caixa { edicao: Some(""), .. }`), or a
11363        // `None → Some("2026")` collapse (which would silently reify
11364        // the substrate's default edition at the accessor boundary
11365        // and every downstream consumer keying off the
11366        // `Option::is_none()` discriminator would lose the "author
11367        // omitted the slot" signal).
11368        for edicao in [
11369            None,
11370            Some(""),
11371            Some("2026"),
11372            Some("2018"),
11373            Some("2021"),
11374            Some("2024"),
11375            Some("2026 "),
11376            Some(" 2026"),
11377            Some("2026\n"),
11378            Some("2026"),
11379            Some("v2026"),
11380            Some("2026.1"),
11381            Some("26"),
11382            Some("latest"),
11383        ] {
11384            let c = caixa_with_edicao(edicao);
11385            assert_eq!(
11386                c.edicao(),
11387                edicao,
11388                "Caixa::edicao must return :edicao verbatim (got {:?}, \
11389                 expected {edicao:?})",
11390                c.edicao(),
11391            );
11392            assert_eq!(
11393                c.edicao(),
11394                c.edicao.as_deref(),
11395                "Caixa::edicao must byte-equal the raw \
11396                 `self.edicao.as_deref()` field access across every \
11397                 value in the Option<&str> accept-set",
11398            );
11399        }
11400    }
11401
11402    #[test]
11403    fn validate_edicao_empty_arm_routes_through_accessor() {
11404        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
11405        // must key off [`Caixa::edicao`], not the raw
11406        // `self.edicao.as_deref()` field access. Structurally: a
11407        // `Caixa { edicao: Some(""), .. }` must surface the
11408        // `EdicaoEmpty` refusal exactly, and a
11409        // `Caixa { edicao: Some("2026"), .. }` (the canonical
11410        // 4-digit-ASCII-decimal-year form) must pass validate. The
11411        // pair jointly pins the accessor + validate-gate composition:
11412        // any future silent detour that had the accessor return `None`
11413        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
11414        // would silently absorb the `EdicaoEmpty` refusal at the
11415        // accessor boundary and the validate gate would accept a
11416        // struct-literal `Caixa { edicao: Some(""), .. }` — the
11417        // composition pin catches that at caixa-core build time.
11418        //
11419        // Peer of the [`Caixa::licenca`] (6d5bc28)
11420        // `validate_licenca_empty_arm_routes_through_accessor`,
11421        // [`Caixa::repositorio`] (cc7332d)
11422        // `validate_repositorio_empty_arm_routes_through_accessor`,
11423        // and [`Caixa::descricao`] (3f16e2f)
11424        // `validate_descricao_empty_arm_routes_through_accessor`
11425        // composition pins on the sibling outer top-level [`Caixa`]
11426        // `Option<&str>` universal-axis surface — same "the validate /
11427        // shape-gate predicate must route through the substrate-
11428        // primitive typed dispatch" discipline extended onto the
11429        // fourth and final outer top-level [`Caixa`] universal-axis
11430        // `Option<&str>`-composition surface, closing the accessor-
11431        // composition family.
11432        let c = caixa_with_edicao(Some(""));
11433        assert!(
11434            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
11435            "validate_edicao must reject edicao == Some(\"\") with \
11436             EdicaoEmpty — the accessor and the validate gate must \
11437             route through the same substrate-primitive typed dispatch \
11438             on the :edicao empty arm",
11439        );
11440        let c = caixa_with_edicao(Some("2026"));
11441        assert!(
11442            c.validate_edicao().is_ok(),
11443            "validate_edicao must accept edicao == Some(\"2026\") \
11444             (the canonical 4-digit-ASCII-decimal-year shape)",
11445        );
11446    }
11447
11448    #[test]
11449    fn edicao_projects_option_str_by_borrow() {
11450        // The by-borrow pin: [`Caixa::edicao`] returns
11451        // `Option<&str>` by borrow — the `&str` borrows the underlying
11452        // `String` storage of the `Option<String>` slot and the
11453        // accessor must not allocate a fresh `String` on every call.
11454        // Peer of the [`Caixa::licenca`] (6d5bc28),
11455        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
11456        // (3f16e2f) by-borrow pins on the peer outer top-level
11457        // [`Caixa`] `Option<&str>`-return axes, and of the
11458        // per-`:placement`
11459        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11460        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
11461        // return axis, extended onto the fourth and final outer top-
11462        // level [`Caixa`] universal-axis `Option<&str>` shape — the
11463        // accessor's returned `&str` must borrow from `&self` (the
11464        // returned reference's lifetime is tied to `&self`), and
11465        // calling the accessor twice on the same [`Caixa`] must yield
11466        // the same `Option<&str>` verbatim (idempotent, no side
11467        // effects on `&self`).
11468        //
11469        // Pins against a future silent detour that returned an owned
11470        // `Option<String>` (which would type-check but silently
11471        // allocate on every call, breaking the zero-cost projection
11472        // every peer sibling accessor carries), or a one-arm-only
11473        // accessor that returned a saturating value on some sentinel
11474        // input (breaking the pass-through invariant the sibling
11475        // required-scalar accessors carry).
11476        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
11477            let c = caixa_with_edicao(edicao);
11478            let first = c.edicao();
11479            let second = c.edicao();
11480            assert_eq!(
11481                first, second,
11482                "Caixa::edicao must be idempotent — two successive \
11483                 calls on the same &self must return the same \
11484                 Option<&str>",
11485            );
11486            assert_eq!(
11487                first, edicao,
11488                "Caixa::edicao must return :edicao verbatim by \
11489                 borrow — got {first:?}, expected {edicao:?}",
11490            );
11491        }
11492    }
11493
11494    #[test]
11495    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
11496        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
11497        // label caixa-identity scalar pin: [`Caixa::nome`] must return
11498        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
11499        // the raw field access across every representative value in
11500        // the accept-set — the canonical `"demo"` template baseline
11501        // (the same `feira init`-scaffolded default the sibling
11502        // `validate_nome_accepts_canonical_template` positive-control
11503        // gate pins), plus every sibling per-typed-slot atom accessor's
11504        // canonical positive-arm byte-string (`"catalog"` per
11505        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
11506        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
11507        // `caixa-helm`/`caixa-flux` cross-crate integration-test
11508        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
11509        // canonical example), plus every past-the-guard sentinel for
11510        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
11511        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
11512        // the bare DNS-1123 63-byte cap but overflows the joint
11513        // `lareira-<nome>` chart-name budget the sibling
11514        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
11515        //
11516        // The past-the-guard sentinels pin the accessor doesn't
11517        // silently absorb the refusal cases into a template-derived
11518        // fallback (a future `.nome().is_empty().then(|| "demo")`
11519        // collapse would silently absorb the `NomeEmpty` refusal at
11520        // the accessor boundary and the validate gate would accept a
11521        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
11522        // catches that at caixa-core build time).
11523        //
11524        // First outer top-level [`Caixa`] `&str`-return required-
11525        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
11526        // required-scalar" projection pattern the sibling per-`Caixa`
11527        // `:versao` future lift folds on. Sibling in shape to the peer
11528        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
11529        // required-`String`-carry accessor pin on the sibling per-
11530        // sub-struct required-axis, extended onto the outer top-level
11531        // [`Caixa`] universal-axis required-`String`-carry axis.
11532        for nome in [
11533            "demo",
11534            "catalog",
11535            "cart",
11536            "hello-rio",
11537            "checkout",
11538            "",
11539            "Bad_Name",
11540            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
11541        ] {
11542            let c = caixa_with_nome(nome);
11543            assert_eq!(
11544                c.nome(),
11545                nome,
11546                "Caixa::nome must return :nome verbatim (got {}, \
11547                 expected {nome})",
11548                c.nome(),
11549            );
11550            assert_eq!(
11551                c.nome(),
11552                c.nome.as_str(),
11553                "Caixa::nome must byte-equal the raw .nome field \
11554                 access across every value in the String accept-set",
11555            );
11556        }
11557    }
11558
11559    #[test]
11560    fn validate_nome_empty_arm_routes_through_accessor() {
11561        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
11562        // key off [`Caixa::nome`], not the raw `.nome` field access.
11563        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
11564        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
11565        // template baseline (the peer positive-arm the sibling
11566        // `validate_nome_accepts_canonical_template` gate carves out)
11567        // must pass validate. The pair jointly pins the accessor +
11568        // validate-gate composition: any future silent detour that
11569        // had the accessor return a fresh `"demo"` on the empty arm
11570        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
11571        // would silently absorb the `NomeEmpty` refusal at the
11572        // accessor boundary and the validate gate would accept a
11573        // struct-literal `Caixa { nome: "".into(), .. }` — the
11574        // composition pin catches that at caixa-core build time.
11575        //
11576        // Peer of the sibling per-`Caixa`
11577        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
11578        // / `validate_repositorio_empty_arm_routes_through_accessor`
11579        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
11580        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
11581        // (2641cbd) composition pins on the sibling outer top-level
11582        // [`Caixa`] `Option<&str>` axes — same "the validate /
11583        // shape-gate predicate must route through the substrate-
11584        // primitive typed dispatch" discipline extended onto the peer
11585        // outer top-level [`Caixa`] required-`&str` composition axis.
11586        let c = caixa_with_nome("");
11587        assert!(
11588            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
11589            "validate_nome must reject nome == \"\" with NomeEmpty — \
11590             the accessor and the validate gate must route through the \
11591             same substrate-primitive typed dispatch on the :nome \
11592             empty-arm",
11593        );
11594        let c = caixa_with_nome("demo");
11595        assert!(
11596            c.validate_nome().is_ok(),
11597            "validate_nome must accept nome == \"demo\" (the canonical \
11598             DNS-1123-label template baseline)",
11599        );
11600    }
11601
11602    #[test]
11603    fn nome_projects_str_by_borrow() {
11604        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
11605        // — the `&str` borrows the underlying `String` storage of the
11606        // required `nome` slot and the accessor must not allocate a
11607        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
11608        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
11609        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
11610        // by-borrow pins on the peer outer top-level [`Caixa`]
11611        // `Option<&str>`-return axes, extended onto the first outer
11612        // top-level [`Caixa`] required-`&str`-return axis — the
11613        // accessor's returned `&str` must borrow from `&self` (the
11614        // returned reference's lifetime is tied to `&self`), and
11615        // calling the accessor twice on the same [`Caixa`] must yield
11616        // the same `&str` verbatim (idempotent, no side effects on
11617        // `&self`).
11618        //
11619        // Pins against a future silent detour that returned an owned
11620        // `String` (which would type-check but silently allocate on
11621        // every call, breaking the zero-cost projection every peer
11622        // sibling accessor carries), an accidental
11623        // `.nome.to_lowercase()` detour that returned a fresh
11624        // allocation through an already-DNS-1123-lowercase-only
11625        // string (breaking a future `const fn` regression), or a
11626        // one-arm-only accessor that returned a canonicalized value
11627        // on some sentinel input (breaking the pass-through invariant
11628        // the sibling required-scalar accessors carry).
11629        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
11630            let c = caixa_with_nome(nome);
11631            let first = c.nome();
11632            let second = c.nome();
11633            assert_eq!(
11634                first, second,
11635                "Caixa::nome must be idempotent — two successive calls \
11636                 on the same &self must return the same &str",
11637            );
11638            assert_eq!(
11639                first, nome,
11640                "Caixa::nome must return :nome verbatim by borrow — \
11641                 got {first}, expected {nome}",
11642            );
11643        }
11644    }
11645
11646    #[test]
11647    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
11648        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
11649        // pinned-version scalar pin: [`Caixa::versao`] must return the
11650        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
11651        // raw `.versao` field access across every representative value
11652        // in the accept-set — the canonical `"0.1.0"` template baseline
11653        // (the same `feira init`-scaffolded default the sibling
11654        // `validate_versao_accepts_canonical_template` positive-control
11655        // gate pins), plus every canonical SemVer-2 shape the sibling
11656        // `validate_versao_accepts_canonical_forms` positive-arm sweep
11657        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
11658        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
11659        // `"10.20.30"`), plus every past-the-guard sentinel for the
11660        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
11661        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
11662        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
11663        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
11664        // `"latest"` the docker-tag-shape footgun — the sentinels pin
11665        // the accessor doesn't silently absorb the refusal cases into a
11666        // template-derived fallback like `"0.1.0"`).
11667        //
11668        // The past-the-guard sentinels pin the accessor doesn't silently
11669        // absorb the refusal cases into a template-derived fallback (a
11670        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
11671        // silently absorb the `VersaoEmpty` refusal at the accessor
11672        // boundary and the validate gate would accept a struct-literal
11673        // `Caixa { versao: "".into(), .. }` — the pin catches that at
11674        // caixa-core build time).
11675        //
11676        // Second outer top-level [`Caixa`] `&str`-return required-scalar
11677        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
11678        // scalar" projection pattern the sibling per-`Caixa`
11679        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
11680        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
11681        // (4127bb6) / per-`:children`
11682        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
11683        // / per-`:upgrade-from`
11684        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
11685        // struct `:versao`-shaped `&str`-return accessor pins on the
11686        // sibling per-typed-slot version-carrier axes, extended onto the
11687        // second outer top-level [`Caixa`] universal-axis required-
11688        // `String`-carry axis so the two universal-axis identity-
11689        // carrying scalars every `defcaixa` form supplies (`:nome` +
11690        // `:versao`) share the same "one typed dispatch per axis" pin
11691        // discipline.
11692        for versao in [
11693            "0.1.0",
11694            "0.0.0",
11695            "1.0.0",
11696            "0.2.0-rc.1",
11697            "1.0.0-alpha.0",
11698            "1.0.0+build.42",
11699            "1.0.0-rc.1+build.42",
11700            "10.20.30",
11701            "",
11702            "v0.1.0",
11703            "0.1",
11704            "^0.1",
11705            "0.1.0.0",
11706            "latest",
11707        ] {
11708            let c = caixa_with_versao(versao);
11709            assert_eq!(
11710                c.versao(),
11711                versao,
11712                "Caixa::versao must return :versao verbatim (got {}, \
11713                 expected {versao})",
11714                c.versao(),
11715            );
11716            assert_eq!(
11717                c.versao(),
11718                c.versao.as_str(),
11719                "Caixa::versao must byte-equal the raw .versao field \
11720                 access across every value in the String accept-set",
11721            );
11722        }
11723    }
11724
11725    #[test]
11726    fn validate_versao_empty_arm_routes_through_accessor() {
11727        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
11728        // must key off [`Caixa::versao`], not the raw `.versao` field
11729        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
11730        // surface the `VersaoEmpty` refusal exactly, and the canonical
11731        // `"0.1.0"` template baseline (the peer positive-arm the sibling
11732        // `validate_versao_accepts_canonical_template` gate carves out)
11733        // must pass validate. The pair jointly pins the accessor +
11734        // validate-gate composition: any future silent detour that had
11735        // the accessor return a fresh `"0.1.0"` on the empty arm
11736        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
11737        // would silently absorb the `VersaoEmpty` refusal at the
11738        // accessor boundary and the validate gate would accept a
11739        // struct-literal `Caixa { versao: "".into(), .. }` — the
11740        // composition pin catches that at caixa-core build time.
11741        //
11742        // Peer of the sibling per-`Caixa`
11743        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
11744        // composition pin on the sibling outer top-level [`Caixa`]
11745        // required-`&str` universal-axis surface — same "the validate /
11746        // shape-gate predicate must route through the substrate-
11747        // primitive typed dispatch" discipline extended onto the peer
11748        // outer top-level [`Caixa`] required-`&str` universal-axis
11749        // pinned-version composition axis, closing the second
11750        // coordinate of the "one canonical typed dispatch per per-Caixa
11751        // required-`&str` universal-axis" discipline.
11752        let c = caixa_with_versao("");
11753        assert!(
11754            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
11755            "validate_versao must reject versao == \"\" with VersaoEmpty — \
11756             the accessor and the validate gate must route through the \
11757             same substrate-primitive typed dispatch on the :versao \
11758             empty-arm",
11759        );
11760        let c = caixa_with_versao("0.1.0");
11761        assert!(
11762            c.validate_versao().is_ok(),
11763            "validate_versao must accept versao == \"0.1.0\" (the \
11764             canonical SemVer-2 template baseline)",
11765        );
11766    }
11767
11768    #[test]
11769    fn versao_projects_str_by_borrow() {
11770        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
11771        // — the `&str` borrows the underlying `String` storage of the
11772        // required `versao` slot and the accessor must not allocate a
11773        // fresh `String` on every call. Peer of the [`Caixa::nome`]
11774        // (e6b7d97) by-borrow pin on the sibling outer top-level
11775        // [`Caixa`] required-`&str`-return axis, extended onto the
11776        // second outer top-level [`Caixa`] required-`&str`-return
11777        // universal-axis pinned-version surface — the accessor's
11778        // returned `&str` must borrow from `&self` (the returned
11779        // reference's lifetime is tied to `&self`), and calling the
11780        // accessor twice on the same [`Caixa`] must yield the same
11781        // `&str` verbatim (idempotent, no side effects on `&self`).
11782        //
11783        // Pins against a future silent detour that returned an owned
11784        // `String` (which would type-check but silently allocate on
11785        // every call, breaking the zero-cost projection every peer
11786        // sibling accessor carries), an accidental
11787        // `semver::Version::parse(&self.versao).unwrap().to_string()`
11788        // detour that returned a canonicalized fresh allocation through
11789        // an already-canonical byte-string (breaking a future `const fn`
11790        // regression and silently absorbing the `VersaoInvalid` refusal
11791        // at the accessor boundary), or a one-arm-only accessor that
11792        // returned a canonicalized value on some sentinel input
11793        // (breaking the pass-through invariant the sibling required-
11794        // scalar accessors carry).
11795        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
11796            let c = caixa_with_versao(versao);
11797            let first = c.versao();
11798            let second = c.versao();
11799            assert_eq!(
11800                first, second,
11801                "Caixa::versao must be idempotent — two successive \
11802                 calls on the same &self must return the same &str",
11803            );
11804            assert_eq!(
11805                first, versao,
11806                "Caixa::versao must return :versao verbatim by borrow \
11807                 — got {first}, expected {versao}",
11808            );
11809        }
11810    }
11811
11812    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
11813        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11814        c.kind = kind;
11815        c
11816    }
11817
11818    #[test]
11819    fn kind_returns_kind_variant_verbatim_across_permutations() {
11820        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
11821        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
11822        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
11823        // the raw `.kind` field access across every variant in the
11824        // closed accept-set (`Biblioteca` — the library kind that
11825        // exports lisp forms; `Binario` — the nix-built executable kind
11826        // under `exe/`; `Servico` — the wasm-component daemon kind
11827        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
11828        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
11829        // composition kind).
11830        //
11831        // Pins against a future silent detour that re-derived the kind
11832        // from a peer axis (an accidental fallback to
11833        // `if !servicos.is_empty() { Servico } else if
11834        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
11835        // collapse that read the code-surface / mesh-slot columns into
11836        // the kind discriminator), a variant remap the operator
11837        // authors on one consumer without the other, or a stale-derive
11838        // detour that substituted [`CaixaKind::Biblioteca`] as the
11839        // default when the field held any other variant (which would
11840        // silently collapse the distinction between "author explicitly
11841        // declared `:kind Servico`" and "author declared any other
11842        // kind" every downstream renderer-dispatch site depends on).
11843        //
11844        // First outer top-level [`Caixa`] `Copy`-return required-enum-
11845        // discriminant accessor pin — opens the "outer [`Caixa`]
11846        // `Copy`-return required-discriminant" projection pattern.
11847        // Sibling in shape to the peer per-`:supervisor`
11848        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
11849        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
11850        // (921fe1b), and per-`:children`
11851        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
11852        // `Copy`-return closed-set-enum discriminant accessor pins on
11853        // the sibling nested-spec typed-slot discriminator axes,
11854        // extended here to the outer top-level [`Caixa`] universal-
11855        // axis surface.
11856        for kind in [
11857            CaixaKind::Biblioteca,
11858            CaixaKind::Binario,
11859            CaixaKind::Servico,
11860            CaixaKind::Supervisor,
11861            CaixaKind::Aplicacao,
11862        ] {
11863            let c = caixa_with_kind(kind);
11864            assert_eq!(
11865                c.kind(),
11866                kind,
11867                "Caixa::kind must return :kind verbatim (got {:?}, \
11868                 expected {kind:?})",
11869                c.kind(),
11870            );
11871            assert_eq!(
11872                c.kind(),
11873                c.kind,
11874                "Caixa::kind accessor and .kind field access must \
11875                 byte-equal — the accessor is the substrate-primitive \
11876                 typed dispatch every downstream kind-gate consumer \
11877                 must route through",
11878            );
11879        }
11880    }
11881
11882    #[test]
11883    fn require_kind_reads_through_lifted_kind_accessor() {
11884        // Two-consumer coherence pin: the [`crate::render::require_kind`]
11885        // entry-gate predicate (the canonical two-line
11886        // `require_kind(caixa, Servico)?` prelude every per-Servico /
11887        // per-Aplicacao renderer runs at its entry-point) and the
11888        // sibling [`crate::render::KindMismatch`] error carrier's
11889        // `actual:` field (which names the offending caixa's variant
11890        // in the diagnostic) must both key off the lifted accessor, so
11891        // any future rebrand on the typed slot's reader shape lands at
11892        // exactly one place. Pins the two-site coherence by exercising
11893        // every off-diagonal `(actual, expected)` pair across the
11894        // closed accept-set — the `KindMismatch { actual, expected }`
11895        // surfaced on the mismatch arm must byte-equal the pair the
11896        // accessor returns for each side.
11897        //
11898        // Peer of the sibling per-`:placement`
11899        // `validate_placement_reads_through_lifted_estrategia_accessor`
11900        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
11901        // `Copy`-return discriminant axis — same "the entry-gate
11902        // predicate and the error carrier's `actual:` field must route
11903        // through the substrate-primitive typed dispatch" discipline
11904        // extended onto the outer top-level [`Caixa`] universal-axis
11905        // discriminant surface.
11906        for expected in [
11907            CaixaKind::Biblioteca,
11908            CaixaKind::Binario,
11909            CaixaKind::Servico,
11910            CaixaKind::Supervisor,
11911            CaixaKind::Aplicacao,
11912        ] {
11913            for actual in [
11914                CaixaKind::Biblioteca,
11915                CaixaKind::Binario,
11916                CaixaKind::Servico,
11917                CaixaKind::Supervisor,
11918                CaixaKind::Aplicacao,
11919            ] {
11920                let c = caixa_with_kind(actual);
11921                let result = crate::render::require_kind(&c, expected);
11922                if expected == actual {
11923                    assert!(
11924                        result.is_ok(),
11925                        "require_kind must accept when actual == expected \
11926                         (actual={actual:?}, expected={expected:?})",
11927                    );
11928                } else {
11929                    let err = result.expect_err("require_kind must reject when actual != expected");
11930                    assert_eq!(
11931                        err.actual,
11932                        c.kind(),
11933                        "KindMismatch.actual must byte-equal Caixa::kind() \
11934                         — the error carrier's `actual:` field reads \
11935                         through the lifted accessor",
11936                    );
11937                    assert_eq!(
11938                        err.expected, expected,
11939                        "KindMismatch.expected must byte-equal the \
11940                         expected variant passed to require_kind",
11941                    );
11942                }
11943            }
11944        }
11945    }
11946
11947    #[test]
11948    fn aplicacao_view_kind_gate_routes_through_accessor() {
11949        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
11950        // must key off [`Caixa::kind`], not the raw `.kind` field
11951        // access. Structurally: a `Caixa { kind: X, .. }` for any
11952        // non-`Aplicacao` variant must fold to `None` on the
11953        // `aplicacao_view` composer (the "kind mismatch → no typed
11954        // view" contract every downstream Aplicacao consumer keys off
11955        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
11956        // `Some(_)`. The pair jointly pins the accessor + view-gate
11957        // composition: any future silent detour that had the accessor
11958        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
11959        // input would silently absorb the kind-mismatch case at the
11960        // accessor boundary and every per-Aplicacao renderer would
11961        // silently render a non-Aplicacao caixa's mesh slots — the
11962        // composition pin catches that at caixa-core build time.
11963        //
11964        // Peer of the sibling per-`Caixa`
11965        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
11966        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
11967        // composition pins on the sibling outer top-level [`Caixa`]
11968        // required-`&str` universal-axis surfaces — same "the
11969        // composer / validate gate must route through the substrate-
11970        // primitive typed dispatch" discipline extended onto the
11971        // outer top-level [`Caixa`] `Copy`-return required-
11972        // discriminant composition axis.
11973        for kind in [
11974            CaixaKind::Biblioteca,
11975            CaixaKind::Binario,
11976            CaixaKind::Servico,
11977            CaixaKind::Supervisor,
11978        ] {
11979            let c = caixa_with_kind(kind);
11980            assert!(
11981                c.aplicacao_view().is_none(),
11982                "aplicacao_view must return None on non-Aplicacao \
11983                 kind {kind:?} — the composer's kind-gate must route \
11984                 through Caixa::kind()",
11985            );
11986        }
11987        let c = caixa_with_kind(CaixaKind::Aplicacao);
11988        assert!(
11989            c.aplicacao_view().is_some(),
11990            "aplicacao_view must return Some on kind Aplicacao — \
11991             the composer's kind-gate must accept the matching arm \
11992             through Caixa::kind()",
11993        );
11994    }
11995
11996    #[test]
11997    fn supervisor_view_kind_gate_routes_through_accessor() {
11998        // Composition pin (mirror of the sibling
11999        // `aplicacao_view_kind_gate_routes_through_accessor` on the
12000        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
12001        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
12002        // field access. A `Caixa { kind: X, .. }` for any non-
12003        // `Supervisor` variant must fold to `None` on the
12004        // `supervisor_view` composer, and a `Caixa { kind:
12005        // Supervisor, .. }` must fold to `Some(_)`. Same peer
12006        // composition pin discipline on the second `_view` composer
12007        // axis.
12008        for kind in [
12009            CaixaKind::Biblioteca,
12010            CaixaKind::Binario,
12011            CaixaKind::Servico,
12012            CaixaKind::Aplicacao,
12013        ] {
12014            let c = caixa_with_kind(kind);
12015            assert!(
12016                c.supervisor_view().is_none(),
12017                "supervisor_view must return None on non-Supervisor \
12018                 kind {kind:?} — the composer's kind-gate must route \
12019                 through Caixa::kind()",
12020            );
12021        }
12022        let mut c = caixa_with_kind(CaixaKind::Supervisor);
12023        // A Supervisor caixa needs a strategy + at least one child to
12024        // fold to a Some(_) that also validates; the composer itself
12025        // requires only the kind arm, so bare kind flip is enough to
12026        // pin the `Some(_)` return, but we populate the minimum
12027        // supervisor shape so a future strengthening of the composer
12028        // to reject an empty spec doesn't false-positive this pin.
12029        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
12030        c.children = vec![crate::supervisor::ChildSpec {
12031            caixa: "child".into(),
12032            versao: "^0.1".into(),
12033            restart: crate::supervisor::RestartPolicy::Permanent,
12034        }];
12035        assert!(
12036            c.supervisor_view().is_some(),
12037            "supervisor_view must return Some on kind Supervisor — \
12038             the composer's kind-gate must accept the matching arm \
12039             through Caixa::kind()",
12040        );
12041    }
12042
12043    #[test]
12044    fn kind_projects_by_copy() {
12045        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
12046        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
12047        // `&self` (the returned value is owned, `Copy`-projected from
12048        // the underlying [`CaixaKind`] storage; two calls on the same
12049        // [`Caixa`] must yield byte-equal values). Peer of the peer
12050        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
12051        // `SupervisorSpec::estrategia` / per-`:children`
12052        // `ChildSpec::restart` `Copy`-return discriminant accessor
12053        // pins on the sibling nested-spec typed-slot discriminator
12054        // axes, extended onto the first outer top-level [`Caixa`]
12055        // required-`Copy`-return axis — pins against a future silent
12056        // detour that returned `&CaixaKind` (which would type-check
12057        // but silently constrain every consumer's callsite to a
12058        // borrow-shaped dispatch, breaking the zero-cost `Copy`
12059        // projection every peer sibling accessor carries).
12060        for kind in [
12061            CaixaKind::Biblioteca,
12062            CaixaKind::Binario,
12063            CaixaKind::Servico,
12064            CaixaKind::Supervisor,
12065            CaixaKind::Aplicacao,
12066        ] {
12067            let c = caixa_with_kind(kind);
12068            let first: CaixaKind = c.kind();
12069            let second: CaixaKind = c.kind();
12070            assert_eq!(
12071                first, second,
12072                "Caixa::kind must be idempotent — two successive \
12073                 calls on the same &self must return the same \
12074                 CaixaKind variant",
12075            );
12076            assert_eq!(
12077                first, kind,
12078                "Caixa::kind must return :kind verbatim by Copy — \
12079                 got {first:?}, expected {kind:?}",
12080            );
12081        }
12082    }
12083
12084    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
12085
12086    #[test]
12087    fn autores_returns_autores_slice_verbatim_across_permutations() {
12088        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
12089        // name-list slice pin: [`Caixa::autores`] must return the
12090        // `:autores` typed [`Vec<String>`] list verbatim as a
12091        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
12092        // access across every representative value in the accept-set —
12093        // `[]` (the "no maintainers declared" arm every existing
12094        // fixture without an `:autores` line carries), `[""]` (a past-
12095        // the-guard sentinel that pins the accessor doesn't perform a
12096        // silent `[""] → []` collapse on the empty-entry arm — validate
12097        // rejects `[""]` through `AutorEmpty` but the accessor must
12098        // ship the raw slot verbatim so a validate-time gate regression
12099        // surfaces at the caixa-helm emit boundary rather than being
12100        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
12101        // canonical single-maintainer form every `feira init` template
12102        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
12103        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
12104        // (the canonical RFC-5322 `<name> <email>` form the
12105        // `is_chart_maintainer_name_shape` predicate accepts), and
12106        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
12107        // sentinel — validate rejects through `AutorDuplicate` but the
12108        // accessor must ship the raw slot verbatim).
12109        //
12110        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
12111        // pin on the substrate primitive — opens the "outer [`Caixa`]
12112        // `&[T]` slice" projection pattern the sibling per-`Caixa`
12113        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
12114        // / `:servicos` / `:upgrade-from` / `:children` future lifts
12115        // fold on. Sibling in shape to the peer per-`:supervisor`
12116        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
12117        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
12118        // (a6e18d7), per-`:membros`
12119        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
12120        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
12121        // (0dcc926), and per-`:upgrade-from :instructions`
12122        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
12123        // `&[T]`-return slice accessor pins on the sibling per-M2 /
12124        // per-M3 typed-slot list axes, extended onto the outer top-
12125        // level [`Caixa`] universal-axis surface. Pins against a future
12126        // silent detour that returned an owned `Vec<String>` (which
12127        // would type-check but silently clone on every accessor call,
12128        // breaking the zero-cost projection every peer sibling slice
12129        // accessor carries), a `[""] → []` collapse (which would
12130        // silently absorb the `AutorEmpty` refusal case at the accessor
12131        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
12132        // would silently absorb the `AutorDuplicate` refusal case at
12133        // the accessor boundary and the caixa-helm `maintainers:` fold
12134        // would silently render a dedupped list on a struct-literal
12135        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
12136        for autores in [
12137            vec![],
12138            vec![""],
12139            vec!["pleme-io"],
12140            vec!["alice", "bob"],
12141            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
12142            vec!["pleme-io", "pleme-io"],
12143        ] {
12144            let c = caixa_with_autores(autores.clone());
12145            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
12146            assert_eq!(
12147                c.autores(),
12148                expected.as_slice(),
12149                "Caixa::autores must return :autores verbatim (got {:?}, \
12150                 expected {expected:?})",
12151                c.autores(),
12152            );
12153            assert_eq!(
12154                c.autores(),
12155                c.autores.as_slice(),
12156                "Caixa::autores must byte-equal the raw \
12157                 `self.autores.as_slice()` field access across every \
12158                 value in the Vec<String> accept-set",
12159            );
12160        }
12161    }
12162
12163    #[test]
12164    fn validate_autores_empty_entry_arm_routes_through_accessor() {
12165        // Composition pin: [`Caixa::validate_autores`]'s per-entry
12166        // empty-arm gate must key off [`Caixa::autores`], not the raw
12167        // `&self.autores` field-borrow walk. Structurally: a
12168        // `Caixa { autores: vec!["".into()], .. }` must surface the
12169        // `AutorEmpty` refusal exactly, and a
12170        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
12171        // canonical single-maintainer form) must pass validate. The
12172        // pair jointly pins the accessor + validate-gate composition:
12173        // any future silent detour that had the accessor return an
12174        // empty slice on the `[""]` arm (a
12175        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
12176        // would silently absorb the `AutorEmpty` refusal at the
12177        // accessor boundary and the validate gate would accept a
12178        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
12179        // the composition pin catches that at caixa-core build time.
12180        //
12181        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
12182        // accessor-composition pin
12183        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
12184        // sibling `Option<&str>`-composition axis and the
12185        // per-`:politicas :circuit-breaker`
12186        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
12187        // accessor-composition pin
12188        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
12189        // on the sibling required-`u32`-composition axis — same "the
12190        // validate / shape-gate predicate must route through the
12191        // substrate-primitive typed dispatch" discipline extended onto
12192        // the outer top-level [`Caixa`] universal-axis `&[T]`-
12193        // composition surface.
12194        let c = caixa_with_autores(vec![""]);
12195        assert!(
12196            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
12197            "validate_autores must reject autores == vec![\"\"] with \
12198             AutorEmpty — the accessor and the validate gate must \
12199             route through the same substrate-primitive typed dispatch \
12200             on the :autores per-entry empty arm",
12201        );
12202        let c = caixa_with_autores(vec!["pleme-io"]);
12203        assert!(
12204            c.validate_autores().is_ok(),
12205            "validate_autores must accept autores == vec![\"pleme-io\"] \
12206             (the canonical single-maintainer shape every `feira init` \
12207             template scaffolds)",
12208        );
12209    }
12210
12211    #[test]
12212    fn autores_projects_slice_by_borrow() {
12213        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
12214        // borrow — the returned slice borrows the underlying
12215        // `Vec<String>` storage of the `:autores` slot and the
12216        // accessor must not clone the backing `Vec` on every call.
12217        // Peer of the per-`:membros`
12218        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
12219        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
12220        // (0dcc926) / per-`:placement`
12221        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
12222        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
12223        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
12224        // typed-slot `&[T]`-return axes, extended onto the outer top-
12225        // level [`Caixa`] universal-axis `&[String]` shape — the
12226        // accessor's returned slice must borrow from `&self` (the
12227        // returned reference's lifetime is tied to `&self`), and
12228        // calling the accessor twice on the same [`Caixa`] must yield
12229        // slices that are pointer-equal (the underlying byte-buffer is
12230        // the storage `Vec`'s allocation, not a fresh copy) as well as
12231        // value-equal (idempotent, no side effects on `&self`).
12232        //
12233        // Pins against a future silent detour that returned an owned
12234        // `Vec<String>` (which would type-check but silently clone on
12235        // every call, breaking the zero-cost projection every peer
12236        // sibling slice accessor carries), a `&Vec<String>` return
12237        // (which would leak the backing `Vec`'s grow/push/reserve
12238        // surface no downstream consumer reaches for), or a one-arm-
12239        // only accessor that returned a saturating value on some
12240        // sentinel input (breaking the pass-through invariant the
12241        // sibling slice accessors carry).
12242        for autores in [
12243            vec![],
12244            vec!["pleme-io"],
12245            vec!["alice", "bob"],
12246            vec!["pleme-io", "pleme-io"],
12247        ] {
12248            let c = caixa_with_autores(autores.clone());
12249            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
12250            let first = c.autores();
12251            let second = c.autores();
12252            assert_eq!(
12253                first, second,
12254                "Caixa::autores must be idempotent — two successive \
12255                 calls on the same &self must return the same \
12256                 &[String]",
12257            );
12258            assert_eq!(
12259                first.as_ptr(),
12260                second.as_ptr(),
12261                "Caixa::autores must borrow the underlying Vec<String> \
12262                 storage — two successive calls must return slices \
12263                 with the same backing pointer (a fresh Vec<String> \
12264                 clone would change the pointer on every call)",
12265            );
12266            assert_eq!(
12267                first,
12268                expected.as_slice(),
12269                "Caixa::autores must return :autores verbatim by \
12270                 borrow — got {first:?}, expected {expected:?}",
12271            );
12272        }
12273    }
12274
12275    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
12276
12277    #[test]
12278    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
12279        // The canonical per-`Caixa` `:etiquetas` universal-axis
12280        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
12281        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
12282        // as a `&[String]`, byte-equal to the raw
12283        // `self.etiquetas.as_slice()` access across every representative
12284        // value in the accept-set — `[]` (the "no tags declared" arm
12285        // every existing fixture without an `:etiquetas` line carries),
12286        // `[""]` (a past-the-guard sentinel that pins the accessor
12287        // doesn't perform a silent `[""] → []` collapse on the empty-
12288        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
12289        // but the accessor must ship the raw slot verbatim so a
12290        // validate-time gate regression surfaces at the caixa-helm emit
12291        // boundary rather than being silently absorbed into a keyword-
12292        // drop), `["demo"]` (the canonical single-tag form every
12293        // `feira init` template scaffolds), `["example", "aplicacao",
12294        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
12295        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
12296        // (a past-the-guard duplicate sentinel — validate rejects
12297        // through `EtiquetaDuplicate` but the accessor must ship the
12298        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
12299        // at chart-render time isn't silently promoted into the
12300        // accessor boundary and struct-literal
12301        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
12302        // fixtures continue to expose the duplicate at the accessor).
12303        //
12304        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
12305        // pin on the substrate primitive — folds on the "outer
12306        // [`Caixa`] `&[T]` slice" projection pattern
12307        // `autores_returns_autores_slice_verbatim_across_permutations`
12308        // (b5d813f) opened, sibling in shape and idiom. Pins against a
12309        // future silent detour that returned an owned `Vec<String>`
12310        // (which would type-check but silently clone on every accessor
12311        // call, breaking the zero-cost projection every peer sibling
12312        // slice accessor carries), a `[""] → []` collapse (which would
12313        // silently absorb the `EtiquetaEmpty` refusal case at the
12314        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
12315        // (which would silently absorb the `EtiquetaDuplicate` refusal
12316        // case at the accessor boundary — the caixa-helm chart-render
12317        // `BTreeSet::collect` dedup is downstream of the accessor and
12318        // must not be silently promoted into it).
12319        for etiquetas in [
12320            vec![],
12321            vec![""],
12322            vec!["demo"],
12323            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
12324            vec!["demo", "demo"],
12325        ] {
12326            let c = caixa_with_etiquetas(etiquetas.clone());
12327            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
12328            assert_eq!(
12329                c.etiquetas(),
12330                expected.as_slice(),
12331                "Caixa::etiquetas must return :etiquetas verbatim (got \
12332                 {:?}, expected {expected:?})",
12333                c.etiquetas(),
12334            );
12335            assert_eq!(
12336                c.etiquetas(),
12337                c.etiquetas.as_slice(),
12338                "Caixa::etiquetas must byte-equal the raw \
12339                 `self.etiquetas.as_slice()` field access across every \
12340                 value in the Vec<String> accept-set",
12341            );
12342        }
12343    }
12344
12345    #[test]
12346    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
12347        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
12348        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
12349        // `&self.etiquetas` field-borrow walk. Structurally: a
12350        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
12351        // `EtiquetaEmpty` refusal exactly, and a
12352        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
12353        // single-tag form) must pass validate. The pair jointly pins
12354        // the accessor + validate-gate composition: any future silent
12355        // detour that had the accessor return an empty slice on the
12356        // `[""]` arm (a
12357        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
12358        // silently absorb the `EtiquetaEmpty` refusal at the accessor
12359        // boundary and the validate gate would accept a struct-literal
12360        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
12361        // pin catches that at caixa-core build time.
12362        //
12363        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
12364        // through_accessor` (b5d813f) accessor-composition pin on the
12365        // sibling `&[T]`-composition axis — same "the validate / shape-
12366        // gate predicate must route through the substrate-primitive
12367        // typed dispatch" discipline extended onto the sibling outer
12368        // top-level [`Caixa`] `&[T]`-composition surface.
12369        let c = caixa_with_etiquetas(vec![""]);
12370        assert!(
12371            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
12372            "validate_etiquetas must reject etiquetas == vec![\"\"] \
12373             with EtiquetaEmpty — the accessor and the validate gate \
12374             must route through the same substrate-primitive typed \
12375             dispatch on the :etiquetas per-entry empty arm",
12376        );
12377        let c = caixa_with_etiquetas(vec!["demo"]);
12378        assert!(
12379            c.validate_etiquetas().is_ok(),
12380            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
12381             (the canonical single-tag shape every `feira init` \
12382             template scaffolds)",
12383        );
12384    }
12385
12386    #[test]
12387    fn etiquetas_projects_slice_by_borrow() {
12388        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
12389        // by borrow — the returned slice borrows the underlying
12390        // `Vec<String>` storage of the `:etiquetas` slot and the
12391        // accessor must not clone the backing `Vec` on every call.
12392        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12393        // (b5d813f) by-borrow pin on the sibling outer top-level
12394        // [`Caixa`] `&[String]`-return axis — the accessor's returned
12395        // slice must borrow from `&self` (the returned reference's
12396        // lifetime is tied to `&self`), and calling the accessor twice
12397        // on the same [`Caixa`] must yield slices that are pointer-
12398        // equal (the underlying byte-buffer is the storage `Vec`'s
12399        // allocation, not a fresh copy) as well as value-equal
12400        // (idempotent, no side effects on `&self`).
12401        //
12402        // Pins against a future silent detour that returned an owned
12403        // `Vec<String>` (which would type-check but silently clone on
12404        // every call, breaking the zero-cost projection every peer
12405        // sibling slice accessor carries), a `&Vec<String>` return
12406        // (which would leak the backing `Vec`'s grow/push/reserve
12407        // surface no downstream consumer reaches for), or a one-arm-
12408        // only accessor that returned a saturating value on some
12409        // sentinel input (breaking the pass-through invariant the
12410        // sibling slice accessors carry).
12411        for etiquetas in [
12412            vec![],
12413            vec!["demo"],
12414            vec!["example", "aplicacao", "mesh"],
12415            vec!["demo", "demo"],
12416        ] {
12417            let c = caixa_with_etiquetas(etiquetas.clone());
12418            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
12419            let first = c.etiquetas();
12420            let second = c.etiquetas();
12421            assert_eq!(
12422                first, second,
12423                "Caixa::etiquetas must be idempotent — two successive \
12424                 calls on the same &self must return the same \
12425                 &[String]",
12426            );
12427            assert_eq!(
12428                first.as_ptr(),
12429                second.as_ptr(),
12430                "Caixa::etiquetas must borrow the underlying \
12431                 Vec<String> storage — two successive calls must \
12432                 return slices with the same backing pointer (a fresh \
12433                 Vec<String> clone would change the pointer on every \
12434                 call)",
12435            );
12436            assert_eq!(
12437                first,
12438                expected.as_slice(),
12439                "Caixa::etiquetas must return :etiquetas verbatim by \
12440                 borrow — got {first:?}, expected {expected:?}",
12441            );
12442        }
12443    }
12444
12445    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
12446
12447    #[test]
12448    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
12449        // The canonical per-`Caixa` `:bibliotecas` universal-axis
12450        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
12451        // must return the `:bibliotecas` typed [`Vec<String>`] list
12452        // verbatim as a `&[String]`, byte-equal to the raw
12453        // `self.bibliotecas.as_slice()` access across every
12454        // representative value in the accept-set — `[]` (the "no
12455        // libraries declared" arm every `:kind` other than `Biblioteca`
12456        // + every `Biblioteca` relying on the canonical
12457        // `lib/<nome>.lisp` implicit-default path carries; the
12458        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
12459        // fires exactly on this empty-slot + `Biblioteca`-kind
12460        // combination), `[""]` (a past-the-guard sentinel that pins
12461        // the accessor doesn't perform a silent `[""] → []` collapse
12462        // on the empty-entry arm — validate rejects `[""]` through
12463        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
12464        // must ship the raw slot verbatim so a validate-time gate
12465        // regression surfaces at the `feira build` phase-1 parse
12466        // boundary rather than being silently absorbed into a
12467        // library-drop), `["lib/demo.lisp"]` (the canonical single-
12468        // entry form `Caixa::template` scaffolds and every `feira init`
12469        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
12470        // (the canonical multi-library form the
12471        // `validate_code_paths_accepts_explicit_relative_paths_on_
12472        // every_slot` fixture emits), and `["lib/foo.lisp",
12473        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
12474        // validate rejects through `CodePathDuplicate { slot:
12475        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
12476        // but the accessor must ship the raw slot verbatim so the
12477        // `feira build` `for entry in caixa.bibliotecas()` parse walk
12478        // sees the duplicate at the accessor boundary and struct-
12479        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
12480        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
12481        // the duplicate at the accessor).
12482        //
12483        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
12484        // pin on the substrate primitive — folds on the "outer
12485        // [`Caixa`] `&[T]` slice" projection pattern
12486        // `autores_returns_autores_slice_verbatim_across_permutations`
12487        // (b5d813f) opened and
12488        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12489        // (78c7d3c) folded on, sibling in shape and idiom. Pins
12490        // against a future silent detour that returned an owned
12491        // `Vec<String>` (which would type-check but silently clone on
12492        // every accessor call, breaking the zero-cost projection
12493        // every peer sibling slice accessor carries), a `[""] → []`
12494        // collapse (which would silently absorb the `CodePathEmpty`
12495        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
12496        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
12497        // would silently absorb the `CodePathDuplicate` refusal case
12498        // at the accessor boundary — the per-slot set-not-multiset
12499        // gate is downstream of the accessor and must not be silently
12500        // promoted into it).
12501        for bibliotecas in [
12502            vec![],
12503            vec![""],
12504            vec!["lib/demo.lisp"],
12505            vec!["lib/demo.lisp", "lib/helpers.lisp"],
12506            vec!["lib/foo.lisp", "lib/foo.lisp"],
12507        ] {
12508            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
12509            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
12510            assert_eq!(
12511                c.bibliotecas(),
12512                expected.as_slice(),
12513                "Caixa::bibliotecas must return :bibliotecas verbatim \
12514                 (got {:?}, expected {expected:?})",
12515                c.bibliotecas(),
12516            );
12517            assert_eq!(
12518                c.bibliotecas(),
12519                c.bibliotecas.as_slice(),
12520                "Caixa::bibliotecas must byte-equal the raw \
12521                 `self.bibliotecas.as_slice()` field access across \
12522                 every value in the Vec<String> accept-set",
12523            );
12524        }
12525    }
12526
12527    #[test]
12528    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
12529        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12530        // empty-arm gate on the `:bibliotecas` slot must key off
12531        // [`Caixa::bibliotecas`], not a divergent raw
12532        // `&self.bibliotecas` field-borrow walk. Structurally: a
12533        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
12534        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
12535        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
12536        // into()], .. }` (the canonical single-library form
12537        // `Caixa::template` scaffolds) must pass validate. The pair
12538        // jointly pins the accessor + validate-gate composition: any
12539        // future silent detour that had the accessor return an empty
12540        // slice on the `[""]` arm (a `.iter().filter(|s|
12541        // !s.is_empty()).collect()` collapse) would silently absorb
12542        // the `CodePathEmpty` refusal at the accessor boundary and
12543        // the validate gate would accept a struct-literal
12544        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
12545        // composition pin catches that at caixa-core build time.
12546        //
12547        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
12548        // through_accessor` (b5d813f) and
12549        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12550        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12551        // composition axes — same "the validate / shape-gate
12552        // predicate must route through the substrate-primitive typed
12553        // dispatch" discipline extended onto the sibling outer top-
12554        // level [`Caixa`] `&[T]`-composition surface. Nominally the
12555        // in-tree `validate_code_paths` production body still keys
12556        // off the internal `[(":bibliotecas", &self.bibliotecas,
12557        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12558        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12559        // (the tuple's homogeneous slice-typed shape blocks a per-
12560        // element accessor swap in isolation — a future companion
12561        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
12562        // `&[T]` slice-accessor axis closes that tuple onto the
12563        // triple of typed dispatches as a unit); the composition pin
12564        // catches any future accessor-side silent filter drop against
12565        // that eventual tuple-closure regardless of whether the
12566        // `:bibliotecas` slot is threaded through the accessor or the
12567        // raw field access at the tuple's construction site.
12568        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
12569        assert!(
12570            matches!(
12571                c.validate_code_paths(),
12572                Err(ManifestError::CodePathEmpty {
12573                    slot: ":bibliotecas"
12574                })
12575            ),
12576            "validate_code_paths must reject bibliotecas == vec![\"\"] \
12577             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
12578             accessor and the validate gate must route through the \
12579             same substrate-primitive typed dispatch on the \
12580             :bibliotecas per-entry empty arm",
12581        );
12582        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
12583        assert!(
12584            c.validate_code_paths().is_ok(),
12585            "validate_code_paths must accept bibliotecas == \
12586             vec![\"lib/demo.lisp\"] (the canonical single-library \
12587             shape every `feira init` template scaffolds)",
12588        );
12589    }
12590
12591    #[test]
12592    fn bibliotecas_projects_slice_by_borrow() {
12593        // The by-borrow pin: [`Caixa::bibliotecas`] returns
12594        // `&[String]` by borrow — the returned slice borrows the
12595        // underlying `Vec<String>` storage of the `:bibliotecas` slot
12596        // and the accessor must not clone the backing `Vec` on every
12597        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12598        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
12599        // by-borrow pins on the sibling outer top-level [`Caixa`]
12600        // `&[String]`-return axes — the accessor's returned slice
12601        // must borrow from `&self` (the returned reference's lifetime
12602        // is tied to `&self`), and calling the accessor twice on the
12603        // same [`Caixa`] must yield slices that are pointer-equal
12604        // (the underlying byte-buffer is the storage `Vec`'s
12605        // allocation, not a fresh copy) as well as value-equal
12606        // (idempotent, no side effects on `&self`).
12607        //
12608        // Pins against a future silent detour that returned an owned
12609        // `Vec<String>` (which would type-check but silently clone on
12610        // every call, breaking the zero-cost projection every peer
12611        // sibling slice accessor carries), a `&Vec<String>` return
12612        // (which would leak the backing `Vec`'s grow/push/reserve
12613        // surface no downstream consumer reaches for), or a one-arm-
12614        // only accessor that returned a saturating value on some
12615        // sentinel input (breaking the pass-through invariant the
12616        // sibling slice accessors carry).
12617        for bibliotecas in [
12618            vec![],
12619            vec!["lib/demo.lisp"],
12620            vec!["lib/demo.lisp", "lib/helpers.lisp"],
12621            vec!["lib/foo.lisp", "lib/foo.lisp"],
12622        ] {
12623            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
12624            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
12625            let first = c.bibliotecas();
12626            let second = c.bibliotecas();
12627            assert_eq!(
12628                first, second,
12629                "Caixa::bibliotecas must be idempotent — two \
12630                 successive calls on the same &self must return the \
12631                 same &[String]",
12632            );
12633            assert_eq!(
12634                first.as_ptr(),
12635                second.as_ptr(),
12636                "Caixa::bibliotecas must borrow the underlying \
12637                 Vec<String> storage — two successive calls must \
12638                 return slices with the same backing pointer (a \
12639                 fresh Vec<String> clone would change the pointer on \
12640                 every call)",
12641            );
12642            assert_eq!(
12643                first,
12644                expected.as_slice(),
12645                "Caixa::bibliotecas must return :bibliotecas verbatim \
12646                 by borrow — got {first:?}, expected {expected:?}",
12647            );
12648        }
12649    }
12650
12651    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
12652
12653    #[test]
12654    fn exe_returns_exe_slice_verbatim_across_permutations() {
12655        // The canonical per-`Caixa` `:exe` universal-axis
12656        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
12657        // must return the `:exe` typed [`Vec<String>`] list verbatim as
12658        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
12659        // access across every representative value in the accept-set —
12660        // `[]` (the "no executable declared" arm every `:kind` other
12661        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
12662        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
12663        // + `Binario`-kind combination), `[""]` (a past-the-guard
12664        // sentinel that pins the accessor doesn't perform a silent
12665        // `[""] → []` collapse on the empty-entry arm — validate rejects
12666        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
12667        // accessor must ship the raw slot verbatim so a validate-time
12668        // gate regression surfaces at the layout / `feira nix` boundary
12669        // rather than being silently absorbed into an executable-drop),
12670        // `["exe/cli"]` (the canonical single-entry Binario form every
12671        // in-tree `caixa_with_code_paths` positive control uses),
12672        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
12673        // form the `validate_code_paths_accepts_explicit_relative_paths_
12674        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
12675        // (a past-the-guard duplicate sentinel — validate rejects
12676        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
12677        // set-not-multiset gate, but the accessor must ship the raw
12678        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
12679        // into(), "exe/cli".into()], .. }` fixtures continue to expose
12680        // the duplicate at the accessor).
12681        //
12682        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
12683        // pin on the substrate primitive — folds on the "outer
12684        // [`Caixa`] `&[T]` slice" projection pattern
12685        // `autores_returns_autores_slice_verbatim_across_permutations`
12686        // (b5d813f) opened,
12687        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12688        // (78c7d3c) folded on, and
12689        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12690        // (8a36c23) closed the universal-axis text-tag family of.
12691        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
12692        // the sibling `:servicos` future lift closes onto. Pins against
12693        // a future silent detour that returned an owned `Vec<String>`
12694        // (which would type-check but silently clone on every accessor
12695        // call, breaking the zero-cost projection every peer sibling
12696        // slice accessor carries), a `[""] → []` collapse (which would
12697        // silently absorb the `CodePathEmpty` refusal case at the
12698        // accessor boundary), or an `["exe/cli", "exe/cli"] →
12699        // ["exe/cli"]` dedup collapse (which would silently absorb the
12700        // `CodePathDuplicate` refusal case at the accessor boundary —
12701        // the per-slot set-not-multiset gate is downstream of the
12702        // accessor and must not be silently promoted into it).
12703        for exe in [
12704            vec![],
12705            vec![""],
12706            vec!["exe/cli"],
12707            vec!["exe/cli", "exe/serve"],
12708            vec!["exe/cli", "exe/cli"],
12709        ] {
12710            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
12711            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
12712            assert_eq!(
12713                c.exe(),
12714                expected.as_slice(),
12715                "Caixa::exe must return :exe verbatim (got {:?}, \
12716                 expected {expected:?})",
12717                c.exe(),
12718            );
12719            assert_eq!(
12720                c.exe(),
12721                c.exe.as_slice(),
12722                "Caixa::exe must byte-equal the raw \
12723                 `self.exe.as_slice()` field access across every value \
12724                 in the Vec<String> accept-set",
12725            );
12726        }
12727    }
12728
12729    #[test]
12730    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
12731        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12732        // empty-arm gate on the `:exe` slot must key off
12733        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
12734        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
12735        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
12736        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
12737        // (the canonical single-executable form every in-tree
12738        // `caixa_with_code_paths` positive control uses) must pass
12739        // validate. The pair jointly pins the accessor + validate-gate
12740        // composition: any future silent detour that had the accessor
12741        // return an empty slice on the `[""]` arm (a
12742        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
12743        // silently absorb the `CodePathEmpty` refusal at the accessor
12744        // boundary and the validate gate would accept a struct-literal
12745        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
12746        // catches that at caixa-core build time.
12747        //
12748        // Peer of the per-`Caixa`
12749        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12750        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
12751        // (b5d813f), and
12752        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12753        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12754        // composition axes — same "the validate / shape-gate predicate
12755        // must route through the substrate-primitive typed dispatch"
12756        // discipline extended onto the sibling outer top-level [`Caixa`]
12757        // `&[T]`-composition surface. Nominally the in-tree
12758        // `validate_code_paths` production body still keys off the
12759        // internal `[(":bibliotecas", &self.bibliotecas,
12760        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12761        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12762        // (the tuple's homogeneous slice-typed shape blocks a per-
12763        // element accessor swap in isolation — a future companion lift
12764        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
12765        // accessor axis closes that tuple onto the triple of typed
12766        // dispatches as a unit); the composition pin catches any future
12767        // accessor-side silent filter drop against that eventual tuple-
12768        // closure regardless of whether the `:exe` slot is threaded
12769        // through the accessor or the raw field access at the tuple's
12770        // construction site.
12771        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
12772        assert!(
12773            matches!(
12774                c.validate_code_paths(),
12775                Err(ManifestError::CodePathEmpty { slot: ":exe" })
12776            ),
12777            "validate_code_paths must reject exe == vec![\"\"] \
12778             with CodePathEmpty {{ slot: \":exe\" }} — the \
12779             accessor and the validate gate must route through the \
12780             same substrate-primitive typed dispatch on the \
12781             :exe per-entry empty arm",
12782        );
12783        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
12784        assert!(
12785            c.validate_code_paths().is_ok(),
12786            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
12787             (the canonical single-executable shape every in-tree \
12788             `caixa_with_code_paths` positive control uses)",
12789        );
12790    }
12791
12792    #[test]
12793    fn exe_projects_slice_by_borrow() {
12794        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
12795        // borrow — the returned slice borrows the underlying
12796        // `Vec<String>` storage of the `:exe` slot and the accessor
12797        // must not clone the backing `Vec` on every call. Peer of the
12798        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
12799        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
12800        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
12801        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
12802        // return axes — the accessor's returned slice must borrow from
12803        // `&self` (the returned reference's lifetime is tied to
12804        // `&self`), and calling the accessor twice on the same
12805        // [`Caixa`] must yield slices that are pointer-equal (the
12806        // underlying byte-buffer is the storage `Vec`'s allocation,
12807        // not a fresh copy) as well as value-equal (idempotent, no
12808        // side effects on `&self`).
12809        //
12810        // Pins against a future silent detour that returned an owned
12811        // `Vec<String>` (which would type-check but silently clone on
12812        // every call, breaking the zero-cost projection every peer
12813        // sibling slice accessor carries), a `&Vec<String>` return
12814        // (which would leak the backing `Vec`'s grow/push/reserve
12815        // surface no downstream consumer reaches for), or a one-arm-
12816        // only accessor that returned a saturating value on some
12817        // sentinel input (breaking the pass-through invariant the
12818        // sibling slice accessors carry).
12819        for exe in [
12820            vec![],
12821            vec!["exe/cli"],
12822            vec!["exe/cli", "exe/serve"],
12823            vec!["exe/cli", "exe/cli"],
12824        ] {
12825            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
12826            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
12827            let first = c.exe();
12828            let second = c.exe();
12829            assert_eq!(
12830                first, second,
12831                "Caixa::exe must be idempotent — two successive calls \
12832                 on the same &self must return the same &[String]",
12833            );
12834            assert_eq!(
12835                first.as_ptr(),
12836                second.as_ptr(),
12837                "Caixa::exe must borrow the underlying Vec<String> \
12838                 storage — two successive calls must return slices \
12839                 with the same backing pointer (a fresh Vec<String> \
12840                 clone would change the pointer on every call)",
12841            );
12842            assert_eq!(
12843                first,
12844                expected.as_slice(),
12845                "Caixa::exe must return :exe verbatim by borrow — \
12846                 got {first:?}, expected {expected:?}",
12847            );
12848        }
12849    }
12850
12851    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
12852
12853    #[test]
12854    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
12855        // The canonical per-`Caixa` `:servicos` universal-axis
12856        // ComputeUnit-CR-YAML-entry-path-list slice pin:
12857        // [`Caixa::servicos`] must return the `:servicos` typed
12858        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
12859        // the raw `self.servicos.as_slice()` access across every
12860        // representative value in the accept-set — `[]` (the "no
12861        // ComputeUnit-CR declared" arm every `:kind` other than
12862        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
12863        // `ServicoWithoutServicos` arm-gate fires exactly on this
12864        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
12865        // guard sentinel that pins the accessor doesn't perform a
12866        // silent `[""] → []` collapse on the empty-entry arm — validate
12867        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
12868        // but the accessor must ship the raw slot verbatim so a
12869        // validate-time gate regression surfaces at the layout /
12870        // per-Servico renderer boundary rather than being silently
12871        // absorbed into a component-drop),
12872        // `["servicos/demo.computeunit.yaml"]` (the canonical
12873        // singleton V0-shape every in-tree `caixa_with_code_paths`
12874        // positive control uses; the same shape
12875        // [`crate::require_single_servico`] admits),
12876        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
12877        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
12878        // singularity gate rejects through `ServicoCountMismatch
12879        // { count: 2 }` but the accessor must ship the raw slot
12880        // verbatim so struct-literal `Caixa { servicos: vec![...,
12881        // ...], .. }` fixtures continue to expose the count at the
12882        // accessor), and `["servicos/a.computeunit.yaml",
12883        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
12884        // sentinel — validate rejects through
12885        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
12886        // set-not-multiset gate, but the accessor must ship the raw
12887        // slot verbatim so struct-literal fixtures continue to expose
12888        // the duplicate at the accessor).
12889        //
12890        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
12891        // slice accessor pin on the substrate primitive — folds on the
12892        // "outer [`Caixa`] `&[T]` slice" projection pattern
12893        // `autores_returns_autores_slice_verbatim_across_permutations`
12894        // (b5d813f) opened,
12895        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12896        // (78c7d3c) folded on,
12897        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12898        // (8a36c23) closed the universal-axis text-tag family of, and
12899        // `exe_returns_exe_slice_verbatim_across_permutations`
12900        // (65d9527) opened the foreign-code-slot sub-family of. Closes
12901        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
12902        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
12903        // `:servicos`) now each carries a substrate-canonical slice
12904        // accessor. Pins against a future silent detour that returned
12905        // an owned `Vec<String>` (which would type-check but silently
12906        // clone on every accessor call, breaking the zero-cost
12907        // projection every peer sibling slice accessor carries), a
12908        // `[""] → []` collapse (which would silently absorb the
12909        // `CodePathEmpty` refusal case at the accessor boundary), an
12910        // `[a, a] → [a]` dedup collapse (which would silently absorb
12911        // the `CodePathDuplicate` refusal case at the accessor
12912        // boundary — the per-slot set-not-multiset gate is downstream
12913        // of the accessor and must not be silently promoted into it),
12914        // or a `[a, b] → [a]` singleton collapse (which would silently
12915        // absorb the V0 `ServicoCountMismatch` refusal case at the
12916        // accessor boundary — the V0 singularity gate is downstream of
12917        // the accessor and must not be silently promoted into it).
12918        for servicos in [
12919            vec![],
12920            vec![""],
12921            vec!["servicos/demo.computeunit.yaml"],
12922            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
12923            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
12924        ] {
12925            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
12926            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
12927            assert_eq!(
12928                c.servicos(),
12929                expected.as_slice(),
12930                "Caixa::servicos must return :servicos verbatim (got \
12931                 {:?}, expected {expected:?})",
12932                c.servicos(),
12933            );
12934            assert_eq!(
12935                c.servicos(),
12936                c.servicos.as_slice(),
12937                "Caixa::servicos must byte-equal the raw \
12938                 `self.servicos.as_slice()` field access across every \
12939                 value in the Vec<String> accept-set",
12940            );
12941        }
12942    }
12943
12944    #[test]
12945    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
12946        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12947        // empty-arm gate on the `:servicos` slot must key off
12948        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
12949        // field-borrow walk. Structurally: a `Caixa { servicos:
12950        // vec!["".into()], .. }` must surface the `CodePathEmpty
12951        // { slot: ":servicos" }` refusal exactly, and a `Caixa
12952        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
12953        // .. }` (the canonical singleton V0-shape every in-tree
12954        // `caixa_with_code_paths` positive control uses) must pass
12955        // validate. The pair jointly pins the accessor + validate-gate
12956        // composition: any future silent detour that had the accessor
12957        // return an empty slice on the `[""]` arm (a `.iter().filter
12958        // (|s| !s.is_empty()).collect()` collapse) would silently
12959        // absorb the `CodePathEmpty` refusal at the accessor boundary
12960        // and the validate gate would accept a struct-literal
12961        // `Caixa { servicos: vec!["".into()], .. }` — the composition
12962        // pin catches that at caixa-core build time.
12963        //
12964        // Peer of the per-`Caixa`
12965        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12966        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
12967        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
12968        // (b5d813f), and
12969        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12970        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12971        // composition axes — same "the validate / shape-gate predicate
12972        // must route through the substrate-primitive typed dispatch"
12973        // discipline extended onto the sibling outer top-level
12974        // [`Caixa`] `&[T]`-composition surface, closing the trio of
12975        // code-surface accessor-composition pins on the same axis.
12976        // Nominally the in-tree `validate_code_paths` production body
12977        // still keys off the internal
12978        // `[(":bibliotecas", &self.bibliotecas,
12979        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12980        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12981        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
12982        // per-element accessor swap in isolation — a future companion
12983        // lift promotes the tuple's element type to `&[String]` and
12984        // threads the triple of typed dispatches through as a unit);
12985        // the composition pin catches any future accessor-side silent
12986        // filter drop against that eventual tuple-closure regardless
12987        // of whether the `:servicos` slot is threaded through the
12988        // accessor or the raw field access at the tuple's construction
12989        // site.
12990        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
12991        assert!(
12992            matches!(
12993                c.validate_code_paths(),
12994                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
12995            ),
12996            "validate_code_paths must reject servicos == vec![\"\"] \
12997             with CodePathEmpty {{ slot: \":servicos\" }} — the \
12998             accessor and the validate gate must route through the \
12999             same substrate-primitive typed dispatch on the \
13000             :servicos per-entry empty arm",
13001        );
13002        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
13003        assert!(
13004            c.validate_code_paths().is_ok(),
13005            "validate_code_paths must accept servicos == \
13006             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
13007             singleton V0-shape every in-tree `caixa_with_code_paths` \
13008             positive control uses)",
13009        );
13010    }
13011
13012    #[test]
13013    fn servicos_projects_slice_by_borrow() {
13014        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
13015        // borrow — the returned slice borrows the underlying
13016        // `Vec<String>` storage of the `:servicos` slot and the
13017        // accessor must not clone the backing `Vec` on every call.
13018        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13019        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
13020        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
13021        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
13022        // the sibling outer top-level [`Caixa`] `&[String]`-return
13023        // axes — the accessor's returned slice must borrow from
13024        // `&self` (the returned reference's lifetime is tied to
13025        // `&self`), and calling the accessor twice on the same
13026        // [`Caixa`] must yield slices that are pointer-equal (the
13027        // underlying byte-buffer is the storage `Vec`'s allocation,
13028        // not a fresh copy) as well as value-equal (idempotent, no
13029        // side effects on `&self`).
13030        //
13031        // Pins against a future silent detour that returned an owned
13032        // `Vec<String>` (which would type-check but silently clone on
13033        // every call, breaking the zero-cost projection every peer
13034        // sibling slice accessor carries), a `&Vec<String>` return
13035        // (which would leak the backing `Vec`'s grow/push/reserve
13036        // surface no downstream consumer reaches for), or a one-arm-
13037        // only accessor that returned a saturating value on some
13038        // sentinel input (breaking the pass-through invariant the
13039        // sibling slice accessors carry).
13040        for servicos in [
13041            vec![],
13042            vec!["servicos/demo.computeunit.yaml"],
13043            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
13044            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
13045        ] {
13046            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
13047            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
13048            let first = c.servicos();
13049            let second = c.servicos();
13050            assert_eq!(
13051                first, second,
13052                "Caixa::servicos must be idempotent — two successive \
13053                 calls on the same &self must return the same &[String]",
13054            );
13055            assert_eq!(
13056                first.as_ptr(),
13057                second.as_ptr(),
13058                "Caixa::servicos must borrow the underlying \
13059                 Vec<String> storage — two successive calls must \
13060                 return slices with the same backing pointer (a fresh \
13061                 Vec<String> clone would change the pointer on every \
13062                 call)",
13063            );
13064            assert_eq!(
13065                first,
13066                expected.as_slice(),
13067                "Caixa::servicos must return :servicos verbatim by \
13068                 borrow — got {first:?}, expected {expected:?}",
13069            );
13070        }
13071    }
13072
13073    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
13074
13075    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
13076        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13077        c.deps = deps;
13078        c
13079    }
13080
13081    #[test]
13082    fn deps_returns_deps_slice_verbatim_across_permutations() {
13083        // The canonical per-`Caixa` `:deps` universal-axis runtime-
13084        // dependency-declaration-list slice pin: [`Caixa::deps`] must
13085        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
13086        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
13087        // access across every representative value in the accept-set —
13088        // `[]` (the "no runtime deps declared" arm every existing
13089        // fixture without a `:deps` line carries; the
13090        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
13091        // single-entry list (the shape most consumer caixas carry), a
13092        // canonical two-entry list (the multi-dep runtime closure), and
13093        // two past-the-guard sentinels — a `[""]`-`:nome` entry
13094        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
13095        // `NomeInvalid` but the accessor must ship the raw slot
13096        // verbatim) and a `[a, a]` duplicate (validate rejects through
13097        // `DuplicateNome { list: ":deps" }` but the accessor must ship
13098        // the raw slot verbatim so struct-literal fixtures continue to
13099        // expose the duplicate at the accessor).
13100        //
13101        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
13102        // pin on the substrate primitive — opens the outer-`Caixa`
13103        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
13104        // future lift closes on. Peer of the closed outer-`Caixa`
13105        // foreign-code-slot `&[String]` sub-family
13106        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
13107        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
13108        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
13109        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
13110        // (`autores_returns_autores_slice_verbatim_across_permutations`
13111        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13112        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
13113        // projection pattern onto a novel element-type axis (`Dep`
13114        // composite vs the prior sibling family's `String` scalar).
13115        // Pins against a future silent detour that returned an owned
13116        // `Vec<Dep>` (which would type-check but silently clone on every
13117        // accessor call, breaking the zero-cost projection every peer
13118        // sibling slice accessor carries), a `[""] → []` collapse (which
13119        // would silently absorb the `NomeEmpty` refusal case at the
13120        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
13121        // would silently absorb the `DuplicateNome` refusal case at the
13122        // accessor boundary).
13123        for deps in [
13124            vec![],
13125            vec![Dep::simple("", "^0.1")],
13126            vec![Dep::simple("caixa-teia", "^0.1")],
13127            vec![
13128                Dep::simple("caixa-teia", "^0.1"),
13129                Dep::simple("caixa-core", "^0.1"),
13130            ],
13131            vec![
13132                Dep::simple("caixa-teia", "^0.1"),
13133                Dep::simple("caixa-teia", "^0.2"),
13134            ],
13135        ] {
13136            let c = caixa_with_deps(deps.clone());
13137            assert_eq!(
13138                c.deps(),
13139                deps.as_slice(),
13140                "Caixa::deps must return :deps verbatim (got {:?}, \
13141                 expected {deps:?})",
13142                c.deps(),
13143            );
13144            assert_eq!(
13145                c.deps(),
13146                c.deps.as_slice(),
13147                "Caixa::deps must element-equal the raw \
13148                 `self.deps.as_slice()` field access across every \
13149                 value in the Vec<Dep> accept-set",
13150            );
13151        }
13152    }
13153
13154    #[test]
13155    fn validate_deps_duplicate_arm_routes_through_accessor() {
13156        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
13157        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
13158        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
13159        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
13160        // "^0.2")], .. }` must surface the `DuplicateNome { list:
13161        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
13162        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
13163        // form) must pass validate. The pair jointly pins the accessor +
13164        // validate-gate composition: any future silent detour that had
13165        // the accessor return a dedupped slice on the `[a, a]` arm (a
13166        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
13167        // would silently absorb the `DuplicateNome` refusal at the
13168        // accessor boundary and the validate gate would accept a
13169        // struct-literal `Caixa` carrying the drift — the composition
13170        // pin catches that at caixa-core build time.
13171        //
13172        // Peer of the per-`Caixa`
13173        // `validate_autores_empty_entry_arm_routes_through_accessor`
13174        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13175        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
13176        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
13177        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
13178        // (611f78b) accessor-composition pins on the sibling `&[T]`-
13179        // composition axes — same "the validate gate must route through
13180        // the substrate-primitive typed dispatch" discipline extended
13181        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
13182        // composition surface, opening the outer-`Caixa` dependency-slot
13183        // arm of the composition-pin family.
13184        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
13185        let err = c.validate_deps().unwrap_err();
13186        assert!(
13187            matches!(
13188                err,
13189                DepError::DuplicateNome { ref nome, list } if nome == "d"
13190                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
13191            ),
13192            "validate_deps must reject deps == \
13193             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
13194             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
13195             accessor and the validate gate must route through the \
13196             same substrate-primitive typed dispatch on the :deps \
13197             within-list duplicate arm (got {err:?})",
13198        );
13199        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
13200        assert!(
13201            c.validate_deps().is_ok(),
13202            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
13203             (the canonical single-entry form)",
13204        );
13205    }
13206
13207    #[test]
13208    fn deps_projects_slice_by_borrow() {
13209        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
13210        // — the returned slice borrows the underlying `Vec<Dep>` storage
13211        // of the `:deps` slot and the accessor must not clone the
13212        // backing `Vec` on every call. Peer of the per-`Caixa`
13213        // `autores_projects_slice_by_borrow` (b5d813f),
13214        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
13215        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
13216        // `exe_projects_slice_by_borrow` (65d9527), and
13217        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
13218        // on the sibling outer top-level [`Caixa`] `&[String]`-return
13219        // axes — the accessor's returned slice must borrow from `&self`
13220        // (the returned reference's lifetime is tied to `&self`), and
13221        // calling the accessor twice on the same [`Caixa`] must yield
13222        // slices that are pointer-equal (the underlying byte-buffer is
13223        // the storage `Vec`'s allocation, not a fresh copy) as well as
13224        // value-equal (idempotent, no side effects on `&self`).
13225        //
13226        // Pins against a future silent detour that returned an owned
13227        // `Vec<Dep>` (which would type-check but silently clone on
13228        // every call), a `&Vec<Dep>` return (which would leak the
13229        // backing `Vec`'s grow/push/reserve surface no downstream
13230        // consumer reaches for), or a one-arm-only accessor that
13231        // returned a saturating value on some sentinel input.
13232        for deps in [
13233            vec![],
13234            vec![Dep::simple("caixa-teia", "^0.1")],
13235            vec![
13236                Dep::simple("caixa-teia", "^0.1"),
13237                Dep::simple("caixa-core", "^0.1"),
13238            ],
13239        ] {
13240            let c = caixa_with_deps(deps.clone());
13241            let first = c.deps();
13242            let second = c.deps();
13243            assert_eq!(
13244                first, second,
13245                "Caixa::deps must be idempotent — two successive calls \
13246                 on the same &self must return the same &[Dep]",
13247            );
13248            assert_eq!(
13249                first.as_ptr(),
13250                second.as_ptr(),
13251                "Caixa::deps must borrow the underlying Vec<Dep> \
13252                 storage — two successive calls must return slices \
13253                 with the same backing pointer (a fresh Vec<Dep> clone \
13254                 would change the pointer on every call)",
13255            );
13256            assert_eq!(
13257                first,
13258                deps.as_slice(),
13259                "Caixa::deps must return :deps verbatim by borrow — \
13260                 got {first:?}, expected {deps:?}",
13261            );
13262        }
13263    }
13264
13265    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
13266
13267    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
13268        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13269        c.deps_dev = deps_dev;
13270        c
13271    }
13272
13273    #[test]
13274    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
13275        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
13276        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
13277        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
13278        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
13279        // access across every representative value in the accept-set —
13280        // `[]` (the "no dev deps declared" arm every existing fixture
13281        // without a `:deps-dev` line carries; the [`Caixa::template`]
13282        // scaffold emits `:deps-dev ()`), a canonical single-entry list
13283        // (the shape most consumer caixas carry — a `tatara-check` dev
13284        // pin), a canonical two-entry list (the multi-dev-dep closure),
13285        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
13286        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
13287        // `NomeInvalid` but the accessor must ship the raw slot
13288        // verbatim) and a `[a, a]` duplicate (validate rejects through
13289        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
13290        // ship the raw slot verbatim so struct-literal fixtures continue
13291        // to expose the duplicate at the accessor).
13292        //
13293        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
13294        // pin on the substrate primitive — closes the outer-`Caixa`
13295        // dependency-slot `&[Dep]` sub-family the sibling
13296        // `deps_returns_deps_slice_verbatim_across_permutations`
13297        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
13298        // slice" projection pattern onto the sibling dev-dep axis —
13299        // pins against a future silent detour that returned an owned
13300        // `Vec<Dep>` (which would type-check but silently clone on every
13301        // accessor call, breaking the zero-cost projection every peer
13302        // sibling slice accessor carries), a `[""] → []` collapse (which
13303        // would silently absorb the `NomeEmpty` refusal case at the
13304        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
13305        // would silently absorb the `DuplicateNome` refusal case at the
13306        // accessor boundary).
13307        for deps_dev in [
13308            vec![],
13309            vec![Dep::simple("", "^0.1")],
13310            vec![Dep::simple("tatara-check", "^0.1")],
13311            vec![
13312                Dep::simple("tatara-check", "^0.1"),
13313                Dep::simple("caixa-lint", "^0.1"),
13314            ],
13315            vec![
13316                Dep::simple("tatara-check", "^0.1"),
13317                Dep::simple("tatara-check", "^0.2"),
13318            ],
13319        ] {
13320            let c = caixa_with_deps_dev(deps_dev.clone());
13321            assert_eq!(
13322                c.deps_dev(),
13323                deps_dev.as_slice(),
13324                "Caixa::deps_dev must return :deps-dev verbatim (got \
13325                 {:?}, expected {deps_dev:?})",
13326                c.deps_dev(),
13327            );
13328            assert_eq!(
13329                c.deps_dev(),
13330                c.deps_dev.as_slice(),
13331                "Caixa::deps_dev must element-equal the raw \
13332                 `self.deps_dev.as_slice()` field access across every \
13333                 value in the Vec<Dep> accept-set",
13334            );
13335        }
13336    }
13337
13338    #[test]
13339    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
13340        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
13341        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
13342        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
13343        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
13344        // Dep::simple("d", "^0.2")], .. }` must surface the
13345        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
13346        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
13347        // canonical single-entry form) must pass validate. The pair
13348        // jointly pins the accessor + validate-gate composition: any
13349        // future silent detour that had the accessor return a dedupped
13350        // slice on the `[a, a]` arm (a
13351        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
13352        // would silently absorb the `DuplicateNome` refusal at the
13353        // accessor boundary and the validate gate would accept a
13354        // struct-literal `Caixa` carrying the drift — the composition
13355        // pin catches that at caixa-core build time.
13356        //
13357        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
13358        // (ad34b4e) on the sibling `:deps` axis — same "the validate
13359        // gate must route through the substrate-primitive typed
13360        // dispatch" discipline folded onto the sibling `:deps-dev`
13361        // axis, closing the two-list dep-graph composition-pin family.
13362        // The `:deps-dev` diagnostic must carry the
13363        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
13364        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
13365        // offending list unambiguously.
13366        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
13367        let err = c.validate_deps().unwrap_err();
13368        assert!(
13369            matches!(
13370                err,
13371                DepError::DuplicateNome { ref nome, list } if nome == "d"
13372                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
13373            ),
13374            "validate_deps must reject deps_dev == \
13375             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
13376             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
13377             accessor and the validate gate must route through the \
13378             same substrate-primitive typed dispatch on the :deps-dev \
13379             within-list duplicate arm (got {err:?})",
13380        );
13381        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
13382        assert!(
13383            c.validate_deps().is_ok(),
13384            "validate_deps must accept deps_dev == \
13385             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
13386        );
13387    }
13388
13389    #[test]
13390    fn deps_dev_projects_slice_by_borrow() {
13391        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
13392        // borrow — the returned slice borrows the underlying `Vec<Dep>`
13393        // storage of the `:deps-dev` slot and the accessor must not
13394        // clone the backing `Vec` on every call. Peer of
13395        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
13396        // `:deps` axis, and of the per-`Caixa`
13397        // `autores_projects_slice_by_borrow` (b5d813f),
13398        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
13399        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
13400        // `exe_projects_slice_by_borrow` (65d9527), and
13401        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
13402        // on the sibling outer top-level [`Caixa`] `&[String]`-return
13403        // axes — the accessor's returned slice must borrow from `&self`
13404        // (the returned reference's lifetime is tied to `&self`), and
13405        // calling the accessor twice on the same [`Caixa`] must yield
13406        // slices that are pointer-equal (the underlying byte-buffer is
13407        // the storage `Vec`'s allocation, not a fresh copy) as well as
13408        // value-equal (idempotent, no side effects on `&self`).
13409        //
13410        // Pins against a future silent detour that returned an owned
13411        // `Vec<Dep>` (which would type-check but silently clone on
13412        // every call), a `&Vec<Dep>` return (which would leak the
13413        // backing `Vec`'s grow/push/reserve surface no downstream
13414        // consumer reaches for), or a one-arm-only accessor that
13415        // returned a saturating value on some sentinel input.
13416        for deps_dev in [
13417            vec![],
13418            vec![Dep::simple("tatara-check", "^0.1")],
13419            vec![
13420                Dep::simple("tatara-check", "^0.1"),
13421                Dep::simple("caixa-lint", "^0.1"),
13422            ],
13423        ] {
13424            let c = caixa_with_deps_dev(deps_dev.clone());
13425            let first = c.deps_dev();
13426            let second = c.deps_dev();
13427            assert_eq!(
13428                first, second,
13429                "Caixa::deps_dev must be idempotent — two successive \
13430                 calls on the same &self must return the same &[Dep]",
13431            );
13432            assert_eq!(
13433                first.as_ptr(),
13434                second.as_ptr(),
13435                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
13436                 storage — two successive calls must return slices \
13437                 with the same backing pointer (a fresh Vec<Dep> clone \
13438                 would change the pointer on every call)",
13439            );
13440            assert_eq!(
13441                first,
13442                deps_dev.as_slice(),
13443                "Caixa::deps_dev must return :deps-dev verbatim by \
13444                 borrow — got {first:?}, expected {deps_dev:?}",
13445            );
13446        }
13447    }
13448
13449    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
13450
13451    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
13452        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13453        c.limits = limits;
13454        c
13455    }
13456
13457    #[test]
13458    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
13459        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
13460        // composite optional-composite-reference-shape pin:
13461        // [`Caixa::limits`] must return the `:limits` typed
13462        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
13463        // reference over the same backing storage the raw
13464        // `self.limits.as_ref()` field access borrows from, byte-equal
13465        // across every representative fixture in the accept-set — the
13466        // author-omitted `None` shape (the "engine-default applies"
13467        // partition every downstream Servico M2 overlay emitter treats
13468        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
13469        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
13470        // per-axis cap is `None`, so the peer M2 overlay emitter's
13471        // `.is_empty()`-gated projection still emits nothing but the
13472        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
13473        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
13474        // fixture (only `:memory` set — the canonical shape most
13475        // memory-heavy Servicos carry), and a fully-populated composite
13476        // (every per-axis cap set — the canonical shape a
13477        // sandboxed-by-default Servico carries).
13478        //
13479        // Pins against a future silent detour that returned a fresh-
13480        // cloned [`LimitsSpec`] copy (which would type-check via the
13481        // `Clone` impl but silently break every downstream caller that
13482        // relied on the reference sharing the composite's backing
13483        // identity), a reference to an operator-resolved overlay (the
13484        // future per-cluster `:limits-overrides` slot — its resolution
13485        // must land at exactly this accessor body, not silently divert
13486        // the raw slot away from a second consumer), a
13487        // `None` → `Some(LimitsSpec::default)` cluster-default
13488        // projection (which would collapse the load-bearing
13489        // "author-omitted `:limits` ⇒ engine-default applies" partition
13490        // the peer [`crate::render::servico_m2_overlay`] emitter and
13491        // the peer [`Caixa::declared_servico_slots`] enumerator both
13492        // read), or an axis-shuffled projection (a future detour that
13493        // swapped `memory` and `fuel` through the accessor would
13494        // silently split the paired [`crate::StandardLayout::verify`]
13495        // per-`:limits` shape gate's traversal input from the peer
13496        // `servico_m2_overlay` emitter's projection input).
13497        //
13498        // First outer top-level [`Caixa`] `Option<&Composite>`-return
13499        // composite-reference accessor pin on the substrate primitive
13500        // — opens the outer-`Caixa` `Option<&Composite>` composite-
13501        // reference projection pattern the sibling `:behavior`
13502        // [`crate::BehaviorSpec`] / `:politicas`
13503        // [`crate::aplicacao::MeshPolicy`] / `:placement`
13504        // [`crate::aplicacao::Placement`] / `:entrada`
13505        // [`crate::aplicacao::Entrada`] future outer-composite lifts
13506        // fold on. Peer of the closed M3 outer-composite family the
13507        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
13508        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
13509        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
13510        // reference accessor pins already carry on the outer
13511        // [`crate::AplicacaoSpec`] altitude — extends the outer-
13512        // accessor byte-equal-projection discipline onto the outer
13513        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
13514        use crate::LimitsSpec;
13515        use std::time::Duration;
13516        let fixtures: Vec<Option<LimitsSpec>> = vec![
13517            None,
13518            Some(LimitsSpec::default()),
13519            Some(LimitsSpec {
13520                memory: Some(64 * 1024 * 1024),
13521                ..Default::default()
13522            }),
13523            Some(LimitsSpec {
13524                memory: Some(64 * 1024 * 1024),
13525                fuel: Some(1_000_000),
13526                wall_clock: Some(Duration::from_secs(30)),
13527                cpu: Some(500),
13528            }),
13529        ];
13530        for limits in fixtures {
13531            let c = caixa_with_limits(limits.clone());
13532            assert_eq!(
13533                c.limits(),
13534                limits.as_ref(),
13535                "Caixa::limits must return :limits verbatim (got {:?}, \
13536                 expected {:?})",
13537                c.limits(),
13538                limits.as_ref(),
13539            );
13540            match (c.limits(), c.limits.as_ref()) {
13541                (Some(a), Some(b)) => assert!(
13542                    std::ptr::eq(a, b),
13543                    "Caixa::limits accessor and self.limits.as_ref() \
13544                     field access must borrow the same backing storage \
13545                     — the accessor is the substrate-primitive typed \
13546                     dispatch every downstream Servico-M2-overlay \
13547                     composite consumer must route through, and a \
13548                     reference-identity split would silently break \
13549                     every consumer that relied on the borrow sharing \
13550                     the composite's storage",
13551                ),
13552                (None, None) => {}
13553                _ => panic!(
13554                    "Caixa::limits presence bit must byte-equal \
13555                     self.limits.is_some() — a presence-bit drift would \
13556                     silently split the paired StandardLayout::verify \
13557                     per-`:limits` shape gate's traversal head from \
13558                     the peer render::servico_m2_overlay M2 overlay \
13559                     emitter's traversal head from the peer \
13560                     Caixa::declared_servico_slots M2 declared-slot \
13561                     enumerator's presence probe",
13562                ),
13563            }
13564            assert_eq!(
13565                c.limits().is_some(),
13566                c.limits.is_some(),
13567                "Caixa::limits().is_some() must byte-equal \
13568                 self.limits.is_some() — a presence-bit drift would \
13569                 silently split every downstream Option<&LimitsSpec> \
13570                 consumer's partition on the engine-default arm",
13571            );
13572        }
13573    }
13574
13575    #[test]
13576    fn declared_servico_slots_limits_arm_routes_through_accessor() {
13577        // Composition pin: [`Caixa::declared_servico_slots`]'s
13578        // `:limits` presence-probe arm must key off [`Caixa::limits`],
13579        // not the raw `self.limits.is_some()` field-probe. Structurally:
13580        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
13581        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
13582        // (the presence bit is `Some`, so the M2 kind-coherence gate
13583        // must surface the slot as "declared" even when every per-axis
13584        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
13585        // push the label (the "author omitted the slot entirely"
13586        // partition). The pair jointly pins the accessor + declared-
13587        // slot enumerator composition: any future silent detour that
13588        // had the accessor collapse `Some(LimitsSpec::default())` to
13589        // `None` (a `.filter(|l| !l.is_empty())` projection) would
13590        // silently absorb the "declared but empty" arm at the
13591        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
13592        // kind-coherence gate would silently accept a
13593        // struct-literal `Caixa` carrying the drift.
13594        //
13595        // Peer of the sibling per-`Caixa`
13596        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
13597        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
13598        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
13599        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
13600        // enumerator gate must route through the substrate-primitive
13601        // typed dispatch" discipline extended onto the outer top-level
13602        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
13603        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
13604        // composition-pin family.
13605        use crate::LimitsSpec;
13606        let c = caixa_with_limits(Some(LimitsSpec::default()));
13607        let slots = c.declared_servico_slots();
13608        assert!(
13609            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
13610            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
13611             when `:limits` is Some (even for LimitsSpec::default()) \
13612             — the accessor and the enumerator gate must route through \
13613             the same substrate-primitive typed dispatch on the outer \
13614             :limits presence bit (got slots={slots:?})",
13615        );
13616        let c = caixa_with_limits(None);
13617        let slots = c.declared_servico_slots();
13618        assert!(
13619            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
13620            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
13621             when `:limits` is None — the author-omitted arm must \
13622             route through the accessor's None-return unchanged (got \
13623             slots={slots:?})",
13624        );
13625    }
13626
13627    #[test]
13628    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
13629        // Composition pin: [`crate::render::servico_m2_overlay`]'s
13630        // per-`:limits` M2 overlay emit arm must key off
13631        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
13632        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
13633        // Some(64 MiB), .. default }), .. }` must surface the
13634        // `M2_KEY_LIMITS` key with the per-axis
13635        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
13636        // limits: Some(LimitsSpec::default()), .. }` must omit the
13637        // key entirely (the `.is_empty()`-gated inner arm elides an
13638        // empty composite even when the outer presence bit is `Some`),
13639        // and a `Caixa { limits: None, .. }` must also omit the key
13640        // (the "author omitted the slot entirely" partition). The
13641        // three-fixture family jointly pins the accessor + M2 overlay
13642        // emitter composition: any future silent detour that had the
13643        // accessor return a fresh-cloned copy on the `Some` arm (a
13644        // `LimitsSpec::clone()` projection) would silently break the
13645        // reference-identity pin the peer per-axis
13646        // `serde_yaml::to_value(limits)` projection reads from.
13647        use crate::LimitsSpec;
13648        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
13649        let c = caixa_with_limits(Some(LimitsSpec {
13650            memory: Some(64 * 1024 * 1024),
13651            ..Default::default()
13652        }));
13653        let overlay = servico_m2_overlay(&c).unwrap();
13654        assert!(
13655            overlay.contains_key(M2_KEY_LIMITS),
13656            "servico_m2_overlay must surface M2_KEY_LIMITS when \
13657             `:limits` carries a non-empty composite — the accessor \
13658             and the M2 overlay emitter must route through the same \
13659             substrate-primitive typed dispatch on the outer :limits \
13660             composite (got overlay={overlay:?})",
13661        );
13662        let c = caixa_with_limits(Some(LimitsSpec::default()));
13663        let overlay = servico_m2_overlay(&c).unwrap();
13664        assert!(
13665            !overlay.contains_key(M2_KEY_LIMITS),
13666            "servico_m2_overlay must omit M2_KEY_LIMITS when \
13667             `:limits` is Some(LimitsSpec::default()) — the empty \
13668             composite's `.is_empty()`-gated inner arm must elide \
13669             the key regardless of the outer presence bit (got \
13670             overlay={overlay:?})",
13671        );
13672        let c = caixa_with_limits(None);
13673        let overlay = servico_m2_overlay(&c).unwrap();
13674        assert!(
13675            !overlay.contains_key(M2_KEY_LIMITS),
13676            "servico_m2_overlay must omit M2_KEY_LIMITS when \
13677             `:limits` is None — the author-omitted arm must route \
13678             through the accessor's None-return unchanged (got \
13679             overlay={overlay:?})",
13680        );
13681    }
13682
13683    #[test]
13684    fn limits_projects_option_ref_by_borrow() {
13685        // The by-borrow pin: [`Caixa::limits`] returns
13686        // `Option<&LimitsSpec>` by borrow — the returned reference
13687        // borrows the underlying `Option<LimitsSpec>` storage of the
13688        // `:limits` slot and the accessor must not clone the backing
13689        // composite on every call. Peer of the sibling
13690        // `deps_projects_slice_by_borrow` (ad34b4e) /
13691        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
13692        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
13693        // extended here to the outer [`Caixa`] `Option<&Composite>`-
13694        // return axis: the accessor's returned reference must borrow
13695        // from `&self` (the returned reference's lifetime is tied to
13696        // `&self`), and calling the accessor twice on the same
13697        // [`Caixa`] must yield references that are pointer-equal (the
13698        // underlying byte-buffer is the storage `LimitsSpec`'s
13699        // allocation, not a fresh copy) as well as value-equal
13700        // (idempotent, no side effects on `&self`).
13701        //
13702        // Pins against a future silent detour that returned an owned
13703        // `LimitsSpec` (which would type-check via the `Clone` impl
13704        // but silently clone on every call), a `&LimitsSpec` panic-
13705        // return on the `None` arm (which would collapse the load-
13706        // bearing `Option` presence-bit into a runtime panic), or a
13707        // one-arm-only accessor that returned a saturating composite
13708        // on some sentinel input.
13709        use crate::LimitsSpec;
13710        use std::time::Duration;
13711        for limits in [
13712            Some(LimitsSpec::default()),
13713            Some(LimitsSpec {
13714                memory: Some(64 * 1024 * 1024),
13715                fuel: Some(1_000_000),
13716                wall_clock: Some(Duration::from_secs(30)),
13717                cpu: Some(500),
13718            }),
13719        ] {
13720            let c = caixa_with_limits(limits.clone());
13721            let first = c.limits().unwrap();
13722            let second = c.limits().unwrap();
13723            assert_eq!(
13724                first, second,
13725                "Caixa::limits must be idempotent — two successive \
13726                 calls on the same &self must return the same \
13727                 &LimitsSpec",
13728            );
13729            assert!(
13730                std::ptr::eq(first, second),
13731                "Caixa::limits must borrow the underlying \
13732                 Option<LimitsSpec> storage — two successive calls \
13733                 must return references with the same backing pointer \
13734                 (a fresh LimitsSpec clone would change the pointer \
13735                 on every call)",
13736            );
13737            assert_eq!(
13738                Some(first),
13739                limits.as_ref(),
13740                "Caixa::limits must return :limits verbatim by borrow \
13741                 — got {first:?}, expected {:?}",
13742                limits.as_ref(),
13743            );
13744        }
13745        let c = caixa_with_limits(None);
13746        assert!(
13747            c.limits().is_none(),
13748            "Caixa::limits must return None when :limits is absent — \
13749             the author-omitted arm must project through the \
13750             accessor's Option::None unchanged",
13751        );
13752    }
13753
13754    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
13755
13756    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
13757        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13758        c.behavior = behavior;
13759        c
13760    }
13761
13762    #[test]
13763    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
13764        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
13765        // composite optional-composite-reference-shape pin:
13766        // [`Caixa::behavior`] must return the `:behavior` typed
13767        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
13768        // reference over the same backing storage the raw
13769        // `self.behavior.as_ref()` field access borrows from, byte-equal
13770        // across every representative fixture in the accept-set — the
13771        // author-omitted `None` shape (the "runtime-default applies"
13772        // partition every downstream Servico M2 overlay emitter treats
13773        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
13774        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
13775        // every per-callback path is `None`, so the peer M2 overlay
13776        // emitter's `.is_empty()`-gated projection still emits nothing
13777        // but the outer presence-bit is `Some`, so
13778        // [`Caixa::declared_servico_slots`] still pushes the
13779        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
13780        // (only `:on-state-change` set — the canonical shape a caixa
13781        // that only wires the hot-upgrade migration path carries), and
13782        // a fully-populated composite (every per-callback path set —
13783        // the canonical shape a fully-instrumented gen_server-shaped
13784        // Servico carries).
13785        //
13786        // Peer of the sibling
13787        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13788        // (b2bd9d7) opening fixture-family + reference-identity +
13789        // presence-bit tetrad pin on the outer top-level [`Caixa`]
13790        // `Option<&Composite>`-return sub-family — extended here to the
13791        // second axis of that sub-family so both of the currently-lifted
13792        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
13793        // `:behavior`) carry the same "byte-equal, borrow-shared,
13794        // presence-bit-preserved" outer-accessor discipline.
13795        //
13796        // Pins against a future silent detour that returned a fresh-
13797        // cloned [`crate::BehaviorSpec`] copy (which would type-check
13798        // via the `Clone` impl but silently break every downstream
13799        // caller that relied on the reference sharing the composite's
13800        // backing identity), a reference to an operator-resolved
13801        // overlay (a future per-cluster `:behavior-overrides` slot —
13802        // its resolution must land at exactly this accessor body, not
13803        // silently divert the raw slot away from a second consumer), a
13804        // `None` → `Some(BehaviorSpec::default)` cluster-default
13805        // projection (which would collapse the load-bearing
13806        // "author-omitted `:behavior` ⇒ runtime-default applies"
13807        // partition the peer [`crate::render::servico_m2_overlay`]
13808        // emitter, the peer [`Caixa::declared_servico_slots`]
13809        // enumerator, and the cross-slot
13810        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
13811        // gate all read), or a callback-shuffled projection (a future
13812        // detour that swapped `on_init` and `on_terminate` through the
13813        // accessor would silently split the paired
13814        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
13815        // traversal input from the peer `servico_m2_overlay` emitter's
13816        // projection input from the cross-slot `:state-change`
13817        // composition gate's traversal input).
13818        use crate::BehaviorSpec;
13819        use std::path::PathBuf;
13820        let fixtures: Vec<Option<BehaviorSpec>> = vec![
13821            None,
13822            Some(BehaviorSpec::default()),
13823            Some(BehaviorSpec {
13824                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13825                ..Default::default()
13826            }),
13827            Some(BehaviorSpec {
13828                on_init: Some(PathBuf::from("lib/init.lisp")),
13829                on_call: Some(PathBuf::from("lib/handlers.lisp")),
13830                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
13831                on_info: Some(PathBuf::from("lib/handlers.lisp")),
13832                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13833                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
13834            }),
13835        ];
13836        for behavior in fixtures {
13837            let c = caixa_with_behavior(behavior.clone());
13838            assert_eq!(
13839                c.behavior(),
13840                behavior.as_ref(),
13841                "Caixa::behavior must return :behavior verbatim (got \
13842                 {:?}, expected {:?})",
13843                c.behavior(),
13844                behavior.as_ref(),
13845            );
13846            match (c.behavior(), c.behavior.as_ref()) {
13847                (Some(a), Some(b)) => assert!(
13848                    std::ptr::eq(a, b),
13849                    "Caixa::behavior accessor and self.behavior.as_ref() \
13850                     field access must borrow the same backing storage \
13851                     — the accessor is the substrate-primitive typed \
13852                     dispatch every downstream Servico-M2-overlay \
13853                     composite consumer must route through, and a \
13854                     reference-identity split would silently break \
13855                     every consumer that relied on the borrow sharing \
13856                     the composite's storage",
13857                ),
13858                (None, None) => {}
13859                _ => panic!(
13860                    "Caixa::behavior presence bit must byte-equal \
13861                     self.behavior.is_some() — a presence-bit drift \
13862                     would silently split the paired \
13863                     StandardLayout::verify per-`:behavior` shape \
13864                     gate's traversal head from the peer \
13865                     render::servico_m2_overlay M2 overlay emitter's \
13866                     traversal head from the cross-slot \
13867                     validate_upgrade_from_against_behavior \
13868                     composition gate's traversal head from the peer \
13869                     Caixa::declared_servico_slots M2 declared-slot \
13870                     enumerator's presence probe",
13871                ),
13872            }
13873            assert_eq!(
13874                c.behavior().is_some(),
13875                c.behavior.is_some(),
13876                "Caixa::behavior().is_some() must byte-equal \
13877                 self.behavior.is_some() — a presence-bit drift would \
13878                 silently split every downstream Option<&BehaviorSpec> \
13879                 consumer's partition on the runtime-default arm",
13880            );
13881        }
13882    }
13883
13884    #[test]
13885    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
13886        // Composition pin: [`Caixa::declared_servico_slots`]'s
13887        // `:behavior` presence-probe arm must key off
13888        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
13889        // field-probe. Structurally: a `Caixa { behavior:
13890        // Some(BehaviorSpec::default()), .. }` must still push
13891        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
13892        // presence bit is `Some`, so the M2 kind-coherence gate must
13893        // surface the slot as "declared" even when every per-callback
13894        // path is unset), and a `Caixa { behavior: None, .. }` must
13895        // NOT push the label (the "author omitted the slot entirely"
13896        // partition). The pair jointly pins the accessor + declared-
13897        // slot enumerator composition: any future silent detour that
13898        // had the accessor collapse `Some(BehaviorSpec::default())`
13899        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
13900        // silently absorb the "declared but empty" arm at the
13901        // accessor boundary and the
13902        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
13903        // kind-coherence gate would silently accept a struct-literal
13904        // `Caixa` carrying the drift.
13905        //
13906        // Peer of the sibling
13907        // `declared_servico_slots_limits_arm_routes_through_accessor`
13908        // (b2bd9d7) composition pin on the sibling `:limits` outer-
13909        // `Option<&LimitsSpec>` arm of the same
13910        // [`Caixa::declared_servico_slots`] M2 declared-slot
13911        // enumerator's traversal — same "the enumerator gate must
13912        // route through the substrate-primitive typed dispatch"
13913        // discipline extended onto the outer top-level [`Caixa`]
13914        // `Option<&BehaviorSpec>`-composition surface.
13915        use crate::BehaviorSpec;
13916        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
13917        let slots = c.declared_servico_slots();
13918        assert!(
13919            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
13920            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
13921             when `:behavior` is Some (even for BehaviorSpec::default()) \
13922             — the accessor and the enumerator gate must route through \
13923             the same substrate-primitive typed dispatch on the outer \
13924             :behavior presence bit (got slots={slots:?})",
13925        );
13926        let c = caixa_with_behavior(None);
13927        let slots = c.declared_servico_slots();
13928        assert!(
13929            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
13930            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
13931             when `:behavior` is None — the author-omitted arm must \
13932             route through the accessor's None-return unchanged (got \
13933             slots={slots:?})",
13934        );
13935    }
13936
13937    #[test]
13938    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
13939        // Composition pin: [`crate::render::servico_m2_overlay`]'s
13940        // per-`:behavior` M2 overlay emit arm must key off
13941        // [`Caixa::behavior`], not the raw `&caixa.behavior`
13942        // field-borrow. Structurally: a `Caixa { behavior:
13943        // Some(BehaviorSpec { on_state_change: Some(...), .. default
13944        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
13945        // per-callback `onStateChange` sub-mapping in the overlay, a
13946        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
13947        // must omit the key entirely (the `.is_empty()`-gated inner
13948        // arm elides an empty composite even when the outer presence
13949        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
13950        // also omit the key (the "author omitted the slot entirely"
13951        // partition). The three-fixture family jointly pins the
13952        // accessor + M2 overlay emitter composition: any future
13953        // silent detour that had the accessor return a fresh-cloned
13954        // copy on the `Some` arm (a `BehaviorSpec::clone()`
13955        // projection) would silently break the reference-identity
13956        // pin the peer per-callback `serde_yaml::to_value(behavior)`
13957        // projection reads from.
13958        //
13959        // Peer of the sibling
13960        // `servico_m2_overlay_limits_arm_routes_through_accessor`
13961        // (b2bd9d7) composition pin on the sibling `:limits` outer-
13962        // `Option<&LimitsSpec>` arm of the same
13963        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
13964        // traversal — same "the emitter must route through the
13965        // substrate-primitive typed dispatch on the outer composite"
13966        // discipline extended onto the outer top-level [`Caixa`]
13967        // `Option<&BehaviorSpec>`-composition surface.
13968        use crate::BehaviorSpec;
13969        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
13970        use std::path::PathBuf;
13971        let c = caixa_with_behavior(Some(BehaviorSpec {
13972            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13973            ..Default::default()
13974        }));
13975        let overlay = servico_m2_overlay(&c).unwrap();
13976        assert!(
13977            overlay.contains_key(M2_KEY_BEHAVIOR),
13978            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
13979             `:behavior` carries a non-empty composite — the accessor \
13980             and the M2 overlay emitter must route through the same \
13981             substrate-primitive typed dispatch on the outer :behavior \
13982             composite (got overlay={overlay:?})",
13983        );
13984        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
13985        let overlay = servico_m2_overlay(&c).unwrap();
13986        assert!(
13987            !overlay.contains_key(M2_KEY_BEHAVIOR),
13988            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
13989             `:behavior` is Some(BehaviorSpec::default()) — the empty \
13990             composite's `.is_empty()`-gated inner arm must elide the \
13991             key regardless of the outer presence bit (got \
13992             overlay={overlay:?})",
13993        );
13994        let c = caixa_with_behavior(None);
13995        let overlay = servico_m2_overlay(&c).unwrap();
13996        assert!(
13997            !overlay.contains_key(M2_KEY_BEHAVIOR),
13998            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
13999             `:behavior` is None — the author-omitted arm must route \
14000             through the accessor's None-return unchanged (got \
14001             overlay={overlay:?})",
14002        );
14003    }
14004
14005    #[test]
14006    fn behavior_projects_option_ref_by_borrow() {
14007        // The by-borrow pin: [`Caixa::behavior`] returns
14008        // `Option<&BehaviorSpec>` by borrow — the returned reference
14009        // borrows the underlying `Option<BehaviorSpec>` storage of the
14010        // `:behavior` slot and the accessor must not clone the backing
14011        // composite on every call. Peer of the sibling
14012        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
14013        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
14014        // return sub-family — extended here to the second axis of the
14015        // same sub-family: the accessor's returned reference must
14016        // borrow from `&self` (the returned reference's lifetime is
14017        // tied to `&self`), and calling the accessor twice on the same
14018        // [`Caixa`] must yield references that are pointer-equal (the
14019        // underlying byte-buffer is the storage `BehaviorSpec`'s
14020        // allocation, not a fresh copy) as well as value-equal
14021        // (idempotent, no side effects on `&self`).
14022        //
14023        // Pins against a future silent detour that returned an owned
14024        // `BehaviorSpec` (which would type-check via the `Clone` impl
14025        // but silently clone on every call), a `&BehaviorSpec` panic-
14026        // return on the `None` arm (which would collapse the load-
14027        // bearing `Option` presence-bit into a runtime panic), or a
14028        // one-arm-only accessor that returned a saturating composite
14029        // on some sentinel input.
14030        use crate::BehaviorSpec;
14031        use std::path::PathBuf;
14032        for behavior in [
14033            Some(BehaviorSpec::default()),
14034            Some(BehaviorSpec {
14035                on_init: Some(PathBuf::from("lib/init.lisp")),
14036                on_call: Some(PathBuf::from("lib/handlers.lisp")),
14037                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
14038                on_info: Some(PathBuf::from("lib/handlers.lisp")),
14039                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14040                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
14041            }),
14042        ] {
14043            let c = caixa_with_behavior(behavior.clone());
14044            let first = c.behavior().unwrap();
14045            let second = c.behavior().unwrap();
14046            assert_eq!(
14047                first, second,
14048                "Caixa::behavior must be idempotent — two successive \
14049                 calls on the same &self must return the same \
14050                 &BehaviorSpec",
14051            );
14052            assert!(
14053                std::ptr::eq(first, second),
14054                "Caixa::behavior must borrow the underlying \
14055                 Option<BehaviorSpec> storage — two successive calls \
14056                 must return references with the same backing pointer \
14057                 (a fresh BehaviorSpec clone would change the pointer \
14058                 on every call)",
14059            );
14060            assert_eq!(
14061                Some(first),
14062                behavior.as_ref(),
14063                "Caixa::behavior must return :behavior verbatim by \
14064                 borrow — got {first:?}, expected {:?}",
14065                behavior.as_ref(),
14066            );
14067        }
14068        let c = caixa_with_behavior(None);
14069        assert!(
14070            c.behavior().is_none(),
14071            "Caixa::behavior must return None when :behavior is absent \
14072             — the author-omitted arm must project through the \
14073             accessor's Option::None unchanged",
14074        );
14075    }
14076
14077    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
14078
14079    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
14080        use crate::aplicacao::{Membro, WitContract};
14081        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14082        c.kind = CaixaKind::Aplicacao;
14083        c.membros = vec![Membro {
14084            caixa: "a".into(),
14085            versao: "^0.1".into(),
14086        }];
14087        c.contratos = vec![WitContract {
14088            de: "a".into(),
14089            para: "a".into(),
14090            wit: "wasi:http/proxy".into(),
14091            endpoint: Some("/x".into()),
14092            subject: None,
14093            slot: None,
14094        }];
14095        c.politicas = politicas;
14096        c
14097    }
14098
14099    #[test]
14100    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
14101        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
14102        // composite optional-composite-reference-shape pin:
14103        // [`Caixa::politicas`] must return the `:politicas` typed
14104        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
14105        // reference over the same backing storage the raw
14106        // `self.politicas.as_ref()` field access borrows from,
14107        // byte-equal across every representative fixture in the
14108        // accept-set — the author-omitted `None` shape (the "cluster-
14109        // default applies" partition every downstream mesh-artifact
14110        // emitter treats as "emit no `:politicas` overlay"), the
14111        // empty-composite `Some(MeshPolicy { .. default })` shape
14112        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
14113        // per-axis mesh-policy scalar is `None`, so the peer inner
14114        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
14115        // caixa-mesh overlay elides every per-axis emit but the outer
14116        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
14117        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
14118        // single-axis fixture (only `:timeout` set — the canonical
14119        // shape a latency-sensitive Aplicacao carries), and a
14120        // fully-populated composite (every per-axis mesh-policy
14121        // scalar set — the canonical shape a fully-governed
14122        // Aplicacao carries).
14123        //
14124        // Pins against a future silent detour that returned a fresh-
14125        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
14126        // type-check via the `Clone` impl but silently break every
14127        // downstream caller that relied on the reference sharing the
14128        // composite's backing identity), a reference to an operator-
14129        // resolved overlay (the future per-cluster
14130        // `:politicas-overrides` slot — its resolution must land at
14131        // exactly this accessor body, not silently divert the raw
14132        // slot away from the peer [`Caixa::declared_mesh_slots`]
14133        // enumerator's presence probe), a
14134        // `None` → `Some(MeshPolicy::default)` cluster-default
14135        // projection (which would collapse the load-bearing
14136        // "author-omitted `:politicas` ⇒ cluster-default applies"
14137        // partition the peer [`Caixa::declared_mesh_slots`]
14138        // enumerator and the peer [`Caixa::aplicacao_view`]
14139        // Aplicacao-composition seed both read), or an axis-shuffled
14140        // projection (a future detour that swapped `timeout` and
14141        // `retries` through the accessor would silently split the
14142        // paired [`Caixa::aplicacao_view`] seed's fold input from the
14143        // sibling M3 mesh-artifact emitter's projection input).
14144        //
14145        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
14146        // composite-reference accessor pin on the substrate primitive
14147        // — peer of the sibling
14148        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14149        // (b2bd9d7) and
14150        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14151        // (35d8b52) opening tetrad pins on the outer top-level
14152        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14153        // here to the first of the three M3 mesh-slot axes so the
14154        // opening third of the outer `Option<&Composite>` sub-family
14155        // carries the same "byte-equal, borrow-shared, presence-bit-
14156        // preserved" outer-accessor discipline.
14157        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
14158        use std::time::Duration;
14159        let fixtures: Vec<Option<MeshPolicy>> = vec![
14160            None,
14161            Some(MeshPolicy::default()),
14162            Some(MeshPolicy {
14163                timeout: Some(Duration::from_secs(30)),
14164                ..Default::default()
14165            }),
14166            Some(MeshPolicy {
14167                timeout: Some(Duration::from_secs(30)),
14168                retries: Some(3),
14169                circuit_breaker: Some(CircuitBreaker {
14170                    max_failures: 5,
14171                    window: Duration::from_secs(60),
14172                }),
14173                mtls_required: Some(true),
14174                rate_limit: Some(RateLimit {
14175                    rate: 100,
14176                    window: Duration::from_secs(1),
14177                }),
14178            }),
14179        ];
14180        for politicas in fixtures {
14181            let c = caixa_aplicacao_with_politicas(politicas.clone());
14182            assert_eq!(
14183                c.politicas(),
14184                politicas.as_ref(),
14185                "Caixa::politicas must return :politicas verbatim (got \
14186                 {:?}, expected {:?})",
14187                c.politicas(),
14188                politicas.as_ref(),
14189            );
14190            match (c.politicas(), c.politicas.as_ref()) {
14191                (Some(a), Some(b)) => assert!(
14192                    std::ptr::eq(a, b),
14193                    "Caixa::politicas accessor and self.politicas.as_ref() \
14194                     field access must borrow the same backing storage \
14195                     — the accessor is the substrate-primitive typed \
14196                     dispatch every downstream Aplicacao-mesh-overlay \
14197                     composite consumer must route through, and a \
14198                     reference-identity split would silently break \
14199                     every consumer that relied on the borrow sharing \
14200                     the composite's storage",
14201                ),
14202                (None, None) => {}
14203                _ => panic!(
14204                    "Caixa::politicas presence bit must byte-equal \
14205                     self.politicas.is_some() — a presence-bit drift \
14206                     would silently split the paired \
14207                     Caixa::aplicacao_view Aplicacao-composition seed's \
14208                     traversal head from the peer \
14209                     Caixa::declared_mesh_slots M3 declared-slot \
14210                     enumerator's presence probe",
14211                ),
14212            }
14213            assert_eq!(
14214                c.politicas().is_some(),
14215                c.politicas.is_some(),
14216                "Caixa::politicas().is_some() must byte-equal \
14217                 self.politicas.is_some() — a presence-bit drift would \
14218                 silently split every downstream Option<&MeshPolicy> \
14219                 consumer's partition on the cluster-default arm",
14220            );
14221        }
14222    }
14223
14224    #[test]
14225    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
14226        // Composition pin: [`Caixa::declared_mesh_slots`]'s
14227        // `:politicas` presence-probe arm must key off
14228        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
14229        // field-probe. Structurally: a `Caixa { politicas:
14230        // Some(MeshPolicy::default()), .. }` must still push
14231        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
14232        // presence bit is `Some`, so the M3 kind-coherence gate must
14233        // surface the slot as "declared" even when every per-axis
14234        // scalar is unset), and a `Caixa { politicas: None, .. }` must
14235        // NOT push the label (the "author omitted the slot entirely"
14236        // partition). The pair jointly pins the accessor + declared-
14237        // slot enumerator composition: any future silent detour that
14238        // had the accessor collapse `Some(MeshPolicy::default())` to
14239        // `None` (a `.filter(|p| !p.is_empty())` projection) would
14240        // silently absorb the "declared but empty" arm at the
14241        // accessor boundary and the
14242        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
14243        // coherence gate would silently accept a struct-literal
14244        // `Caixa` carrying the drift.
14245        //
14246        // Peer of the sibling
14247        // `declared_servico_slots_limits_arm_routes_through_accessor`
14248        // (b2bd9d7) and
14249        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14250        // (35d8b52) composition pins on the sibling `:limits` /
14251        // `:behavior` outer-`Option<&Composite>` arms of the peer
14252        // [`Caixa::declared_servico_slots`] M2 declared-slot
14253        // enumerator's traversal — same "the enumerator gate must
14254        // route through the substrate-primitive typed dispatch"
14255        // discipline extended onto the outer top-level [`Caixa`] M3
14256        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
14257        // enumerator carries the same routing invariant as its M2
14258        // sibling.
14259        use crate::aplicacao::MeshPolicy;
14260        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
14261        let slots = c.declared_mesh_slots();
14262        assert!(
14263            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
14264            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
14265             when `:politicas` is Some (even for MeshPolicy::default()) \
14266             — the accessor and the enumerator gate must route through \
14267             the same substrate-primitive typed dispatch on the outer \
14268             :politicas presence bit (got slots={slots:?})",
14269        );
14270        let c = caixa_aplicacao_with_politicas(None);
14271        let slots = c.declared_mesh_slots();
14272        assert!(
14273            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
14274            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
14275             when `:politicas` is None — the author-omitted arm must \
14276             route through the accessor's None-return unchanged (got \
14277             slots={slots:?})",
14278        );
14279    }
14280
14281    #[test]
14282    fn aplicacao_view_politicas_arm_folds_through_accessor() {
14283        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
14284        // Aplicacao-composition seed must fold through
14285        // [`Caixa::politicas`], not the raw
14286        // `self.politicas.clone().unwrap_or_default()` field-borrow.
14287        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
14288        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
14289        // must surface a projected [`crate::AplicacaoSpec`] whose
14290        // `politicas().timeout()` field byte-equals the outer
14291        // composite's `timeout` scalar (the fold must project the
14292        // authored composite verbatim), a `Caixa { politicas:
14293        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
14294        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
14295        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
14296        // fold's empty-composite arm collapses to the same default the
14297        // author-omitted arm does), and a `Caixa { politicas: None,
14298        // kind: Aplicacao, .. }` must surface an
14299        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
14300        // [`crate::aplicacao::MeshPolicy::default`] (the "author
14301        // omitted the slot entirely" arm folds through the
14302        // `unwrap_or_default` onto the cluster-default). The triad
14303        // jointly pins the accessor + Aplicacao-composition seed
14304        // composition: any future silent detour that had the accessor
14305        // divert the raw slot away from the seed's fold (an operator-
14306        // resolved overlay's default-fold arm silently differing from
14307        // the raw slot's default-fold arm) would silently split the
14308        // build-time mesh-artifact emission gate from the caixa-mesh
14309        // renderer's Aplicacao-view input at the composition boundary.
14310        use crate::aplicacao::MeshPolicy;
14311        use std::time::Duration;
14312        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
14313            timeout: Some(Duration::from_secs(30)),
14314            ..Default::default()
14315        }));
14316        let view = c.aplicacao_view().unwrap();
14317        assert_eq!(
14318            view.politicas().timeout(),
14319            Some(Duration::from_secs(30)),
14320            "Caixa::aplicacao_view must fold the authored :politicas \
14321             :timeout scalar through the accessor verbatim onto the \
14322             projected AplicacaoSpec — a future silent detour at the \
14323             seed's fold arm would surface here as a projected-scalar \
14324             drift (got {:?})",
14325            view.politicas().timeout(),
14326        );
14327        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
14328        let view = c.aplicacao_view().unwrap();
14329        assert_eq!(
14330            view.politicas(),
14331            &MeshPolicy::default(),
14332            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
14333             through the accessor onto MeshPolicy::default — the empty- \
14334             composite arm collapses to the same default the author- \
14335             omitted arm does (got {:?})",
14336            view.politicas(),
14337        );
14338        let c = caixa_aplicacao_with_politicas(None);
14339        let view = c.aplicacao_view().unwrap();
14340        assert_eq!(
14341            view.politicas(),
14342            &MeshPolicy::default(),
14343            "Caixa::aplicacao_view must fold None through the accessor's \
14344             unwrap_or_default onto MeshPolicy::default — the author- \
14345             omitted arm must route through the accessor's None-return \
14346             unchanged (got {:?})",
14347            view.politicas(),
14348        );
14349    }
14350
14351    #[test]
14352    fn politicas_projects_option_ref_by_borrow() {
14353        // The by-borrow pin: [`Caixa::politicas`] returns
14354        // `Option<&MeshPolicy>` by borrow — the returned reference
14355        // borrows the underlying `Option<MeshPolicy>` storage of the
14356        // `:politicas` slot and the accessor must not clone the
14357        // backing composite on every call. Peer of the sibling
14358        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
14359        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
14360        // pins on the outer top-level [`Caixa`]
14361        // `Option<&Composite>`-return sub-family — extended here to
14362        // the third axis of the same sub-family: the accessor's
14363        // returned reference must borrow from `&self` (the returned
14364        // reference's lifetime is tied to `&self`), and calling the
14365        // accessor twice on the same [`Caixa`] must yield references
14366        // that are pointer-equal (the underlying byte-buffer is the
14367        // storage `MeshPolicy`'s allocation, not a fresh copy) as
14368        // well as value-equal (idempotent, no side effects on
14369        // `&self`).
14370        //
14371        // Pins against a future silent detour that returned an owned
14372        // `MeshPolicy` (which would type-check via the `Clone` impl
14373        // but silently clone on every call), a `&MeshPolicy` panic-
14374        // return on the `None` arm (which would collapse the load-
14375        // bearing `Option` presence-bit into a runtime panic), or a
14376        // one-arm-only accessor that returned a saturating composite
14377        // on some sentinel input.
14378        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
14379        use std::time::Duration;
14380        for politicas in [
14381            Some(MeshPolicy::default()),
14382            Some(MeshPolicy {
14383                timeout: Some(Duration::from_secs(30)),
14384                retries: Some(3),
14385                circuit_breaker: Some(CircuitBreaker {
14386                    max_failures: 5,
14387                    window: Duration::from_secs(60),
14388                }),
14389                mtls_required: Some(true),
14390                rate_limit: Some(RateLimit {
14391                    rate: 100,
14392                    window: Duration::from_secs(1),
14393                }),
14394            }),
14395        ] {
14396            let c = caixa_aplicacao_with_politicas(politicas.clone());
14397            let first = c.politicas().unwrap();
14398            let second = c.politicas().unwrap();
14399            assert_eq!(
14400                first, second,
14401                "Caixa::politicas must be idempotent — two successive \
14402                 calls on the same &self must return the same \
14403                 &MeshPolicy",
14404            );
14405            assert!(
14406                std::ptr::eq(first, second),
14407                "Caixa::politicas must borrow the underlying \
14408                 Option<MeshPolicy> storage — two successive calls \
14409                 must return references with the same backing pointer \
14410                 (a fresh MeshPolicy clone would change the pointer on \
14411                 every call)",
14412            );
14413            assert_eq!(
14414                Some(first),
14415                politicas.as_ref(),
14416                "Caixa::politicas must return :politicas verbatim by \
14417                 borrow — got {first:?}, expected {:?}",
14418                politicas.as_ref(),
14419            );
14420        }
14421        let c = caixa_aplicacao_with_politicas(None);
14422        assert!(
14423            c.politicas().is_none(),
14424            "Caixa::politicas must return None when :politicas is \
14425             absent — the author-omitted arm must project through the \
14426             accessor's Option::None unchanged",
14427        );
14428    }
14429
14430    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
14431
14432    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
14433        use crate::aplicacao::{Membro, WitContract};
14434        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14435        c.kind = CaixaKind::Aplicacao;
14436        c.membros = vec![Membro {
14437            caixa: "a".into(),
14438            versao: "^0.1".into(),
14439        }];
14440        c.contratos = vec![WitContract {
14441            de: "a".into(),
14442            para: "a".into(),
14443            wit: "wasi:http/proxy".into(),
14444            endpoint: Some("/x".into()),
14445            subject: None,
14446            slot: None,
14447        }];
14448        c.placement = placement;
14449        c
14450    }
14451
14452    #[test]
14453    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
14454        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
14455        // composite optional-composite-reference-shape pin:
14456        // [`Caixa::placement`] must return the `:placement` typed
14457        // `Option<Placement>` verbatim as an `Option<&Placement>`
14458        // reference over the same backing storage the raw
14459        // `self.placement.as_ref()` field access borrows from,
14460        // byte-equal across every representative fixture in the
14461        // accept-set — the author-omitted `None` shape (the
14462        // "cluster-default applies" partition every downstream mesh-
14463        // artifact emitter treats as "emit no `:placement` overlay"),
14464        // the empty-composite `Some(Placement { .. default })` shape
14465        // (`estrategia: SingleNode`, empty clusters, no shard-key /
14466        // affinity — the outer presence-bit is `Some` so
14467        // [`Caixa::declared_mesh_slots`] still pushes the
14468        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
14469        // `Replicated`-on-two-clusters fixture (the canonical shape a
14470        // stateless HTTP Aplicacao carries), and a fully-populated
14471        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
14472        // shape a stateful Akka-style cluster-sharding Aplicacao
14473        // carries).
14474        //
14475        // Pins against a future silent detour that returned a fresh-
14476        // cloned [`crate::aplicacao::Placement`] copy (which would
14477        // type-check via the `Clone` impl but silently break every
14478        // downstream caller that relied on the reference sharing the
14479        // composite's backing identity), a reference to an operator-
14480        // resolved overlay (the future per-cluster
14481        // `:placement-overrides` slot — its resolution must land at
14482        // exactly this accessor body, not silently divert the raw
14483        // slot away from the peer [`Caixa::declared_mesh_slots`]
14484        // enumerator's presence probe), a `None` →
14485        // `Some(Placement::default)` cluster-default projection (which
14486        // would collapse the load-bearing "author-omitted `:placement`
14487        // ⇒ cluster-default applies" partition the peer
14488        // [`Caixa::declared_mesh_slots`] enumerator and the peer
14489        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
14490        // read), or an axis-shuffled projection (a future detour that
14491        // swapped `clusters` and `affinity` through the accessor would
14492        // silently split the paired [`Caixa::aplicacao_view`] seed's
14493        // fold input from the sibling M3 mesh-artifact emitter's
14494        // projection input).
14495        //
14496        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
14497        // composite-reference accessor pin on the substrate primitive
14498        // — peer of the sibling
14499        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14500        // (b2bd9d7),
14501        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14502        // (35d8b52), and
14503        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
14504        // (5d23d29) opening triad pins on the outer top-level
14505        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14506        // here to the second of the three M3 mesh-slot axes so the
14507        // opening four-fifths of the outer `Option<&Composite>` sub-
14508        // family carries the same "byte-equal, borrow-shared,
14509        // presence-bit-preserved" outer-accessor discipline.
14510        use crate::aplicacao::{Placement, PlacementStrategy};
14511        let fixtures: Vec<Option<Placement>> = vec![
14512            None,
14513            Some(Placement::default()),
14514            Some(Placement {
14515                estrategia: PlacementStrategy::Replicated,
14516                clusters: vec!["rio".into(), "sao-paulo".into()],
14517                affinity: None,
14518                shard_key: None,
14519            }),
14520            Some(Placement {
14521                estrategia: PlacementStrategy::Sharded,
14522                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
14523                affinity: Some("data-locality".into()),
14524                shard_key: Some("$tenantId".into()),
14525            }),
14526        ];
14527        for placement in fixtures {
14528            let c = caixa_aplicacao_with_placement(placement.clone());
14529            assert_eq!(
14530                c.placement(),
14531                placement.as_ref(),
14532                "Caixa::placement must return :placement verbatim (got \
14533                 {:?}, expected {:?})",
14534                c.placement(),
14535                placement.as_ref(),
14536            );
14537            match (c.placement(), c.placement.as_ref()) {
14538                (Some(a), Some(b)) => assert!(
14539                    std::ptr::eq(a, b),
14540                    "Caixa::placement accessor and self.placement.as_ref() \
14541                     field access must borrow the same backing storage \
14542                     — the accessor is the substrate-primitive typed \
14543                     dispatch every downstream Aplicacao-distribution- \
14544                     overlay composite consumer must route through, and \
14545                     a reference-identity split would silently break \
14546                     every consumer that relied on the borrow sharing \
14547                     the composite's storage",
14548                ),
14549                (None, None) => {}
14550                _ => panic!(
14551                    "Caixa::placement presence bit must byte-equal \
14552                     self.placement.is_some() — a presence-bit drift \
14553                     would silently split the paired \
14554                     Caixa::aplicacao_view Aplicacao-composition seed's \
14555                     traversal head from the peer \
14556                     Caixa::declared_mesh_slots M3 declared-slot \
14557                     enumerator's presence probe",
14558                ),
14559            }
14560            assert_eq!(
14561                c.placement().is_some(),
14562                c.placement.is_some(),
14563                "Caixa::placement().is_some() must byte-equal \
14564                 self.placement.is_some() — a presence-bit drift would \
14565                 silently split every downstream Option<&Placement> \
14566                 consumer's partition on the cluster-default arm",
14567            );
14568        }
14569    }
14570
14571    #[test]
14572    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
14573        // Composition pin: [`Caixa::declared_mesh_slots`]'s
14574        // `:placement` presence-probe arm must key off
14575        // [`Caixa::placement`], not the raw `self.placement.is_some()`
14576        // field-probe. Structurally: a `Caixa { placement:
14577        // Some(Placement::default()), .. }` must still push
14578        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
14579        // presence bit is `Some`, so the M3 kind-coherence gate must
14580        // surface the slot as "declared" even when every per-axis
14581        // scalar defers to the cluster-default arm), and a `Caixa {
14582        // placement: None, .. }` must NOT push the label (the "author
14583        // omitted the slot entirely" partition). The pair jointly pins
14584        // the accessor + declared-slot enumerator composition: any
14585        // future silent detour that had the accessor collapse
14586        // `Some(Placement::default())` to `None` (a `.filter(|p|
14587        // p.clusters().is_empty().not())` projection) would silently
14588        // absorb the "declared but empty" arm at the accessor boundary
14589        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
14590        // kind-coherence gate would silently accept a struct-literal
14591        // `Caixa` carrying the drift.
14592        //
14593        // Peer of the sibling
14594        // `declared_servico_slots_limits_arm_routes_through_accessor`
14595        // (b2bd9d7),
14596        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14597        // (35d8b52), and
14598        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
14599        // (5d23d29) composition pins on the sibling `:limits` /
14600        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
14601        // — same "the enumerator gate must route through the
14602        // substrate-primitive typed dispatch" discipline extended onto
14603        // the second of the three M3 mesh-slot axes so the
14604        // [`Caixa::declared_mesh_slots`] enumerator carries the same
14605        // routing invariant on the `:placement` arm as the peer
14606        // `:politicas` arm.
14607        use crate::aplicacao::Placement;
14608        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
14609        let slots = c.declared_mesh_slots();
14610        assert!(
14611            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
14612            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
14613             when `:placement` is Some (even for Placement::default()) \
14614             — the accessor and the enumerator gate must route through \
14615             the same substrate-primitive typed dispatch on the outer \
14616             :placement presence bit (got slots={slots:?})",
14617        );
14618        let c = caixa_aplicacao_with_placement(None);
14619        let slots = c.declared_mesh_slots();
14620        assert!(
14621            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
14622            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
14623             when `:placement` is None — the author-omitted arm must \
14624             route through the accessor's None-return unchanged (got \
14625             slots={slots:?})",
14626        );
14627    }
14628
14629    #[test]
14630    fn aplicacao_view_placement_arm_folds_through_accessor() {
14631        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
14632        // Aplicacao-composition seed must fold through
14633        // [`Caixa::placement`], not the raw
14634        // `self.placement.clone().unwrap_or_default()` field-borrow.
14635        // Structurally: a `Caixa { placement: Some(Placement {
14636        // estrategia: Replicated, clusters: ["rio"], .. default }),
14637        // kind: Aplicacao, .. }` must surface a projected
14638        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
14639        // `placement().clusters()` byte-equal the outer composite's
14640        // authored values (the fold must project the authored
14641        // composite verbatim), a `Caixa { placement:
14642        // Some(Placement::default()), kind: Aplicacao, .. }` must
14643        // surface an [`crate::AplicacaoSpec`] whose `placement()`
14644        // byte-equals [`crate::aplicacao::Placement::default`] (the
14645        // fold's empty-composite arm collapses to the same default
14646        // the author-omitted arm does), and a `Caixa { placement:
14647        // None, kind: Aplicacao, .. }` must surface an
14648        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
14649        // [`crate::aplicacao::Placement::default`] (the "author
14650        // omitted the slot entirely" arm folds through the
14651        // `unwrap_or_default` onto the cluster-default). The triad
14652        // jointly pins the accessor + Aplicacao-composition seed
14653        // composition: any future silent detour that had the accessor
14654        // divert the raw slot away from the seed's fold (an operator-
14655        // resolved overlay's default-fold arm silently differing from
14656        // the raw slot's default-fold arm) would silently split the
14657        // build-time distribution-artifact emission gate from the
14658        // caixa-mesh renderer's Aplicacao-view input at the
14659        // composition boundary.
14660        use crate::aplicacao::{Placement, PlacementStrategy};
14661        let c = caixa_aplicacao_with_placement(Some(Placement {
14662            estrategia: PlacementStrategy::Replicated,
14663            clusters: vec!["rio".into()],
14664            affinity: None,
14665            shard_key: None,
14666        }));
14667        let view = c.aplicacao_view().unwrap();
14668        assert_eq!(
14669            view.placement().estrategia(),
14670            PlacementStrategy::Replicated,
14671            "Caixa::aplicacao_view must fold the authored :placement \
14672             :estrategia scalar through the accessor verbatim onto the \
14673             projected AplicacaoSpec — a future silent detour at the \
14674             seed's fold arm would surface here as a projected-scalar \
14675             drift (got {:?})",
14676            view.placement().estrategia(),
14677        );
14678        assert_eq!(
14679            view.placement().clusters(),
14680            &["rio"],
14681            "Caixa::aplicacao_view must fold the authored :placement \
14682             :clusters list through the accessor verbatim onto the \
14683             projected AplicacaoSpec — a future silent detour at the \
14684             seed's fold arm would surface here as a projected-list \
14685             drift (got {:?})",
14686            view.placement().clusters(),
14687        );
14688        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
14689        let view = c.aplicacao_view().unwrap();
14690        assert_eq!(
14691            view.placement(),
14692            &Placement::default(),
14693            "Caixa::aplicacao_view must fold Some(Placement::default()) \
14694             through the accessor onto Placement::default — the empty- \
14695             composite arm collapses to the same default the author- \
14696             omitted arm does (got {:?})",
14697            view.placement(),
14698        );
14699        let c = caixa_aplicacao_with_placement(None);
14700        let view = c.aplicacao_view().unwrap();
14701        assert_eq!(
14702            view.placement(),
14703            &Placement::default(),
14704            "Caixa::aplicacao_view must fold None through the accessor's \
14705             unwrap_or_default onto Placement::default — the author- \
14706             omitted arm must route through the accessor's None-return \
14707             unchanged (got {:?})",
14708            view.placement(),
14709        );
14710    }
14711
14712    #[test]
14713    fn placement_projects_option_ref_by_borrow() {
14714        // The by-borrow pin: [`Caixa::placement`] returns
14715        // `Option<&Placement>` by borrow — the returned reference
14716        // borrows the underlying `Option<Placement>` storage of the
14717        // `:placement` slot and the accessor must not clone the
14718        // backing composite on every call. Peer of the sibling
14719        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
14720        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
14721        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
14722        // pins on the outer top-level [`Caixa`]
14723        // `Option<&Composite>`-return sub-family — extended here to
14724        // the fourth axis of the same sub-family: the accessor's
14725        // returned reference must borrow from `&self` (the returned
14726        // reference's lifetime is tied to `&self`), and calling the
14727        // accessor twice on the same [`Caixa`] must yield references
14728        // that are pointer-equal (the underlying byte-buffer is the
14729        // storage `Placement`'s allocation, not a fresh copy) as well
14730        // as value-equal (idempotent, no side effects on `&self`).
14731        //
14732        // Pins against a future silent detour that returned an owned
14733        // `Placement` (which would type-check via the `Clone` impl
14734        // but silently clone on every call), a `&Placement` panic-
14735        // return on the `None` arm (which would collapse the load-
14736        // bearing `Option` presence-bit into a runtime panic), or a
14737        // one-arm-only accessor that returned a saturating composite
14738        // on some sentinel input.
14739        use crate::aplicacao::{Placement, PlacementStrategy};
14740        for placement in [
14741            Some(Placement::default()),
14742            Some(Placement {
14743                estrategia: PlacementStrategy::Sharded,
14744                clusters: vec!["rio".into(), "sao-paulo".into()],
14745                affinity: Some("data-locality".into()),
14746                shard_key: Some("$tenantId".into()),
14747            }),
14748        ] {
14749            let c = caixa_aplicacao_with_placement(placement.clone());
14750            let first = c.placement().unwrap();
14751            let second = c.placement().unwrap();
14752            assert_eq!(
14753                first, second,
14754                "Caixa::placement must be idempotent — two successive \
14755                 calls on the same &self must return the same \
14756                 &Placement",
14757            );
14758            assert!(
14759                std::ptr::eq(first, second),
14760                "Caixa::placement must borrow the underlying \
14761                 Option<Placement> storage — two successive calls \
14762                 must return references with the same backing pointer \
14763                 (a fresh Placement clone would change the pointer on \
14764                 every call)",
14765            );
14766            assert_eq!(
14767                Some(first),
14768                placement.as_ref(),
14769                "Caixa::placement must return :placement verbatim by \
14770                 borrow — got {first:?}, expected {:?}",
14771                placement.as_ref(),
14772            );
14773        }
14774        let c = caixa_aplicacao_with_placement(None);
14775        assert!(
14776            c.placement().is_none(),
14777            "Caixa::placement must return None when :placement is \
14778             absent — the author-omitted arm must project through the \
14779             accessor's Option::None unchanged",
14780        );
14781    }
14782
14783    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
14784
14785    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
14786        use crate::aplicacao::{Membro, WitContract};
14787        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14788        c.kind = CaixaKind::Aplicacao;
14789        c.membros = vec![Membro {
14790            caixa: "a".into(),
14791            versao: "^0.1".into(),
14792        }];
14793        c.contratos = vec![WitContract {
14794            de: "a".into(),
14795            para: "a".into(),
14796            wit: "wasi:http/proxy".into(),
14797            endpoint: Some("/x".into()),
14798            subject: None,
14799            slot: None,
14800        }];
14801        c.entrada = entrada;
14802        c
14803    }
14804
14805    #[test]
14806    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
14807        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
14808        // composite optional-composite-reference-shape pin:
14809        // [`Caixa::entrada`] must return the `:entrada` typed
14810        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
14811        // reference over the same backing storage the raw
14812        // `self.entrada.as_ref()` field access borrows from,
14813        // byte-equal across every representative fixture in the
14814        // accept-set — the author-omitted `None` shape (the
14815        // "cluster-internal Aplicacao" partition every downstream
14816        // Gateway-API emitter treats as "emit no listener + no
14817        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
14818        // (empty `paths` — the resolved-paths fallback the peer
14819        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
14820        // onto the substrate catch-all), and a fully-populated
14821        // multi-path-with-non-default-port fixture (the canonical
14822        // shape a public HTTP Aplicacao carries).
14823        //
14824        // Pins against a future silent detour that returned a fresh-
14825        // cloned [`crate::aplicacao::Entrada`] copy (which would
14826        // type-check via the `Clone` impl but silently break every
14827        // downstream caller that relied on the reference sharing the
14828        // composite's backing identity), a reference to an operator-
14829        // resolved overlay (the future per-cluster
14830        // `:entrada-overrides` slot — its resolution must land at
14831        // exactly this accessor body, not silently divert the raw
14832        // slot away from the peer [`Caixa::declared_mesh_slots`]
14833        // enumerator's presence probe), or an axis-shuffled projection
14834        // (a future detour that swapped `host` and `para` through the
14835        // accessor would silently split the paired
14836        // [`Caixa::aplicacao_view`] seed's forward input from the
14837        // sibling M3 gateway-artifact emitter's projection input).
14838        //
14839        // Fifth and final outer top-level [`Caixa`]
14840        // `Option<&Composite>`-return composite-reference accessor pin
14841        // on the substrate primitive — peer of the sibling
14842        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14843        // (b2bd9d7),
14844        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14845        // (35d8b52),
14846        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
14847        // (5d23d29), and
14848        // `placement_returns_placement_option_ref_verbatim_across_permutations`
14849        // (4fb8074) opening tetrad pins on the outer top-level
14850        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14851        // here to the third and final M3 mesh-slot axis so the closed
14852        // outer `Option<&Composite>` sub-family carries the same
14853        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
14854        // accessor discipline across all five arms.
14855        use crate::aplicacao::Entrada;
14856        let fixtures: Vec<Option<Entrada>> = vec![
14857            None,
14858            Some(Entrada {
14859                host: "checkout.quero.cloud".into(),
14860                para: "gateway".into(),
14861                paths: Vec::new(),
14862                port: crate::DEFAULT_SERVICO_PORT,
14863            }),
14864            Some(Entrada {
14865                host: "api.pleme.io".into(),
14866                para: "public-api".into(),
14867                paths: vec!["/v1".into(), "/v2".into()],
14868                port: 8080,
14869            }),
14870        ];
14871        for entrada in fixtures {
14872            let c = caixa_aplicacao_with_entrada(entrada.clone());
14873            assert_eq!(
14874                c.entrada(),
14875                entrada.as_ref(),
14876                "Caixa::entrada must return :entrada verbatim (got \
14877                 {:?}, expected {:?})",
14878                c.entrada(),
14879                entrada.as_ref(),
14880            );
14881            match (c.entrada(), c.entrada.as_ref()) {
14882                (Some(a), Some(b)) => assert!(
14883                    std::ptr::eq(a, b),
14884                    "Caixa::entrada accessor and self.entrada.as_ref() \
14885                     field access must borrow the same backing storage \
14886                     — the accessor is the substrate-primitive typed \
14887                     dispatch every downstream Aplicacao-external- \
14888                     gateway composite consumer must route through, and \
14889                     a reference-identity split would silently break \
14890                     every consumer that relied on the borrow sharing \
14891                     the composite's storage",
14892                ),
14893                (None, None) => {}
14894                _ => panic!(
14895                    "Caixa::entrada presence bit must byte-equal \
14896                     self.entrada.is_some() — a presence-bit drift \
14897                     would silently split the paired \
14898                     Caixa::aplicacao_view Aplicacao-composition seed's \
14899                     traversal head from the peer \
14900                     Caixa::declared_mesh_slots M3 declared-slot \
14901                     enumerator's presence probe",
14902                ),
14903            }
14904            assert_eq!(
14905                c.entrada().is_some(),
14906                c.entrada.is_some(),
14907                "Caixa::entrada().is_some() must byte-equal \
14908                 self.entrada.is_some() — a presence-bit drift would \
14909                 silently split every downstream Option<&Entrada> \
14910                 consumer's partition on the cluster-internal arm",
14911            );
14912        }
14913    }
14914
14915    #[test]
14916    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
14917        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
14918        // presence-probe arm must key off [`Caixa::entrada`], not the
14919        // raw `self.entrada.is_some()` field-probe. Structurally: a
14920        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
14921        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
14922        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
14923        // presence bit is `Some`, so the M3 kind-coherence gate must
14924        // surface the slot as "declared" even when every per-axis
14925        // scalar defers to the substrate catch-all / default port),
14926        // and a `Caixa { entrada: None, .. }` must NOT push the label
14927        // (the "author omitted the slot entirely" partition). The pair
14928        // jointly pins the accessor + declared-slot enumerator
14929        // composition: any future silent detour that had the accessor
14930        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
14931        // `.filter(|e| !e.paths.is_empty())` projection) would silently
14932        // absorb the "declared but empty-paths" arm at the accessor
14933        // boundary and the
14934        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
14935        // coherence gate would silently accept a struct-literal
14936        // `Caixa` carrying the drift.
14937        //
14938        // Peer of the sibling
14939        // `declared_servico_slots_limits_arm_routes_through_accessor`
14940        // (b2bd9d7),
14941        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14942        // (35d8b52),
14943        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
14944        // (5d23d29), and
14945        // `declared_mesh_slots_placement_arm_routes_through_accessor`
14946        // (4fb8074) composition pins on the sibling `:limits` /
14947        // `:behavior` / `:politicas` / `:placement` outer-
14948        // `Option<&Composite>` arms — same "the enumerator gate must
14949        // route through the substrate-primitive typed dispatch"
14950        // discipline extended onto the third and final M3 mesh-slot
14951        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
14952        // carries the routing invariant on every M3 mesh-slot arm.
14953        use crate::aplicacao::Entrada;
14954        let c = caixa_aplicacao_with_entrada(Some(Entrada {
14955            host: "checkout.quero.cloud".into(),
14956            para: "gateway".into(),
14957            paths: Vec::new(),
14958            port: crate::DEFAULT_SERVICO_PORT,
14959        }));
14960        let slots = c.declared_mesh_slots();
14961        assert!(
14962            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
14963            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
14964             `:entrada` is Some (even for empty-paths / default-port) \
14965             — the accessor and the enumerator gate must route through \
14966             the same substrate-primitive typed dispatch on the outer \
14967             :entrada presence bit (got slots={slots:?})",
14968        );
14969        let c = caixa_aplicacao_with_entrada(None);
14970        let slots = c.declared_mesh_slots();
14971        assert!(
14972            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
14973            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
14974             when `:entrada` is None — the author-omitted arm must \
14975             route through the accessor's None-return unchanged (got \
14976             slots={slots:?})",
14977        );
14978    }
14979
14980    #[test]
14981    fn aplicacao_view_entrada_arm_folds_through_accessor() {
14982        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
14983        // Aplicacao-composition seed must fold through
14984        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
14985        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
14986        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
14987        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
14988        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
14989        // equals the outer composite's authored value (the fold must
14990        // project the authored composite verbatim), and a `Caixa {
14991        // entrada: None, kind: Aplicacao, .. }` must surface an
14992        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
14993        // "author omitted the slot entirely" arm folds through the
14994        // accessor's `Option::cloned` onto the same `None` presence
14995        // bit — unlike the peer `:politicas` / `:placement` arms
14996        // `:entrada` has no cluster-default fold, the omitted arm
14997        // stays omitted). The pair jointly pins the accessor +
14998        // Aplicacao-composition seed composition: any future silent
14999        // detour that had the accessor divert the raw slot away from
15000        // the seed's fold (an operator-resolved overlay's forward arm
15001        // silently differing from the raw slot's forward arm) would
15002        // silently split the build-time gateway-artifact emission gate
15003        // from the caixa-mesh renderer's Aplicacao-view input at the
15004        // composition boundary.
15005        use crate::aplicacao::Entrada;
15006        let authored = Entrada {
15007            host: "api.pleme.io".into(),
15008            para: "public-api".into(),
15009            paths: vec!["/v1".into()],
15010            port: 8080,
15011        };
15012        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
15013        let view = c.aplicacao_view().unwrap();
15014        assert_eq!(
15015            view.entrada(),
15016            Some(&authored),
15017            "Caixa::aplicacao_view must fold the authored :entrada \
15018             composite through the accessor verbatim onto the \
15019             projected AplicacaoSpec — a future silent detour at the \
15020             seed's fold arm would surface here as a projected- \
15021             composite drift (got {:?})",
15022            view.entrada(),
15023        );
15024        let c = caixa_aplicacao_with_entrada(None);
15025        let view = c.aplicacao_view().unwrap();
15026        assert!(
15027            view.entrada().is_none(),
15028            "Caixa::aplicacao_view must fold None through the \
15029             accessor's Option::cloned onto None — the author- \
15030             omitted arm must route through the accessor's None-return \
15031             unchanged (got {:?})",
15032            view.entrada(),
15033        );
15034    }
15035
15036    #[test]
15037    fn entrada_projects_option_ref_by_borrow() {
15038        // The by-borrow pin: [`Caixa::entrada`] returns
15039        // `Option<&Entrada>` by borrow — the returned reference
15040        // borrows the underlying `Option<Entrada>` storage of the
15041        // `:entrada` slot and the accessor must not clone the backing
15042        // composite on every call. Peer of the sibling
15043        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
15044        // `behavior_projects_option_ref_by_borrow` (35d8b52),
15045        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
15046        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
15047        // borrow pins on the outer top-level [`Caixa`]
15048        // `Option<&Composite>`-return sub-family — extended here to
15049        // the fifth and final axis of the same sub-family, closing
15050        // the discipline: the accessor's returned reference must
15051        // borrow from `&self` (the returned reference's lifetime is
15052        // tied to `&self`), and calling the accessor twice on the
15053        // same [`Caixa`] must yield references that are pointer-equal
15054        // (the underlying byte-buffer is the storage `Entrada`'s
15055        // allocation, not a fresh copy) as well as value-equal
15056        // (idempotent, no side effects on `&self`).
15057        //
15058        // Pins against a future silent detour that returned an owned
15059        // `Entrada` (which would type-check via the `Clone` impl but
15060        // silently clone on every call), a `&Entrada` panic-return on
15061        // the `None` arm (which would collapse the load-bearing
15062        // `Option` presence-bit into a runtime panic), or a one-arm-
15063        // only accessor that returned a saturating composite on some
15064        // sentinel input.
15065        use crate::aplicacao::Entrada;
15066        for entrada in [
15067            Some(Entrada {
15068                host: "checkout.quero.cloud".into(),
15069                para: "gateway".into(),
15070                paths: Vec::new(),
15071                port: crate::DEFAULT_SERVICO_PORT,
15072            }),
15073            Some(Entrada {
15074                host: "api.pleme.io".into(),
15075                para: "public-api".into(),
15076                paths: vec!["/v1".into(), "/v2".into()],
15077                port: 8080,
15078            }),
15079        ] {
15080            let c = caixa_aplicacao_with_entrada(entrada.clone());
15081            let first = c.entrada().unwrap();
15082            let second = c.entrada().unwrap();
15083            assert_eq!(
15084                first, second,
15085                "Caixa::entrada must be idempotent — two successive \
15086                 calls on the same &self must return the same &Entrada",
15087            );
15088            assert!(
15089                std::ptr::eq(first, second),
15090                "Caixa::entrada must borrow the underlying \
15091                 Option<Entrada> storage — two successive calls must \
15092                 return references with the same backing pointer (a \
15093                 fresh Entrada clone would change the pointer on every \
15094                 call)",
15095            );
15096            assert_eq!(
15097                Some(first),
15098                entrada.as_ref(),
15099                "Caixa::entrada must return :entrada verbatim by \
15100                 borrow — got {first:?}, expected {:?}",
15101                entrada.as_ref(),
15102            );
15103        }
15104        let c = caixa_aplicacao_with_entrada(None);
15105        assert!(
15106            c.entrada().is_none(),
15107            "Caixa::entrada must return None when :entrada is absent \
15108             — the author-omitted arm must project through the \
15109             accessor's Option::None unchanged",
15110        );
15111    }
15112
15113    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
15114
15115    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
15116        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15117        c.estrategia = estrategia;
15118        c
15119    }
15120
15121    #[test]
15122    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
15123        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
15124        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
15125        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
15126        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
15127        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
15128        // over the same discriminant the raw `self.estrategia` field
15129        // access carries, byte-equal across every representative fixture
15130        // in the accept-set — the author-omitted `None` shape (the
15131        // "defer to [`RestartStrategy::default`] through the
15132        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
15133        // every non-`Supervisor`-kind `defcaixa` carries by
15134        // `#[serde(default)]`), and each of the four closed-set variants
15135        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
15136        // / [`RestartStrategy::RestForOne`] /
15137        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
15138        // partitions on.
15139        //
15140        // Pins against a future silent detour that re-derived the
15141        // strategy from a peer axis (an accidental fallback to
15142        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
15143        // collapse that read the outer `:children` list-length axis into
15144        // the strategy discriminator at the accessor boundary), a
15145        // stale-derive detour that substituted [`RestartStrategy::default`]
15146        // when the outer `Option` held `None` (which would silently
15147        // collapse the load-bearing "author explicitly declared
15148        // `:estrategia OneForOne`" vs "author omitted the slot and
15149        // inherited the default" partition the [`Self::declared_supervisor_slots`]
15150        // presence-probe reads — the enumerator gate would still push
15151        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
15152        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
15153        // kind-coherence gate's traversal head from the
15154        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
15155        // composition head), a reference to an operator-resolved overlay
15156        // (the future per-cluster `:estrategia-overrides` slot — its
15157        // resolution must land at exactly this accessor body, not
15158        // silently divert the raw slot away from a second consumer), or
15159        // an axis-remap projection (a future detour that mapped
15160        // `OneForAll` through the accessor onto `OneForOne` would
15161        // silently split every downstream sibling-restart-strategy
15162        // consumer's per-arm fan-out).
15163        //
15164        // First outer top-level [`Caixa`] `Option<Copy>`-return
15165        // supervisor-tree-slot flat-spread accessor pin on the substrate
15166        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
15167        // projection pattern the sibling per-`Caixa` `:max-restarts` /
15168        // `:restart-window` future outer-scalar pins fold on. Peer of
15169        // the inner-altitude
15170        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
15171        // (eafb619) pin on the post-composition [`SupervisorSpec`]
15172        // altitude — same "the substrate-primitive accessor must byte-
15173        // equal the raw field access verbatim across every author-
15174        // declared value" discipline extended onto the pre-composition
15175        // outer author-surface [`Caixa`] altitude. Peer of the closed
15176        // outer-`Caixa` `Option<&Composite>` composite-reference family
15177        // the sibling `limits` / `behavior` / `politicas` / `placement` /
15178        // `entrada`
15179        // `..._returns_..._option_ref_verbatim_across_permutations` pins
15180        // already carry on the outer `Option<&Composite>` altitude.
15181        use crate::supervisor::RestartStrategy;
15182        let fixtures: Vec<Option<RestartStrategy>> = vec![
15183            None,
15184            Some(RestartStrategy::OneForOne),
15185            Some(RestartStrategy::OneForAll),
15186            Some(RestartStrategy::RestForOne),
15187            Some(RestartStrategy::SimpleOneForOne),
15188        ];
15189        for estrategia in fixtures {
15190            let c = caixa_with_estrategia(estrategia);
15191            assert_eq!(
15192                c.estrategia(),
15193                estrategia,
15194                "Caixa::estrategia must return :estrategia verbatim (got \
15195                 {:?}, expected {:?})",
15196                c.estrategia(),
15197                estrategia,
15198            );
15199            assert_eq!(
15200                c.estrategia(),
15201                c.estrategia,
15202                "Caixa::estrategia accessor and self.estrategia field \
15203                 access must byte-equal — the accessor is the substrate-\
15204                 primitive typed dispatch every downstream supervisor-\
15205                 tree flat-spread consumer must route through, and a \
15206                 discriminant split would silently break every consumer \
15207                 that relied on the accessor sharing the field's own \
15208                 Option<Copy> shape",
15209            );
15210            assert_eq!(
15211                c.estrategia().is_some(),
15212                c.estrategia.is_some(),
15213                "Caixa::estrategia().is_some() must byte-equal \
15214                 self.estrategia.is_some() — a presence-bit drift would \
15215                 silently split the paired Caixa::declared_supervisor_slots \
15216                 presence-probe arm from the Caixa::supervisor_view \
15217                 unwrap_or_default() fold's composition input",
15218            );
15219        }
15220    }
15221
15222    #[test]
15223    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
15224        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15225        // `:estrategia` presence-probe arm must key off
15226        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
15227        // field-probe. Structurally: every `Caixa { estrategia:
15228        // Some(RestartStrategy::_), .. }` variant must push
15229        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
15230        // (the presence bit is `Some` for every closed-set variant, so
15231        // the M2 supervisor-tree kind-coherence gate must surface the
15232        // slot as "declared" regardless of which variant the author
15233        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
15234        // the label (the "author omitted the slot entirely, deferring
15235        // to [`RestartStrategy::default`] through the supervisor_view
15236        // fold" partition). The pair jointly pins the accessor +
15237        // declared-slot enumerator composition: any future silent detour
15238        // that had the accessor collapse `Some(RestartStrategy::default())`
15239        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
15240        // projection) would silently absorb the "declared but default-
15241        // valued" arm at the accessor boundary and the
15242        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
15243        // coherence gate would silently accept a struct-literal `Caixa`
15244        // carrying the drift.
15245        //
15246        // Peer of the sibling per-`Caixa`
15247        // `declared_servico_slots_limits_arm_routes_through_accessor`
15248        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
15249        // `Option<&LimitsSpec>` composition axis — same "the enumerator
15250        // gate must route through the substrate-primitive typed
15251        // dispatch" discipline extended onto the flat-spread M2
15252        // supervisor-tree `Option<RestartStrategy>`-composition surface,
15253        // opening the outer-`Caixa` supervisor-tree-slot arm of the
15254        // composition-pin family.
15255        use crate::supervisor::RestartStrategy;
15256        for estrategia in [
15257            RestartStrategy::OneForOne,
15258            RestartStrategy::OneForAll,
15259            RestartStrategy::RestForOne,
15260            RestartStrategy::SimpleOneForOne,
15261        ] {
15262            let c = caixa_with_estrategia(Some(estrategia));
15263            let slots = c.declared_supervisor_slots();
15264            assert!(
15265                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
15266                "declared_supervisor_slots must push \
15267                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
15268                 Some({estrategia:?}) — the accessor and the enumerator \
15269                 gate must route through the same substrate-primitive \
15270                 typed dispatch on the outer :estrategia presence bit \
15271                 (got slots={slots:?})",
15272            );
15273        }
15274        let c = caixa_with_estrategia(None);
15275        let slots = c.declared_supervisor_slots();
15276        assert!(
15277            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
15278            "declared_supervisor_slots must NOT push \
15279             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
15280             — the author-omitted arm must route through the accessor's \
15281             None-return unchanged (got slots={slots:?})",
15282        );
15283    }
15284
15285    #[test]
15286    fn supervisor_view_estrategia_arm_routes_through_accessor() {
15287        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
15288        // [`SupervisorSpec`] construction arm must key off
15289        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
15290        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
15291        // for every `:kind Supervisor` `Caixa` carrying an author-
15292        // declared `Some(RestartStrategy::_)` variant, the composed
15293        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
15294        // outer accessor's declared variant unchanged; and for a
15295        // `:kind Supervisor` `Caixa` carrying `None`, the composed
15296        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
15297        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
15298        // arm the flat-spread `unwrap_or_default()` fold projects to on
15299        // the author-omitted arm — this is the *composition* between the
15300        // outer `Option<RestartStrategy>` accessor's presence-bit
15301        // surface and the inner post-composition non-`Option`
15302        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
15303        // pins the accessor + supervisor_view composition: any future
15304        // silent detour that had the accessor promote `None` to
15305        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
15306        // projection) would silently collapse the two arms into one at
15307        // the accessor boundary and the [`Self::declared_supervisor_slots`]
15308        // presence probe would silently drift from the composition site.
15309        //
15310        // Peer of the sibling M2 supervisor-slot post-composition
15311        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
15312        // pin on the [`SupervisorSpec::validate`] altitude — this pin
15313        // extends that inner-altitude accessor-routing discipline onto
15314        // the pre-composition outer author-surface [`Caixa`] altitude,
15315        // pinning the composition edge between the flat-spread outer
15316        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
15317        // `RestartStrategy` axes.
15318        use crate::CaixaKind;
15319        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
15320        for estrategia in [
15321            RestartStrategy::OneForOne,
15322            RestartStrategy::OneForAll,
15323            RestartStrategy::RestForOne,
15324            RestartStrategy::SimpleOneForOne,
15325        ] {
15326            let mut c = caixa_with_estrategia(Some(estrategia));
15327            c.kind = CaixaKind::Supervisor;
15328            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
15329            // shape partition through the [`gen_platform::IsVariant`]
15330            // derive-generated
15331            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
15332            // than the raw `matches!(estrategia, RestartStrategy::
15333            // SimpleOneForOne)` open-coded pattern-match — same closed-
15334            // set-typed-enum arm-discriminator dispatch discipline the
15335            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
15336            // convergence (915a934) extended onto its two paired positive
15337            // / negated `matches!` sites and the peer
15338            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
15339            // predicate convergence (766ec63) extended onto the M3 mesh-
15340            // slot per-`:placement` distribution-strategy discriminator
15341            // axis. See the sibling `supervisor::tests::
15342            // round_trip_all_strategies` and
15343            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
15344            // fixtures — the three sites (all test-only,
15345            // acknowledged in 915a934's Prior-commits footnote as the
15346            // outstanding follow-up) now consult one typed dispatch on
15347            // the substrate primitive.
15348            c.children = if estrategia.is_simple_one_for_one() {
15349                Vec::new()
15350            } else {
15351                vec![ChildSpec {
15352                    caixa: "worker".into(),
15353                    versao: "^0.1".into(),
15354                    restart: RestartPolicy::Permanent,
15355                }]
15356            };
15357            let view = c.supervisor_view().expect(
15358                "supervisor_view must materialize a SupervisorSpec for a \
15359                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
15360            );
15361            assert_eq!(
15362                view.estrategia(),
15363                c.estrategia().unwrap(),
15364                "supervisor_view must carry the outer Caixa::estrategia() \
15365                 declared variant onto the composed SupervisorSpec.estrategia \
15366                 field verbatim on the Some arm (got {:?}, expected {:?})",
15367                view.estrategia(),
15368                c.estrategia().unwrap(),
15369            );
15370        }
15371        // The author-omitted arm: outer `None` → composed
15372        // `RestartStrategy::default()` through the flat-spread
15373        // `unwrap_or_default()` fold.
15374        let mut c = caixa_with_estrategia(None);
15375        c.kind = CaixaKind::Supervisor;
15376        // Populate children so the sibling supervisor slots are coherent
15377        // for the [`Self::supervisor_view`] projection; the `:estrategia`
15378        // arm still defers to [`RestartStrategy::default`] on the
15379        // author-omitted arm even when the sibling slots carry values.
15380        c.children = vec![ChildSpec {
15381            caixa: "worker".into(),
15382            versao: "^0.1".into(),
15383            restart: RestartPolicy::Permanent,
15384        }];
15385        let view = c.supervisor_view().expect(
15386            "supervisor_view must materialize a SupervisorSpec for a \
15387             :kind Supervisor Caixa carrying a None `:estrategia` slot",
15388        );
15389        assert_eq!(
15390            view.estrategia(),
15391            RestartStrategy::default(),
15392            "supervisor_view must project the outer Caixa::estrategia() \
15393             None arm onto RestartStrategy::default() through the flat-\
15394             spread unwrap_or_default() fold (got {:?}, expected {:?})",
15395            view.estrategia(),
15396            RestartStrategy::default(),
15397        );
15398        assert!(
15399            c.estrategia().is_none(),
15400            "Caixa::estrategia() must remain None on the author-omitted \
15401             arm — the supervisor_view fold must not mutate the outer \
15402             flat-spread presence bit",
15403        );
15404    }
15405
15406    #[test]
15407    fn estrategia_projects_option_by_copy() {
15408        // The by-`Copy` pin: [`Caixa::estrategia`] returns
15409        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
15410        // the accessor does not borrow `&self` past the call (no
15411        // lifetime on the return type), and calling the accessor twice
15412        // on the same [`Caixa`] must yield discriminant-equal values
15413        // (idempotent, no side effects on `&self`). Peer of the sibling
15414        // outer-`Caixa` `Option<&Composite>` by-borrow
15415        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
15416        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
15417        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
15418        // `placement_projects_option_ref_by_borrow` (4fb8074) /
15419        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
15420        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
15421        // extended here to the outer-`Caixa` `Option<Copy>`-return
15422        // flat-spread axis. The `Copy` discipline replaces the pointer-
15423        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
15424        // `Copy` discriminant is definitionally the same discriminant, so
15425        // the axis reduces to discriminant equality).
15426        //
15427        // Pins against a future silent detour that returned a fresh
15428        // `Option<&RestartStrategy>` (which would type-check but silently
15429        // introduce a borrow of `&self` past the call, collapsing the
15430        // load-bearing "no lifetime on the return type" `Copy` projection
15431        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
15432        // read side effect that flipped the outer discriminant on
15433        // successive calls, or an axis-remap projection that returned a
15434        // different variant than the field storage.
15435        use crate::supervisor::RestartStrategy;
15436        for estrategia in [
15437            Some(RestartStrategy::OneForOne),
15438            Some(RestartStrategy::OneForAll),
15439            Some(RestartStrategy::RestForOne),
15440            Some(RestartStrategy::SimpleOneForOne),
15441        ] {
15442            let c = caixa_with_estrategia(estrategia);
15443            let first = c.estrategia();
15444            let second = c.estrategia();
15445            assert_eq!(
15446                first, second,
15447                "Caixa::estrategia must be idempotent — two successive \
15448                 calls on the same &self must return the same \
15449                 Option<RestartStrategy>",
15450            );
15451            assert_eq!(
15452                first, estrategia,
15453                "Caixa::estrategia must return :estrategia verbatim by \
15454                 Copy — got {first:?}, expected {estrategia:?}",
15455            );
15456        }
15457        let c = caixa_with_estrategia(None);
15458        assert!(
15459            c.estrategia().is_none(),
15460            "Caixa::estrategia must return None when :estrategia is \
15461             absent — the author-omitted arm must project through the \
15462             accessor's Option::None unchanged",
15463        );
15464    }
15465
15466    // ── Caixa::max_restarts / Caixa::restart_window —
15467    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
15468    //    (Option<u32> / Option<&str>) folding on the ed04d3c
15469    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
15470
15471    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
15472        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15473        c.max_restarts = max_restarts;
15474        c
15475    }
15476
15477    fn caixa_supervisor_with_max_restarts_and_window(
15478        max_restarts: Option<u32>,
15479        restart_window: Option<&str>,
15480    ) -> Caixa {
15481        use crate::CaixaKind;
15482        use crate::supervisor::{ChildSpec, RestartPolicy};
15483        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
15484        c.kind = CaixaKind::Supervisor;
15485        c.max_restarts = max_restarts;
15486        c.restart_window = restart_window.map(str::to_string);
15487        c.children = vec![ChildSpec {
15488            caixa: "worker".into(),
15489            versao: "^0.1".into(),
15490            restart: RestartPolicy::Permanent,
15491        }];
15492        c
15493    }
15494
15495    #[test]
15496    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
15497        // Value-shape pin: [`Caixa::max_restarts`] returns the
15498        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
15499        // from the typed slot's own storage, byte-equal across the
15500        // author-omitted `None` arm (the "defer to the
15501        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
15502        // `{intensity, 5, 60}` default" partition every
15503        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
15504        // and each of the representative fixtures in the accept-set —
15505        // `0` (the zero-floor arm the peer
15506        // [`crate::supervisor::SupervisorSpec::validate`]
15507        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
15508        // the post-composition altitude — the accessor must ship the
15509        // raw slot verbatim so struct-literal fixtures continue to
15510        // expose the zero at the accessor boundary), the OTP-canonical
15511        // `5` default (`{intensity, 5, 60}` worker-supervisor from
15512        // Learn You Some Erlang), `1000` (the
15513        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
15514        // upper-bound gate accepts on the boundary), `u32::MAX` (a
15515        // past-the-cap sentinel that the substrate-primitive accessor
15516        // must still ship verbatim). Second outer top-level
15517        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
15518        // pin — folds on the sibling
15519        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
15520        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
15521        // onto the sibling `Option<u32>` restart-budget-count arm.
15522        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
15523        for max_restarts in fixtures {
15524            let c = caixa_with_max_restarts(max_restarts);
15525            assert_eq!(
15526                c.max_restarts(),
15527                max_restarts,
15528                "Caixa::max_restarts must return :max-restarts verbatim \
15529                 (got {:?}, expected {max_restarts:?})",
15530                c.max_restarts(),
15531            );
15532            assert_eq!(
15533                c.max_restarts(),
15534                c.max_restarts,
15535                "Caixa::max_restarts accessor and self.max_restarts \
15536                 field access must byte-equal — a presence-bit or count \
15537                 drift would silently split the paired \
15538                 Caixa::declared_supervisor_slots presence-probe arm \
15539                 from the Caixa::supervisor_view unwrap_or(5) fold's \
15540                 composition input",
15541            );
15542        }
15543    }
15544
15545    #[test]
15546    fn max_restarts_projects_option_by_copy() {
15547        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
15548        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
15549        // borrow `&self` past the call (no lifetime on the return type),
15550        // and calling the accessor twice on the same [`Caixa`] must
15551        // yield equal values (idempotent, no side effects). Peer of the
15552        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
15553        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
15554        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
15555            let c = caixa_with_max_restarts(max_restarts);
15556            let first = c.max_restarts();
15557            let second = c.max_restarts();
15558            assert_eq!(
15559                first, second,
15560                "Caixa::max_restarts must be idempotent — two successive \
15561                 calls on the same &self must return the same Option<u32>",
15562            );
15563            assert_eq!(
15564                first, max_restarts,
15565                "Caixa::max_restarts must return :max-restarts verbatim \
15566                 by Copy — got {first:?}, expected {max_restarts:?}",
15567            );
15568        }
15569    }
15570
15571    #[test]
15572    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
15573        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15574        // `:max-restarts` presence-probe arm must key off
15575        // [`Caixa::max_restarts`], not the raw
15576        // `self.max_restarts.is_some()` field-probe. Structurally: every
15577        // `Caixa { max_restarts: Some(_), .. }` variant must push
15578        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
15579        // list (the presence bit is `Some` for every representative
15580        // count, so the M2 kind-coherence gate must surface the slot as
15581        // "declared"), and a `Caixa { max_restarts: None, .. }` must
15582        // NOT push the label. Peer of the sibling
15583        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
15584        // (ed04d3c) composition pin — same routing-through-accessor
15585        // discipline extended onto the sibling flat-spread `Option<u32>`
15586        // arm.
15587        for max_restarts in [0u32, 5, 1000, u32::MAX] {
15588            let c = caixa_with_max_restarts(Some(max_restarts));
15589            let slots = c.declared_supervisor_slots();
15590            assert!(
15591                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
15592                "declared_supervisor_slots must push \
15593                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
15594                 is Some({max_restarts}) — the accessor and the \
15595                 enumerator gate must route through the same \
15596                 substrate-primitive typed dispatch on the outer \
15597                 :max-restarts presence bit (got slots={slots:?})",
15598            );
15599        }
15600        let c = caixa_with_max_restarts(None);
15601        let slots = c.declared_supervisor_slots();
15602        assert!(
15603            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
15604            "declared_supervisor_slots must NOT push \
15605             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
15606             None — the author-omitted arm must route through the \
15607             accessor's None-return unchanged (got slots={slots:?})",
15608        );
15609    }
15610
15611    #[test]
15612    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
15613        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
15614        // [`SupervisorSpec`] construction arm must key off
15615        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
15616        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
15617        // every `:kind Supervisor` `Caixa` carrying an author-declared
15618        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
15619        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
15620        // carrying `None`, the composed [`SupervisorSpec`]'s
15621        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
15622        // of the sibling
15623        // `supervisor_view_estrategia_arm_routes_through_accessor`
15624        // (ed04d3c) composition pin.
15625        for max_restarts in [1u32, 5, 1000] {
15626            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
15627            let view = c.supervisor_view().expect(
15628                "supervisor_view must materialize a SupervisorSpec for a \
15629                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
15630            );
15631            assert_eq!(
15632                view.max_restarts(),
15633                max_restarts,
15634                "supervisor_view must carry the outer \
15635                 Caixa::max_restarts() Some arm onto the composed \
15636                 SupervisorSpec.max_restarts field verbatim (got {}, \
15637                 expected {max_restarts})",
15638                view.max_restarts(),
15639            );
15640        }
15641        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
15642        let view = c.supervisor_view().expect(
15643            "supervisor_view must materialize a SupervisorSpec for a \
15644             :kind Supervisor Caixa carrying a None :max-restarts",
15645        );
15646        assert_eq!(
15647            view.max_restarts(),
15648            5,
15649            "supervisor_view must project the outer \
15650             Caixa::max_restarts() None arm onto the OTP-canonical \
15651             {{intensity, 5, 60}} default (5) through the flat-spread \
15652             unwrap_or(5) fold (got {})",
15653            view.max_restarts(),
15654        );
15655        assert!(
15656            c.max_restarts().is_none(),
15657            "Caixa::max_restarts() must remain None on the author-\
15658             omitted arm — the supervisor_view fold must not mutate \
15659             the outer flat-spread presence bit",
15660        );
15661    }
15662
15663    #[test]
15664    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
15665        // Value-shape pin: [`Caixa::restart_window`] returns the
15666        // `:restart-window` typed `Option<String>` verbatim as an
15667        // `Option<&str>`, borrowed from the typed slot's own storage,
15668        // byte-equal across the author-omitted `None` arm and each of
15669        // the representative fixtures in the accept-set — the canonical
15670        // `"60s"` from `{intensity, 5, 60}`, the sibling
15671        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
15672        // / `"0s"`) the shared codec's positive-set sweep pin covers,
15673        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
15674        // seconds drift the sibling [`Self::validate_restart_window`]
15675        // gate refuses; the accessor must ship the raw slot verbatim
15676        // so struct-literal fixtures continue to expose the drift at
15677        // the accessor boundary). Third outer top-level [`Caixa`]
15678        // supervisor-tree flat-spread pin — extends the sub-family onto
15679        // the sibling `Option<&str>` raw-duration-string arm.
15680        for window in [
15681            None,
15682            Some("60s"),
15683            Some("5m"),
15684            Some("1h"),
15685            Some("500ms"),
15686            Some("1.5s"),
15687            Some(""),
15688        ] {
15689            let c = caixa_with_restart_window(window);
15690            assert_eq!(
15691                c.restart_window(),
15692                window,
15693                "Caixa::restart_window must return :restart-window \
15694                 verbatim as Option<&str> (got {:?}, expected {window:?})",
15695                c.restart_window(),
15696            );
15697            assert_eq!(
15698                c.restart_window(),
15699                c.restart_window.as_deref(),
15700                "Caixa::restart_window accessor and \
15701                 self.restart_window.as_deref() field access must \
15702                 byte-equal — a byte-level drift would silently split \
15703                 the paired Caixa::declared_supervisor_slots \
15704                 presence-probe arm from the \
15705                 Caixa::validate_restart_window shared-codec gate and \
15706                 the Caixa::supervisor_view soft-swallowing fold",
15707            );
15708        }
15709    }
15710
15711    #[test]
15712    fn restart_window_projects_slice_by_borrow() {
15713        // The by-borrow pin: [`Caixa::restart_window`] returns
15714        // `Option<&str>` by borrow — the returned string slice borrows
15715        // the underlying `Option<String>` storage of the `:restart-window`
15716        // slot and the accessor must not clone on every call. Peer of
15717        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
15718        // by-borrow pins on the universal-axis scalar family
15719        // (`licenca_projects_option_ref_by_borrow` /
15720        // `descricao_projects_option_ref_by_borrow` and siblings) —
15721        // extended onto the M2 supervisor-tree flat-spread
15722        // `Option<&str>` raw-duration-string axis.
15723        for window in [None, Some("60s"), Some("5m"), Some("")] {
15724            let c = caixa_with_restart_window(window);
15725            let first = c.restart_window();
15726            let second = c.restart_window();
15727            assert_eq!(
15728                first, second,
15729                "Caixa::restart_window must be idempotent — two \
15730                 successive calls on the same &self must return the \
15731                 same Option<&str>",
15732            );
15733            if let (Some(a), Some(b)) = (first, second) {
15734                assert_eq!(
15735                    a.as_ptr(),
15736                    b.as_ptr(),
15737                    "Caixa::restart_window must borrow the underlying \
15738                     String storage — two successive Some-arm calls must \
15739                     return slices with the same backing pointer (a fresh \
15740                     String clone would change the pointer on every call)",
15741                );
15742            }
15743            assert_eq!(
15744                first, window,
15745                "Caixa::restart_window must return :restart-window \
15746                 verbatim by borrow — got {first:?}, expected {window:?}",
15747            );
15748        }
15749    }
15750
15751    #[test]
15752    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
15753        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15754        // `:restart-window` presence-probe arm must key off
15755        // [`Caixa::restart_window`], not the raw
15756        // `self.restart_window.is_some()` field-probe. Structurally:
15757        // every `Caixa { restart_window: Some(_), .. }` must push
15758        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
15759        // list, and a `Caixa { restart_window: None, .. }` must NOT
15760        // push the label. Peer of the sibling
15761        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
15762        // routing pin.
15763        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
15764            let c = caixa_with_restart_window(Some(window));
15765            let slots = c.declared_supervisor_slots();
15766            assert!(
15767                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
15768                "declared_supervisor_slots must push \
15769                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
15770                 `:restart-window` is Some({window:?}) — the accessor \
15771                 and the enumerator gate must route through the same \
15772                 substrate-primitive typed dispatch on the outer \
15773                 :restart-window presence bit (got slots={slots:?})",
15774            );
15775        }
15776        let c = caixa_with_restart_window(None);
15777        let slots = c.declared_supervisor_slots();
15778        assert!(
15779            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
15780            "declared_supervisor_slots must NOT push \
15781             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
15782             is None — the author-omitted arm must route through the \
15783             accessor's None-return unchanged (got slots={slots:?})",
15784        );
15785    }
15786
15787    #[test]
15788    fn validate_restart_window_arm_routes_through_accessor() {
15789        // Composition pin: [`Caixa::validate_restart_window`]'s
15790        // shared-codec fold arm must key off [`Caixa::restart_window`],
15791        // not the raw `self.restart_window.as_deref()` field-projection.
15792        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
15793        // express no reset" canonical shape); (2) a canonical `Some`
15794        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
15795        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
15796        // .. })` carrying the offending raw string verbatim. The three
15797        // arms jointly pin that the validator's raw-string binding is
15798        // the accessor's return, not a peer projection — any future
15799        // silent detour that had the accessor collapse `Some("")` to
15800        // `None` would silently absorb the empty-after-trim refusal
15801        // case at the accessor boundary.
15802        caixa_with_restart_window(None)
15803            .validate_restart_window()
15804            .expect("None :restart-window must validate through the accessor");
15805        caixa_with_restart_window(Some("60s"))
15806            .validate_restart_window()
15807            .expect("canonical :restart-window \"60s\" must validate through the accessor");
15808        let err = caixa_with_restart_window(Some("1.5s"))
15809            .validate_restart_window()
15810            .expect_err("fractional-seconds :restart-window must fail through the accessor");
15811        assert!(
15812            matches!(
15813                err,
15814                ManifestError::RestartWindowMalformed { ref restart_window, .. }
15815                    if restart_window == "1.5s"
15816            ),
15817            "validator must carry the offending raw string verbatim \
15818             from the accessor's borrowed &str (got {err:?})",
15819        );
15820    }
15821
15822    #[test]
15823    fn supervisor_view_restart_window_arm_routes_through_accessor() {
15824        // Composition pin: [`Caixa::supervisor_view`]'s
15825        // per-`:restart-window` [`SupervisorSpec`] construction arm
15826        // must key off [`Caixa::restart_window`]'s soft-swallowing
15827        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
15828        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
15829        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
15830        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
15831        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
15832        // (the shared codec's canonical parse); (3) codec-rejected
15833        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
15834        // (the soft-swallow preserving the view's best-effort shape).
15835        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
15836        let view = c.supervisor_view().expect("Supervisor kind has a view");
15837        assert_eq!(
15838            view.restart_window(),
15839            None,
15840            "supervisor_view must project outer None :restart-window \
15841             onto None on the composed SupervisorSpec (never-reset \
15842             sentinel) through the accessor's None-return unchanged",
15843        );
15844
15845        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
15846        let view = c.supervisor_view().expect("Supervisor kind has a view");
15847        assert_eq!(
15848            view.restart_window(),
15849            Some(std::time::Duration::from_secs(60)),
15850            "supervisor_view must fold outer Some(\"60s\") through the \
15851             shared duration_codec into Duration::from_secs(60) on the \
15852             composed SupervisorSpec (accessor's Some(&str) → codec \
15853             parse → Some(Duration))",
15854        );
15855
15856        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
15857        let view = c.supervisor_view().expect("Supervisor kind has a view");
15858        assert_eq!(
15859            view.restart_window(),
15860            None,
15861            "supervisor_view must soft-swallow the shared-codec parse \
15862             failure to None (the view's best-effort shape the sibling \
15863             manifest-level validate_restart_window surfaces as \
15864             RestartWindowMalformed); the accessor's raw-string return \
15865             is the single input every downstream consumer keys off",
15866        );
15867    }
15868
15869    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
15870
15871    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
15872        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15873        c.upgrade_from = upgrade_from;
15874        c
15875    }
15876
15877    #[test]
15878    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
15879        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
15880        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
15881        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
15882        // typed `Vec<UpgradeFromEntry>` verbatim as a
15883        // `&[UpgradeFromEntry]` slice-view over the same backing
15884        // buffer the raw `self.upgrade_from.as_slice()` field access
15885        // borrows from, element-equal across every representative
15886        // fixture in the accept-set — `[]` (the "no hot-upgrade path
15887        // declared" arm every `defcaixa` without an `:upgrade-from`
15888        // block carries; `#[serde(default)]` folds an omitted slot
15889        // onto `Vec::new()`), a canonical single-entry `Restart`
15890        // fixture (the shape most Servicos carry — a single prior
15891        // version with the fallback strategy), a canonical multi-
15892        // entry list carrying every typed instruction variant
15893        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
15894        // `Restart`), and a past-the-guard sentinel — a duplicate-
15895        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
15896        // ([`crate::upgrade::validate_upgrade_from`] rejects through
15897        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
15898        // ship the raw slot verbatim so struct-literal fixtures
15899        // continue to expose the duplicate at the accessor boundary).
15900        //
15901        // Pins against a future silent detour that returned an owned
15902        // `Vec<UpgradeFromEntry>` (which would type-check but silently
15903        // clone on every accessor call, breaking the zero-cost
15904        // projection every peer sibling slice accessor carries), a
15905        // `[dup, dup] → [dup]` dedup collapse (which would silently
15906        // absorb the `DuplicateFrom` refusal case at the accessor
15907        // boundary and the [`crate::StandardLayout::verify`] cross-
15908        // entry gate would silently accept a struct-literal `Caixa`
15909        // carrying the drift), a reference to an operator-resolved
15910        // overlay (the future per-cluster `:upgrade-overrides` slot
15911        // — its resolution must land at exactly this accessor body,
15912        // not silently divert the raw slot away from a second
15913        // consumer), or an axis-shuffled projection (a future detour
15914        // that reordered entries through the accessor would silently
15915        // split the paired [`crate::StandardLayout::verify`] per-
15916        // `:upgrade-from` shape gate's traversal input from the peer
15917        // [`crate::render::servico_m2_overlay`] emitter's projection
15918        // input, since the operator's hot-upgrade dispatch matches
15919        // per-`:from` and axis reordering would silently split the
15920        // per-entry script-path existence probe's iteration order
15921        // from the M2 overlay emitter's serialized-entry order).
15922        //
15923        // First outer top-level [`Caixa`] `&[Composite]`-return
15924        // slice accessor pin on the substrate primitive for M2 / M3
15925        // typed-slot vec-carry axes — opens the outer-`Caixa`
15926        // `&[Composite]` composite-slice projection pattern the
15927        // sibling `:children` [`crate::supervisor::ChildSpec`] /
15928        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
15929        // [`crate::aplicacao::WitContract`] future outer-composite-
15930        // slice pins fold on. Peer of the closed outer-`Caixa`
15931        // scalar `Option<&Composite>` composite-reference family the
15932        // sibling `limits` / `behavior` / `politicas` / `placement`
15933        // / `entrada` `..._returns_..._option_ref_verbatim_across_
15934        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
15935        // the "byte-equal, borrow-shared" outer-accessor discipline
15936        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
15937        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15938        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
15939            vec![],
15940            vec![UpgradeFromEntry {
15941                from: "0.0.1".into(),
15942                instructions: vec![UpgradeInstruction::Restart],
15943            }],
15944            vec![
15945                UpgradeFromEntry {
15946                    from: "0.0.1".into(),
15947                    instructions: vec![
15948                        UpgradeInstruction::LoadModule {
15949                            module: "demo".into(),
15950                        },
15951                        UpgradeInstruction::SoftPurge {
15952                            module: "demo".into(),
15953                        },
15954                    ],
15955                },
15956                UpgradeFromEntry {
15957                    from: "0.0.2".into(),
15958                    instructions: vec![
15959                        UpgradeInstruction::StateChange {
15960                            script: "servicos/upgrade.lisp".into(),
15961                        },
15962                        UpgradeInstruction::Purge {
15963                            module: "demo".into(),
15964                        },
15965                        UpgradeInstruction::Restart,
15966                    ],
15967                },
15968            ],
15969            vec![
15970                UpgradeFromEntry {
15971                    from: "0.1.0".into(),
15972                    instructions: vec![UpgradeInstruction::Restart],
15973                },
15974                UpgradeFromEntry {
15975                    from: "0.1.0".into(),
15976                    instructions: vec![UpgradeInstruction::Restart],
15977                },
15978            ],
15979        ];
15980        for upgrade_from in fixtures {
15981            let c = caixa_with_upgrade_from(upgrade_from.clone());
15982            assert_eq!(
15983                c.upgrade_from(),
15984                upgrade_from.as_slice(),
15985                "Caixa::upgrade_from must return :upgrade-from \
15986                 verbatim (got {:?}, expected {upgrade_from:?})",
15987                c.upgrade_from(),
15988            );
15989            assert_eq!(
15990                c.upgrade_from(),
15991                c.upgrade_from.as_slice(),
15992                "Caixa::upgrade_from must element-equal the raw \
15993                 `self.upgrade_from.as_slice()` field access across \
15994                 every value in the Vec<UpgradeFromEntry> accept-set",
15995            );
15996            assert_eq!(
15997                c.upgrade_from().is_empty(),
15998                c.upgrade_from.is_empty(),
15999                "Caixa::upgrade_from().is_empty() must byte-equal \
16000                 self.upgrade_from.is_empty() — a presence-bit drift \
16001                 would silently split the paired \
16002                 Caixa::declared_servico_slots M2 declared-slot \
16003                 enumerator's presence probe from the peer \
16004                 crate::render::servico_m2_overlay M2 overlay \
16005                 emitter's presence gate",
16006            );
16007        }
16008    }
16009
16010    #[test]
16011    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
16012        // Composition pin: [`Caixa::declared_servico_slots`]'s
16013        // `:upgrade-from` presence-probe arm must key off
16014        // [`Caixa::upgrade_from`], not the raw
16015        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
16016        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
16017        // instructions: vec![Restart] }], .. }` must push
16018        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
16019        // (the presence bit is non-empty, so the M2 kind-coherence
16020        // gate must surface the slot as "declared"), and a `Caixa {
16021        // upgrade_from: vec![], .. }` must NOT push the label (the
16022        // "author omitted the slot entirely" arm — the empty-slice
16023        // partition the serde-default folds onto). The pair jointly
16024        // pins the accessor + declared-slot enumerator composition:
16025        // any future silent detour that had the accessor collapse
16026        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
16027        // is_empty())` projection) would silently absorb the
16028        // "declared but degenerate" arm at the accessor boundary and
16029        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
16030        // coherence gate would silently accept a struct-literal
16031        // `Caixa` carrying the drift.
16032        //
16033        // Peer of the sibling
16034        // `declared_servico_slots_limits_arm_routes_through_accessor`
16035        // (b2bd9d7) and
16036        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16037        // (35d8b52) composition pins on the sibling `:limits` /
16038        // `:behavior` outer-`Option<&Composite>` arms — same "the
16039        // enumerator gate must route through the substrate-primitive
16040        // typed dispatch" discipline extended onto the third M2
16041        // Servico-runtime slot axis, closing the enumerator's routing
16042        // invariant on every M2 arm.
16043        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16044        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
16045            from: "0.0.1".into(),
16046            instructions: vec![UpgradeInstruction::Restart],
16047        }]);
16048        let slots = c.declared_servico_slots();
16049        assert!(
16050            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
16051            "declared_servico_slots must push \
16052             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
16053             non-empty — the accessor and the enumerator gate must \
16054             route through the same substrate-primitive typed \
16055             dispatch on the outer :upgrade-from presence bit (got \
16056             slots={slots:?})",
16057        );
16058        let c = caixa_with_upgrade_from(vec![]);
16059        let slots = c.declared_servico_slots();
16060        assert!(
16061            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
16062            "declared_servico_slots must NOT push \
16063             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
16064             empty — the author-omitted arm must route through the \
16065             accessor's empty-slice return unchanged (got \
16066             slots={slots:?})",
16067        );
16068    }
16069
16070    #[test]
16071    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
16072        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16073        // per-`:upgrade-from` M2 overlay emit arm must key off
16074        // [`Caixa::upgrade_from`], not the raw
16075        // `!caixa.upgrade_from.is_empty()` presence gate + the
16076        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
16077        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
16078        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
16079        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
16080        // sequence in the overlay (the emitter fans onto the serde
16081        // slice-serialization), and a `Caixa { upgrade_from: vec![],
16082        // .. }` must omit the key entirely (the empty-slice
16083        // partition — the `!.is_empty()` outer gate elides the key
16084        // when the author omitted the slot). The pair jointly pins
16085        // the accessor + M2 overlay emitter composition: any future
16086        // silent detour that had the accessor return a fresh-cloned
16087        // `Vec<UpgradeFromEntry>` copy would silently break the
16088        // reference-identity pin the peer per-entry
16089        // `serde_yaml::to_value(caixa.upgrade_from())` projection
16090        // reads from — the projection would clone once per accessor
16091        // call instead of borrowing the storage buffer verbatim.
16092        //
16093        // Peer of the sibling
16094        // `servico_m2_overlay_limits_arm_routes_through_accessor`
16095        // (b2bd9d7) and
16096        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
16097        // (35d8b52) composition pins on the sibling `:limits` /
16098        // `:behavior` outer-`Option<&Composite>` arms — same "the
16099        // M2 overlay emitter must route through the substrate-
16100        // primitive typed dispatch" discipline extended onto the
16101        // third M2 Servico-runtime slot axis, closing the overlay
16102        // emitter's routing invariant on every M2 arm.
16103        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
16104        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16105        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
16106            from: "0.0.1".into(),
16107            instructions: vec![UpgradeInstruction::Restart],
16108        }]);
16109        let overlay = servico_m2_overlay(&c).unwrap();
16110        assert!(
16111            overlay.contains_key(M2_KEY_UPGRADE_FROM),
16112            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
16113             `:upgrade-from` is non-empty — the accessor and the M2 \
16114             overlay emitter must route through the same substrate- \
16115             primitive typed dispatch on the outer :upgrade-from \
16116             slice (got overlay={overlay:?})",
16117        );
16118        let c = caixa_with_upgrade_from(vec![]);
16119        let overlay = servico_m2_overlay(&c).unwrap();
16120        assert!(
16121            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
16122            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
16123             `:upgrade-from` is empty — the empty-slice partition \
16124             must route through the accessor's empty-slice return \
16125             unchanged (got overlay={overlay:?})",
16126        );
16127    }
16128
16129    #[test]
16130    fn upgrade_from_projects_slice_by_borrow() {
16131        // The by-borrow pin: [`Caixa::upgrade_from`] returns
16132        // `&[UpgradeFromEntry]` by borrow — the returned slice
16133        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
16134        // the `:upgrade-from` slot and the accessor must not clone
16135        // the backing `Vec` on every call. Peer of the sibling
16136        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
16137        // (`autores_projects_slice_by_borrow` b5d813f,
16138        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16139        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16140        // `exe_projects_slice_by_borrow` 65d9527,
16141        // `servicos_projects_slice_by_borrow` 611f78b,
16142        // `deps_projects_slice_by_borrow` ad34b4e,
16143        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
16144        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
16145        // axes — extended here to the first outer-`Caixa`
16146        // composite-element `&[Composite]` axis: the accessor's
16147        // returned slice must borrow from `&self` (the returned
16148        // reference's lifetime is tied to `&self`), and calling the
16149        // accessor twice on the same [`Caixa`] must yield slices
16150        // that are pointer-equal (the underlying byte-buffer is the
16151        // storage `Vec`'s allocation, not a fresh copy) as well as
16152        // value-equal (idempotent, no side effects on `&self`).
16153        //
16154        // Pins against a future silent detour that returned an owned
16155        // `Vec<UpgradeFromEntry>` (which would type-check but
16156        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
16157        // return (which would leak the backing `Vec`'s
16158        // grow/push/reserve surface no downstream consumer reaches
16159        // for), or a one-arm-only accessor that returned a
16160        // saturating value on some sentinel input.
16161        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16162        for upgrade_from in [
16163            vec![],
16164            vec![UpgradeFromEntry {
16165                from: "0.0.1".into(),
16166                instructions: vec![UpgradeInstruction::Restart],
16167            }],
16168            vec![
16169                UpgradeFromEntry {
16170                    from: "0.0.1".into(),
16171                    instructions: vec![UpgradeInstruction::Restart],
16172                },
16173                UpgradeFromEntry {
16174                    from: "0.0.2".into(),
16175                    instructions: vec![UpgradeInstruction::SoftPurge {
16176                        module: "demo".into(),
16177                    }],
16178                },
16179            ],
16180        ] {
16181            let c = caixa_with_upgrade_from(upgrade_from.clone());
16182            let first = c.upgrade_from();
16183            let second = c.upgrade_from();
16184            assert_eq!(
16185                first, second,
16186                "Caixa::upgrade_from must be idempotent — two \
16187                 successive calls on the same &self must return the \
16188                 same &[UpgradeFromEntry]",
16189            );
16190            assert_eq!(
16191                first.as_ptr(),
16192                second.as_ptr(),
16193                "Caixa::upgrade_from must borrow the underlying \
16194                 Vec<UpgradeFromEntry> storage — two successive calls \
16195                 must return slices with the same backing pointer (a \
16196                 fresh Vec<UpgradeFromEntry> clone would change the \
16197                 pointer on every call)",
16198            );
16199            assert_eq!(
16200                first,
16201                upgrade_from.as_slice(),
16202                "Caixa::upgrade_from must return :upgrade-from \
16203                 verbatim by borrow — got {first:?}, expected \
16204                 {upgrade_from:?}",
16205            );
16206        }
16207    }
16208
16209    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
16210
16211    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
16212        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16213        c.children = children;
16214        c
16215    }
16216
16217    #[test]
16218    fn children_returns_children_slice_verbatim_across_permutations() {
16219        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
16220        // outer-composite `&[ChildSpec]`-return slice-shape pin:
16221        // [`Caixa::children`] must return the `:children` typed
16222        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
16223        // the same backing buffer the raw `self.children.as_slice()`
16224        // field access borrows from, element-equal across every
16225        // representative fixture in the accept-set — `[]` (the "no
16226        // static children declared" arm every non-`Supervisor`-kind
16227        // `defcaixa` carries by `#[serde(default)]` and every
16228        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
16229        // a canonical single-child `Permanent` fixture (the shape
16230        // most `OneForOne` supervisors carry — a single long-running
16231        // worker child), a canonical multi-child list carrying every
16232        // typed restart-policy variant (`Permanent` / `Transient` /
16233        // `Temporary`), and a past-the-guard sentinel — a duplicate
16234        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
16235        // ([`crate::SupervisorSpec::validate`] rejects through
16236        // `DuplicateChildNome { nome: "w" }` but the accessor must
16237        // ship the raw slot verbatim so struct-literal fixtures
16238        // continue to expose the duplicate at the accessor boundary).
16239        //
16240        // Pins against a future silent detour that returned an owned
16241        // `Vec<ChildSpec>` (which would type-check but silently clone
16242        // on every accessor call, breaking the zero-cost projection
16243        // every peer sibling slice accessor carries), a `[dup, dup] →
16244        // [dup]` dedup collapse (which would silently absorb the
16245        // `DuplicateChildNome` refusal case at the accessor boundary
16246        // and the [`crate::StandardLayout::verify`] cross-child gate
16247        // would silently accept a struct-literal `Caixa` carrying the
16248        // drift), a reference to an operator-resolved overlay (the
16249        // future per-cluster `:children-overrides` slot — its
16250        // resolution must land at exactly this accessor body, not
16251        // silently divert the raw slot away from a second consumer),
16252        // or an axis-shuffled projection (a future detour that
16253        // reordered children through the accessor would silently
16254        // split the paired [`crate::StandardLayout::verify`] per-
16255        // supervisor gate's traversal input from the peer
16256        // [`Self::supervisor_view`] fold-in path's clone-order input,
16257        // since the OTP `RestForOne` restart strategy dispatches on
16258        // declared child order and axis reordering would silently
16259        // split the operator's per-cluster restart-fan-out order
16260        // from the caixa.lisp source-order).
16261        //
16262        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
16263        // accessor pin on the substrate primitive for M2 / M3 typed-
16264        // slot vec-carry axes — folds on the outer-`Caixa`
16265        // `&[Composite]` composite-slice sub-family the sibling
16266        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16267        // (2a1f907) pin opened, peer at the outer altitude of the
16268        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
16269        // (bc92bce) accessor on the same OTP-supervisor static-child-
16270        // list axis.
16271        use crate::supervisor::{ChildSpec, RestartPolicy};
16272        let fixtures: Vec<Vec<ChildSpec>> = vec![
16273            vec![],
16274            vec![ChildSpec {
16275                caixa: "worker".into(),
16276                versao: "^0.1".into(),
16277                restart: RestartPolicy::Permanent,
16278            }],
16279            vec![
16280                ChildSpec {
16281                    caixa: "worker-a".into(),
16282                    versao: "^0.1".into(),
16283                    restart: RestartPolicy::Permanent,
16284                },
16285                ChildSpec {
16286                    caixa: "worker-b".into(),
16287                    versao: "^0.1".into(),
16288                    restart: RestartPolicy::Transient,
16289                },
16290                ChildSpec {
16291                    caixa: "worker-c".into(),
16292                    versao: "^0.1".into(),
16293                    restart: RestartPolicy::Temporary,
16294                },
16295            ],
16296            vec![
16297                ChildSpec {
16298                    caixa: "w".into(),
16299                    versao: "^0.1".into(),
16300                    restart: RestartPolicy::Permanent,
16301                },
16302                ChildSpec {
16303                    caixa: "w".into(),
16304                    versao: "^0.1".into(),
16305                    restart: RestartPolicy::Permanent,
16306                },
16307            ],
16308        ];
16309        for children in fixtures {
16310            let c = caixa_with_children(children.clone());
16311            assert_eq!(
16312                c.children(),
16313                children.as_slice(),
16314                "Caixa::children must return :children verbatim \
16315                 (got {:?}, expected {children:?})",
16316                c.children(),
16317            );
16318            assert_eq!(
16319                c.children(),
16320                c.children.as_slice(),
16321                "Caixa::children must element-equal the raw \
16322                 `self.children.as_slice()` field access across \
16323                 every value in the Vec<ChildSpec> accept-set",
16324            );
16325            assert_eq!(
16326                c.children().is_empty(),
16327                c.children.is_empty(),
16328                "Caixa::children().is_empty() must byte-equal \
16329                 self.children.is_empty() — a presence-bit drift \
16330                 would silently split the paired \
16331                 Caixa::declared_supervisor_slots supervisor-tree \
16332                 declared-slot enumerator's presence probe from the \
16333                 peer Caixa::supervisor_view typed-view composer's \
16334                 fold-in path",
16335            );
16336        }
16337    }
16338
16339    #[test]
16340    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
16341        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16342        // `:children` presence-probe arm must key off
16343        // [`Caixa::children`], not the raw
16344        // `!self.children.is_empty()` field-probe. Structurally: a
16345        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
16346        // "^0.1", restart: Permanent }], .. }` must push
16347        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
16348        // (the presence bit is non-empty, so the supervisor-tree
16349        // kind-coherence gate must surface the slot as "declared"),
16350        // and a `Caixa { children: vec![], .. }` must NOT push the
16351        // label (the "author omitted the slot entirely" arm — the
16352        // empty-slice partition the serde-default folds onto). The
16353        // pair jointly pins the accessor + declared-slot enumerator
16354        // composition: any future silent detour that had the accessor
16355        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
16356        // "__reserved__")` projection) would silently absorb the
16357        // "declared but degenerate" arm at the accessor boundary and
16358        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
16359        // kind-coherence gate would silently accept a struct-literal
16360        // `Caixa` carrying the drift.
16361        //
16362        // Peer of the sibling
16363        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16364        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
16365        // same "the enumerator gate must route through the substrate-
16366        // primitive typed dispatch" discipline extended onto the
16367        // supervisor-tree `:children` composite-slice arm.
16368        use crate::supervisor::{ChildSpec, RestartPolicy};
16369        let c = caixa_with_children(vec![ChildSpec {
16370            caixa: "w".into(),
16371            versao: "^0.1".into(),
16372            restart: RestartPolicy::Permanent,
16373        }]);
16374        let slots = c.declared_supervisor_slots();
16375        assert!(
16376            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
16377            "declared_supervisor_slots must push \
16378             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
16379             non-empty — the accessor and the enumerator gate must \
16380             route through the same substrate-primitive typed \
16381             dispatch on the outer :children presence bit (got \
16382             slots={slots:?})",
16383        );
16384        let c = caixa_with_children(vec![]);
16385        let slots = c.declared_supervisor_slots();
16386        assert!(
16387            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
16388            "declared_supervisor_slots must NOT push \
16389             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
16390             empty — the author-omitted arm must route through the \
16391             accessor's empty-slice return unchanged (got \
16392             slots={slots:?})",
16393        );
16394    }
16395
16396    #[test]
16397    fn supervisor_view_children_arm_routes_through_accessor() {
16398        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
16399        // fold-in arm must key off [`Caixa::children`], not the raw
16400        // `self.children.clone()` field-clone. Structurally: a `Caixa {
16401        // kind: Supervisor, estrategia: Some(OneForOne), children:
16402        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
16403        // per-child list through the accessor into the typed
16404        // [`SupervisorSpec`] view's `children` field verbatim — every
16405        // entry the accessor surfaces must land in the view's
16406        // `children` slot in the same order. The pair jointly pins the
16407        // accessor + view-composer composition: any future silent
16408        // detour that had the accessor return a fresh-cloned
16409        // `Vec<ChildSpec>` copy would silently break the reference-
16410        // identity pin the peer `supervisor_view` fold-in path reads
16411        // from — the fold would clone once more per accessor call
16412        // instead of borrowing the storage buffer verbatim once.
16413        //
16414        // Peer of the sibling
16415        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
16416        // family) composition pin on the peer kind-gate arm — same
16417        // "the view composer must route through the substrate-
16418        // primitive typed dispatch" discipline extended onto the
16419        // per-`:children` fold-in arm, closing the supervisor-view
16420        // composer's routing invariant on the composite-slice input.
16421        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16422        let mut c = caixa_with_children(vec![
16423            ChildSpec {
16424                caixa: "worker-a".into(),
16425                versao: "^0.1".into(),
16426                restart: RestartPolicy::Permanent,
16427            },
16428            ChildSpec {
16429                caixa: "worker-b".into(),
16430                versao: "^0.1".into(),
16431                restart: RestartPolicy::Transient,
16432            },
16433        ]);
16434        c.kind = crate::CaixaKind::Supervisor;
16435        c.estrategia = Some(RestartStrategy::OneForOne);
16436        let view = c
16437            .supervisor_view()
16438            .expect("Supervisor kind must produce a supervisor_view");
16439        assert_eq!(
16440            view.children(),
16441            c.children(),
16442            "supervisor_view must fold Caixa::children verbatim into \
16443             SupervisorSpec::children — the accessor and the view \
16444             composer must route through the same substrate-primitive \
16445             typed dispatch on the outer :children slice (got view \
16446             children={:?}, expected {:?})",
16447            view.children(),
16448            c.children(),
16449        );
16450    }
16451
16452    #[test]
16453    fn children_projects_slice_by_borrow() {
16454        // The by-borrow pin: [`Caixa::children`] returns
16455        // `&[ChildSpec]` by borrow — the returned slice borrows the
16456        // underlying `Vec<ChildSpec>` storage of the `:children` slot
16457        // and the accessor must not clone the backing `Vec` on every
16458        // call. Peer of the sibling outer top-level [`Caixa`]
16459        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
16460        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
16461        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16462        // `exe_projects_slice_by_borrow` 65d9527,
16463        // `servicos_projects_slice_by_borrow` 611f78b,
16464        // `deps_projects_slice_by_borrow` ad34b4e,
16465        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16466        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
16467        // sibling outer top-level [`Caixa`] scalar-element and
16468        // composite-element `&[T]` axes — folds on the outer-`Caixa`
16469        // composite-element `&[Composite]` axis: the accessor's
16470        // returned slice must borrow from `&self` (the returned
16471        // reference's lifetime is tied to `&self`), and calling the
16472        // accessor twice on the same [`Caixa`] must yield slices
16473        // that are pointer-equal (the underlying byte-buffer is the
16474        // storage `Vec`'s allocation, not a fresh copy) as well as
16475        // value-equal (idempotent, no side effects on `&self`).
16476        //
16477        // Pins against a future silent detour that returned an owned
16478        // `Vec<ChildSpec>` (which would type-check but silently clone
16479        // on every call), a `&Vec<ChildSpec>` return (which would leak
16480        // the backing `Vec`'s grow/push/reserve surface no downstream
16481        // consumer reaches for), or a one-arm-only accessor that
16482        // returned a saturating value on some sentinel input.
16483        use crate::supervisor::{ChildSpec, RestartPolicy};
16484        for children in [
16485            vec![],
16486            vec![ChildSpec {
16487                caixa: "w".into(),
16488                versao: "^0.1".into(),
16489                restart: RestartPolicy::Permanent,
16490            }],
16491            vec![
16492                ChildSpec {
16493                    caixa: "worker-a".into(),
16494                    versao: "^0.1".into(),
16495                    restart: RestartPolicy::Permanent,
16496                },
16497                ChildSpec {
16498                    caixa: "worker-b".into(),
16499                    versao: "^0.1".into(),
16500                    restart: RestartPolicy::Transient,
16501                },
16502            ],
16503        ] {
16504            let c = caixa_with_children(children.clone());
16505            let first = c.children();
16506            let second = c.children();
16507            assert_eq!(
16508                first, second,
16509                "Caixa::children must be idempotent — two successive \
16510                 calls on the same &self must return the same \
16511                 &[ChildSpec]",
16512            );
16513            assert_eq!(
16514                first.as_ptr(),
16515                second.as_ptr(),
16516                "Caixa::children must borrow the underlying \
16517                 Vec<ChildSpec> storage — two successive calls must \
16518                 return slices with the same backing pointer (a fresh \
16519                 Vec<ChildSpec> clone would change the pointer on \
16520                 every call)",
16521            );
16522            assert_eq!(
16523                first,
16524                children.as_slice(),
16525                "Caixa::children must return :children verbatim by \
16526                 borrow — got {first:?}, expected {children:?}",
16527            );
16528        }
16529    }
16530
16531    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
16532
16533    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
16534        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16535        c.kind = CaixaKind::Aplicacao;
16536        c.membros = membros;
16537        c
16538    }
16539
16540    #[test]
16541    fn membros_returns_membros_slice_verbatim_across_permutations() {
16542        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
16543        // composite `&[Membro]`-return slice-shape pin:
16544        // [`Caixa::membros`] must return the `:membros` typed
16545        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
16546        // same backing buffer the raw `self.membros.as_slice()` field
16547        // access borrows from, element-equal across every
16548        // representative fixture in the accept-set — `[]` (the "no
16549        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
16550        // carries by `#[serde(default)]` and every partially-authored
16551        // Aplicacao carries before the
16552        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
16553        // canonical single-member fixture (the shape a minimal
16554        // Aplicacao carries — one Servico wrapping one contained
16555        // computation), a canonical multi-member list carrying three
16556        // distinct entries (the canonical checkout-shape Aplicacao —
16557        // cart / pricing / auth — every canonical example carries), and
16558        // a past-the-guard sentinel — a duplicate `:caixa`
16559        // `[("cart", ...), ("cart", ...)]` entry pair
16560        // ([`crate::AplicacaoSpec::validate`] rejects through
16561        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
16562        // the raw slot verbatim so struct-literal fixtures continue to
16563        // expose the duplicate at the accessor boundary).
16564        //
16565        // Pins against a future silent detour that returned an owned
16566        // `Vec<Membro>` (which would type-check but silently clone on
16567        // every accessor call, breaking the zero-cost projection every
16568        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
16569        // dedup collapse (which would silently absorb the
16570        // `DuplicateMembro` refusal case at the accessor boundary and
16571        // the [`crate::StandardLayout::verify`] cross-member gate would
16572        // silently accept a struct-literal `Caixa` carrying the drift),
16573        // a reference to an operator-resolved overlay (the future per-
16574        // cluster `:membros-overrides` slot — its resolution must land
16575        // at exactly this accessor body, not silently divert the raw
16576        // slot away from a second consumer), or an axis-shuffled
16577        // projection (a future detour that reordered members through
16578        // the accessor would silently split the paired
16579        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
16580        // traversal input from the peer [`Self::aplicacao_view`] fold-
16581        // in path's clone-order input, since the canonical `:contratos`
16582        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
16583        // read the member set through the same slice).
16584        //
16585        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
16586        // accessor pin on the substrate primitive for M2 / M3 typed-
16587        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
16588        // arm of the `&[Composite]` composite-slice sub-family the
16589        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16590        // (2a1f907) and
16591        // `children_returns_children_slice_verbatim_across_permutations`
16592        // (c17b51e) pins opened, peer at the outer altitude of the
16593        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
16594        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
16595        // list axis.
16596        use crate::aplicacao::Membro;
16597        let fixtures: Vec<Vec<Membro>> = vec![
16598            vec![],
16599            vec![Membro {
16600                caixa: "cart".into(),
16601                versao: "^0.1".into(),
16602            }],
16603            vec![
16604                Membro {
16605                    caixa: "cart".into(),
16606                    versao: "^0.1".into(),
16607                },
16608                Membro {
16609                    caixa: "pricing".into(),
16610                    versao: "^0.2".into(),
16611                },
16612                Membro {
16613                    caixa: "auth".into(),
16614                    versao: "^1.0".into(),
16615                },
16616            ],
16617            vec![
16618                Membro {
16619                    caixa: "cart".into(),
16620                    versao: "^0.1".into(),
16621                },
16622                Membro {
16623                    caixa: "cart".into(),
16624                    versao: "^0.1".into(),
16625                },
16626            ],
16627        ];
16628        for membros in fixtures {
16629            let c = caixa_aplicacao_with_membros(membros.clone());
16630            assert_eq!(
16631                c.membros(),
16632                membros.as_slice(),
16633                "Caixa::membros must return :membros verbatim \
16634                 (got {:?}, expected {membros:?})",
16635                c.membros(),
16636            );
16637            assert_eq!(
16638                c.membros(),
16639                c.membros.as_slice(),
16640                "Caixa::membros must element-equal the raw \
16641                 `self.membros.as_slice()` field access across every \
16642                 value in the Vec<Membro> accept-set",
16643            );
16644            assert_eq!(
16645                c.membros().is_empty(),
16646                c.membros.is_empty(),
16647                "Caixa::membros().is_empty() must byte-equal \
16648                 self.membros.is_empty() — a presence-bit drift would \
16649                 silently split the paired Caixa::declared_mesh_slots \
16650                 mesh declared-slot enumerator's presence probe from \
16651                 the peer Caixa::aplicacao_view typed-view composer's \
16652                 fold-in path",
16653            );
16654        }
16655    }
16656
16657    #[test]
16658    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
16659        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
16660        // presence-probe arm must key off [`Caixa::membros`], not the
16661        // raw `!self.membros.is_empty()` field-probe. Structurally: a
16662        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
16663        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
16664        // declared-slot list (the presence bit is non-empty, so the
16665        // mesh kind-coherence gate must surface the slot as
16666        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
16667        // push the label (the "author omitted the slot entirely" arm
16668        // — the empty-slice partition the serde-default folds onto).
16669        // The pair jointly pins the accessor + declared-slot
16670        // enumerator composition: any future silent detour that had
16671        // the accessor collapse `[Membro { .. }]` to `[]` (a
16672        // `.filter(|m| m.nome() != "__reserved__")` projection) would
16673        // silently absorb the "declared but degenerate" arm at the
16674        // accessor boundary and the
16675        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16676        // coherence gate would silently accept a struct-literal
16677        // `Caixa` carrying the drift.
16678        //
16679        // Peer of the sibling
16680        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16681        // (2a1f907) and
16682        // `declared_supervisor_slots_children_arm_routes_through_accessor`
16683        // (c17b51e) composition pins on the M2 `:upgrade-from` /
16684        // `:children` composite-slice arms — same "the enumerator gate
16685        // must route through the substrate-primitive typed dispatch"
16686        // discipline extended onto the M3 `:membros` composite-slice
16687        // arm, opening the M3 arm of the declared-slot enumerator's
16688        // routing invariant.
16689        use crate::aplicacao::Membro;
16690        let c = caixa_aplicacao_with_membros(vec![Membro {
16691            caixa: "cart".into(),
16692            versao: "^0.1".into(),
16693        }]);
16694        let slots = c.declared_mesh_slots();
16695        assert!(
16696            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
16697            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
16698             `:membros` is non-empty — the accessor and the enumerator \
16699             gate must route through the same substrate-primitive \
16700             typed dispatch on the outer :membros presence bit (got \
16701             slots={slots:?})",
16702        );
16703        let c = caixa_aplicacao_with_membros(vec![]);
16704        let slots = c.declared_mesh_slots();
16705        assert!(
16706            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
16707            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
16708             when `:membros` is empty — the author-omitted arm must \
16709             route through the accessor's empty-slice return unchanged \
16710             (got slots={slots:?})",
16711        );
16712    }
16713
16714    #[test]
16715    fn aplicacao_view_membros_arm_routes_through_accessor() {
16716        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
16717        // fold-in arm must key off [`Caixa::membros`], not the raw
16718        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
16719        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
16720        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
16721        // member list through the accessor into the typed
16722        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
16723        // every entry the accessor surfaces must land in the view's
16724        // `membros` slot in the same order. The pair jointly pins the
16725        // accessor + view-composer composition: any future silent
16726        // detour that had the accessor return a fresh-cloned
16727        // `Vec<Membro>` copy would silently break the reference-
16728        // identity pin the peer `aplicacao_view` fold-in path reads
16729        // from — the fold would clone once more per accessor call
16730        // instead of borrowing the storage buffer verbatim once.
16731        //
16732        // Peer of the sibling
16733        // `aplicacao_view_politicas_arm_folds_through_accessor`
16734        // (5d23d29) /
16735        // `aplicacao_view_placement_arm_folds_through_accessor`
16736        // (4fb8074) /
16737        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
16738        // composition pins on the M3 `:politicas` / `:placement` /
16739        // `:entrada` outer-`Option<&Composite>` arms — extended here to
16740        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
16741        // closing the aplicacao-view composer's routing invariant on
16742        // the composite-slice input.
16743        use crate::aplicacao::Membro;
16744        let c = caixa_aplicacao_with_membros(vec![
16745            Membro {
16746                caixa: "cart".into(),
16747                versao: "^0.1".into(),
16748            },
16749            Membro {
16750                caixa: "pricing".into(),
16751                versao: "^0.2".into(),
16752            },
16753        ]);
16754        let view = c
16755            .aplicacao_view()
16756            .expect("Aplicacao kind must produce an aplicacao_view");
16757        assert_eq!(
16758            view.membros(),
16759            c.membros(),
16760            "aplicacao_view must fold Caixa::membros verbatim into \
16761             AplicacaoSpec::membros — the accessor and the view \
16762             composer must route through the same substrate-primitive \
16763             typed dispatch on the outer :membros slice (got view \
16764             membros={:?}, expected {:?})",
16765            view.membros(),
16766            c.membros(),
16767        );
16768    }
16769
16770    #[test]
16771    fn membros_projects_slice_by_borrow() {
16772        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
16773        // borrow — the returned slice borrows the underlying
16774        // `Vec<Membro>` storage of the `:membros` slot and the
16775        // accessor must not clone the backing `Vec` on every call.
16776        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
16777        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
16778        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16779        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16780        // `exe_projects_slice_by_borrow` 65d9527,
16781        // `servicos_projects_slice_by_borrow` 611f78b,
16782        // `deps_projects_slice_by_borrow` ad34b4e,
16783        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16784        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
16785        // `children_projects_slice_by_borrow` c17b51e) on the sibling
16786        // outer top-level [`Caixa`] scalar-element and composite-
16787        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
16788        // slot composite-element `&[Composite]` axis: the accessor's
16789        // returned slice must borrow from `&self` (the returned
16790        // reference's lifetime is tied to `&self`), and calling the
16791        // accessor twice on the same [`Caixa`] must yield slices that
16792        // are pointer-equal (the underlying byte-buffer is the storage
16793        // `Vec`'s allocation, not a fresh copy) as well as value-equal
16794        // (idempotent, no side effects on `&self`).
16795        //
16796        // Pins against a future silent detour that returned an owned
16797        // `Vec<Membro>` (which would type-check but silently clone on
16798        // every call), a `&Vec<Membro>` return (which would leak the
16799        // backing `Vec`'s grow/push/reserve surface no downstream
16800        // consumer reaches for), or a one-arm-only accessor that
16801        // returned a saturating value on some sentinel input.
16802        use crate::aplicacao::Membro;
16803        for membros in [
16804            vec![],
16805            vec![Membro {
16806                caixa: "cart".into(),
16807                versao: "^0.1".into(),
16808            }],
16809            vec![
16810                Membro {
16811                    caixa: "cart".into(),
16812                    versao: "^0.1".into(),
16813                },
16814                Membro {
16815                    caixa: "pricing".into(),
16816                    versao: "^0.2".into(),
16817                },
16818            ],
16819        ] {
16820            let c = caixa_aplicacao_with_membros(membros.clone());
16821            let first = c.membros();
16822            let second = c.membros();
16823            assert_eq!(
16824                first, second,
16825                "Caixa::membros must be idempotent — two successive \
16826                 calls on the same &self must return the same &[Membro]",
16827            );
16828            assert_eq!(
16829                first.as_ptr(),
16830                second.as_ptr(),
16831                "Caixa::membros must borrow the underlying Vec<Membro> \
16832                 storage — two successive calls must return slices with \
16833                 the same backing pointer (a fresh Vec<Membro> clone \
16834                 would change the pointer on every call)",
16835            );
16836            assert_eq!(
16837                first,
16838                membros.as_slice(),
16839                "Caixa::membros must return :membros verbatim by borrow \
16840                 — got {first:?}, expected {membros:?}",
16841            );
16842        }
16843    }
16844
16845    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
16846
16847    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
16848        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16849        c.kind = CaixaKind::Aplicacao;
16850        c.contratos = contratos;
16851        c
16852    }
16853
16854    fn contrato_http_for_test(
16855        de: &str,
16856        para: &str,
16857        endpoint: &str,
16858    ) -> crate::aplicacao::WitContract {
16859        crate::aplicacao::WitContract {
16860            de: de.into(),
16861            para: para.into(),
16862            wit: "wasi:http/proxy".into(),
16863            endpoint: Some(endpoint.into()),
16864            subject: None,
16865            slot: None,
16866        }
16867    }
16868
16869    #[test]
16870    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
16871        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
16872        // composite `&[WitContract]`-return slice-shape pin:
16873        // [`Caixa::contratos`] must return the `:contratos` typed
16874        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
16875        // over the same backing buffer the raw
16876        // `self.contratos.as_slice()` field access borrows from,
16877        // element-equal across every representative fixture in the
16878        // accept-set — `[]` (the "no contracts declared" arm every
16879        // non-`Aplicacao`-kind `defcaixa` carries by
16880        // `#[serde(default)]` and every leaf-Aplicacao with a single
16881        // member carries), a canonical single-edge fixture (the
16882        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
16883        // edge), and a canonical multi-edge fixture with three distinct
16884        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
16885        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
16886        //
16887        // Pins against a future silent detour that returned an owned
16888        // `Vec<WitContract>` (which would type-check but silently clone
16889        // on every accessor call, breaking the zero-cost projection
16890        // every peer sibling slice accessor carries), an axis-shuffled
16891        // projection (a future detour that reordered edges through the
16892        // accessor would silently split the paired
16893        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
16894        // traversal input from the peer [`Self::aplicacao_view`] fold-
16895        // in path's clone-order input, since every canonical
16896        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
16897        // seed dispatch reads the edge set through the same slice),
16898        // or a reference to an operator-resolved overlay (the future
16899        // per-cluster `:contratos-overrides` slot — its resolution
16900        // must land at exactly this accessor body, not silently divert
16901        // the raw slot away from a second consumer).
16902        //
16903        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
16904        // accessor pin on the substrate primitive for M2 / M3 typed-
16905        // slot vec-carry axes — closes the outer-`Caixa`
16906        // `&[Composite]` composite-slice sub-family the sibling M2
16907        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16908        // (2a1f907) and
16909        // `children_returns_children_slice_verbatim_across_permutations`
16910        // (c17b51e) pins opened and the M3
16911        // `membros_returns_membros_slice_verbatim_across_permutations`
16912        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
16913        // slot arm of the composite-slice sub-family. Peer at the outer
16914        // altitude of the closed inner-
16915        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
16916        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
16917        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
16918            vec![],
16919            vec![contrato_http_for_test("cart", "catalog", "/items")],
16920            vec![
16921                contrato_http_for_test("cart", "catalog", "/items"),
16922                contrato_http_for_test("cart", "pricing", "/price"),
16923                contrato_http_for_test("cart", "auth", "/whoami"),
16924            ],
16925        ];
16926        for contratos in fixtures {
16927            let c = caixa_aplicacao_with_contratos(contratos.clone());
16928            assert_eq!(
16929                c.contratos(),
16930                contratos.as_slice(),
16931                "Caixa::contratos must return :contratos verbatim \
16932                 (got {:?}, expected {contratos:?})",
16933                c.contratos(),
16934            );
16935            assert_eq!(
16936                c.contratos(),
16937                c.contratos.as_slice(),
16938                "Caixa::contratos must element-equal the raw \
16939                 `self.contratos.as_slice()` field access across every \
16940                 value in the Vec<WitContract> accept-set",
16941            );
16942            assert_eq!(
16943                c.contratos().is_empty(),
16944                c.contratos.is_empty(),
16945                "Caixa::contratos().is_empty() must byte-equal \
16946                 self.contratos.is_empty() — a presence-bit drift would \
16947                 silently split the paired Caixa::declared_mesh_slots \
16948                 mesh declared-slot enumerator's presence probe from \
16949                 the peer Caixa::aplicacao_view typed-view composer's \
16950                 fold-in path",
16951            );
16952        }
16953    }
16954
16955    #[test]
16956    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
16957        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
16958        // presence-probe arm must key off [`Caixa::contratos`], not the
16959        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
16960        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
16961        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
16962        // presence bit is non-empty, so the mesh kind-coherence gate
16963        // must surface the slot as "declared"), and a `Caixa {
16964        // contratos: vec![], .. }` must NOT push the label (the "author
16965        // omitted the slot entirely" arm — the empty-slice partition
16966        // the serde-default folds onto). The pair jointly pins the
16967        // accessor + declared-slot enumerator composition: any future
16968        // silent detour that had the accessor collapse
16969        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
16970        // "__reserved__")` projection) would silently absorb the
16971        // "declared but degenerate" arm at the accessor boundary and
16972        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16973        // coherence gate would silently accept a struct-literal
16974        // `Caixa` carrying the drift.
16975        //
16976        // Peer of the sibling
16977        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16978        // (2a1f907),
16979        // `declared_supervisor_slots_children_arm_routes_through_accessor`
16980        // (c17b51e), and
16981        // `declared_mesh_slots_membros_arm_routes_through_accessor`
16982        // (0f26987) composition pins on the M2 `:upgrade-from` /
16983        // `:children` / M3 `:membros` composite-slice arms — same "the
16984        // enumerator gate must route through the substrate-primitive
16985        // typed dispatch" discipline extended onto the M3 `:contratos`
16986        // composite-slice arm, closing the M3 mesh-slot arm of the
16987        // declared-slot enumerator's routing invariant on the
16988        // composite-slice inputs.
16989        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
16990            "cart", "catalog", "/items",
16991        )]);
16992        let slots = c.declared_mesh_slots();
16993        assert!(
16994            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
16995            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
16996             `:contratos` is non-empty — the accessor and the enumerator \
16997             gate must route through the same substrate-primitive \
16998             typed dispatch on the outer :contratos presence bit (got \
16999             slots={slots:?})",
17000        );
17001        let c = caixa_aplicacao_with_contratos(vec![]);
17002        let slots = c.declared_mesh_slots();
17003        assert!(
17004            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
17005            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
17006             when `:contratos` is empty — the author-omitted arm must \
17007             route through the accessor's empty-slice return unchanged \
17008             (got slots={slots:?})",
17009        );
17010    }
17011
17012    #[test]
17013    fn aplicacao_view_contratos_arm_routes_through_accessor() {
17014        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
17015        // fold-in arm must key off [`Caixa::contratos`], not the raw
17016        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
17017        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
17018        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
17019        // per-edge list through the accessor into the typed
17020        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
17021        // every entry the accessor surfaces must land in the view's
17022        // `contratos` slot in the same order. The pair jointly pins
17023        // the accessor + view-composer composition: a future silent
17024        // detour that had the accessor shuffle or drop an edge would
17025        // silently split the paired declared-slot enumerator's
17026        // presence bit from the typed-view composer's edge-list, a
17027        // two-consumer split at the enumerator and the view composer
17028        // far from the source `caixa.lisp`.
17029        //
17030        // Peer of the sibling
17031        // `aplicacao_view_membros_arm_routes_through_accessor`
17032        // (0f26987) composition pin on the M3 `:membros` outer-
17033        // `&[Composite]` composite-slice arm, closing the aplicacao-
17034        // view composer's routing invariant on the composite-slice
17035        // inputs at the outer altitude.
17036        let c = caixa_aplicacao_with_contratos(vec![
17037            contrato_http_for_test("cart", "catalog", "/items"),
17038            contrato_http_for_test("cart", "pricing", "/price"),
17039        ]);
17040        let view = c
17041            .aplicacao_view()
17042            .expect("Aplicacao kind must produce an aplicacao_view");
17043        assert_eq!(
17044            view.contratos(),
17045            c.contratos(),
17046            "aplicacao_view must fold Caixa::contratos verbatim into \
17047             AplicacaoSpec::contratos — the accessor and the view \
17048             composer must route through the same substrate-primitive \
17049             typed dispatch on the outer :contratos slice (got view \
17050             contratos={:?}, expected {:?})",
17051            view.contratos(),
17052            c.contratos(),
17053        );
17054    }
17055
17056    #[test]
17057    fn contratos_projects_slice_by_borrow() {
17058        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
17059        // by borrow — the returned slice borrows the underlying
17060        // `Vec<WitContract>` storage of the `:contratos` slot and the
17061        // accessor must not clone the backing `Vec` on every call.
17062        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
17063        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
17064        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
17065        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17066        // `exe_projects_slice_by_borrow` 65d9527,
17067        // `servicos_projects_slice_by_borrow` 611f78b,
17068        // `deps_projects_slice_by_borrow` ad34b4e,
17069        // `deps_dev_projects_slice_by_borrow` f7fd81e,
17070        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
17071        // `children_projects_slice_by_borrow` c17b51e,
17072        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
17073        // outer top-level [`Caixa`] scalar-element and composite-
17074        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
17075        // composite-element `&[Composite]` axis on the by-borrow pin:
17076        // the accessor's returned slice must borrow from `&self` (the
17077        // returned reference's lifetime is tied to `&self`), and
17078        // calling the accessor twice on the same [`Caixa`] must yield
17079        // slices that are pointer-equal (the underlying byte-buffer is
17080        // the storage `Vec`'s allocation, not a fresh copy) as well as
17081        // value-equal (idempotent, no side effects on `&self`).
17082        //
17083        // Pins against a future silent detour that returned an owned
17084        // `Vec<WitContract>` (which would type-check but silently clone
17085        // on every call), a `&Vec<WitContract>` return (which would
17086        // leak the backing `Vec`'s grow/push/reserve surface no
17087        // downstream consumer reaches for), or a one-arm-only accessor
17088        // that returned a saturating value on some sentinel input.
17089        for contratos in [
17090            vec![],
17091            vec![contrato_http_for_test("cart", "catalog", "/items")],
17092            vec![
17093                contrato_http_for_test("cart", "catalog", "/items"),
17094                contrato_http_for_test("cart", "pricing", "/price"),
17095            ],
17096        ] {
17097            let c = caixa_aplicacao_with_contratos(contratos.clone());
17098            let first = c.contratos();
17099            let second = c.contratos();
17100            assert_eq!(
17101                first, second,
17102                "Caixa::contratos must be idempotent — two successive \
17103                 calls on the same &self must return the same \
17104                 &[WitContract]",
17105            );
17106            assert_eq!(
17107                first.as_ptr(),
17108                second.as_ptr(),
17109                "Caixa::contratos must borrow the underlying \
17110                 Vec<WitContract> storage — two successive calls must \
17111                 return slices with the same backing pointer (a fresh \
17112                 Vec<WitContract> clone would change the pointer on \
17113                 every call)",
17114            );
17115            assert_eq!(
17116                first,
17117                contratos.as_slice(),
17118                "Caixa::contratos must return :contratos verbatim by \
17119                 borrow — got {first:?}, expected {contratos:?}",
17120            );
17121        }
17122    }
17123
17124    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
17125
17126    #[test]
17127    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
17128        // Load-bearing invariant: every multi-word top-level [`Caixa`]
17129        // serde-derived JSON key routes through a lifted `&'static str`
17130        // const. The Rust field names are `snake_case`
17131        // (`deps_dev` / `upgrade_from` / `max_restarts` /
17132        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
17133        // "camelCase")]` derive attribute maps each to the camelCase
17134        // byte-string the [`Caixa::to_lisp`] round-trip's
17135        // `serde_json::to_value(self)` step lands under before
17136        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
17137        // to the kebab-case `:deps-dev` / `:upgrade-from` /
17138        // `:max-restarts` / `:restart-window` author surface. Serialize
17139        // a fully-populated [`Caixa`] and pin that each canonical
17140        // byte-sequence appears verbatim in the JSON — a future
17141        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
17142        // verbatim-field-name flip at the derive attribute (any of
17143        // which would silently break every [`Caixa::to_lisp`]
17144        // round-trip and the future M4 operator-side manifest ingest's
17145        // `Value::get(<key>)` navigation) surfaces here as a build-time
17146        // test failure at `manifest.rs`, not as an apply-time
17147        // `.get(<stale-canonical-const>)` returning `None` far from the
17148        // derive-attr drift's commit. Same discipline the sibling
17149        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
17150        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
17151        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
17152        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
17153        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
17154        // [`UpgradeFromEntry`] per-entry axes — extended here to the
17155        // enclosing M0 [`Caixa`] top-level axis so the last of the four
17156        // multi-word top-level [`Caixa`] serde-derived JSON keys
17157        // (`depsDev`) joins the substrate's "one canonical byte-string
17158        // per typed serialized-key axis" discipline.
17159        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17160        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17161        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17162        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
17163        c.upgrade_from = vec![UpgradeFromEntry {
17164            from: "0.0.1".into(),
17165            instructions: vec![UpgradeInstruction::Restart],
17166        }];
17167        c.estrategia = Some(RestartStrategy::OneForOne);
17168        c.max_restarts = Some(3);
17169        c.restart_window = Some("60s".into());
17170        c.children = vec![ChildSpec {
17171            caixa: "child".into(),
17172            versao: "^0.1".into(),
17173            restart: RestartPolicy::Permanent,
17174        }];
17175        let json = serde_json::to_string(&c).unwrap();
17176        for key in [
17177            crate::render::CAIXA_KEY_DEPS_DEV,
17178            crate::render::M2_KEY_UPGRADE_FROM,
17179            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
17180            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
17181        ] {
17182            let quoted = format!("\"{key}\"");
17183            assert!(
17184                json.contains(&quoted),
17185                "serialized Caixa must carry the lifted top-level \
17186                 multi-word byte-sequence {quoted} verbatim in the JSON \
17187                 emission (got: {json})",
17188            );
17189        }
17190    }
17191
17192    #[test]
17193    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
17194        // Cross-axis drift-detection pin: a future collapse of the four
17195        // canonical [`Caixa`] top-level multi-word byte-strings onto the
17196        // same value (e.g. an accidental copy-paste flip of
17197        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
17198        // `"upgradeFrom"`) would silently reroute every downstream
17199        // `Value::get(<key>)` probe on one axis onto the sibling axis's
17200        // top-level entry and pass every propagation-probe test that
17201        // expected only the stale axis's value. Peer of the sibling
17202        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
17203        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
17204        let all = [
17205            crate::render::CAIXA_KEY_DEPS_DEV,
17206            crate::render::M2_KEY_UPGRADE_FROM,
17207            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
17208            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
17209        ];
17210        for (i, a) in all.iter().enumerate() {
17211            for b in all.iter().skip(i + 1) {
17212                assert_ne!(
17213                    a, b,
17214                    "Caixa top-level multi-word key consts must be \
17215                     pairwise-distinct canonical byte-sequences — got \
17216                     `{a}` == `{b}`",
17217                );
17218            }
17219        }
17220    }
17221
17222    #[test]
17223    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
17224        // Shape-pin: every [`Caixa`] top-level multi-word key const must
17225        // be a lowerCamelCase byte-sequence (no `snake_case`
17226        // underscores, no `kebab-case` hyphens, no leading colon, no
17227        // `PascalCase` leading capital, no whitespace / dots) — the
17228        // canonical shape the `#[serde(rename_all = "camelCase")]`
17229        // derive produces on [`Caixa`]. A future flip to a
17230        // non-camelCase attribute at the derive surfaces both here
17231        // (this test fails on the stale-constant shape) and at
17232        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
17233        // (that test fails on the mismatch between const and derive).
17234        // Peer with `membro_key_consts_are_lower_camel_case_shape`
17235        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
17236        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
17237        for key in [
17238            crate::render::CAIXA_KEY_DEPS_DEV,
17239            crate::render::M2_KEY_UPGRADE_FROM,
17240            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
17241            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
17242        ] {
17243            assert!(
17244                !key.is_empty(),
17245                "Caixa top-level multi-word key const must be non-empty \
17246                 (got {key:?})"
17247            );
17248            let first = key.chars().next().unwrap();
17249            assert!(
17250                first.is_ascii_lowercase(),
17251                "Caixa top-level multi-word key const must lead with an \
17252                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
17253            );
17254            assert!(
17255                key.chars().all(|c| c.is_ascii_alphanumeric()),
17256                "Caixa top-level multi-word key const must be \
17257                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
17258                 whitespace (got {key:?})",
17259            );
17260        }
17261    }
17262
17263    #[test]
17264    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
17265        // Scalar-value pin: the byte-string the
17266        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
17267        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
17268        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
17269        // → `depsTest` matching a hypothetical per-test-target
17270        // vocabulary flip) lands as an edit to exactly one const AND
17271        // one derive attribute — the sibling
17272        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
17273        // pin already ties the const to the derive attribute, so a
17274        // rebrand that touches only one side of the pair fails at
17275        // caixa-core build time. Same "scalar-value pin per const"
17276        // discipline the sibling
17277        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
17278        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
17279        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
17280        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
17281    }
17282
17283    #[test]
17284    fn caixa_key_deps_pins_canonical_byte_string() {
17285        // Scalar-value pin: the byte-string the
17286        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
17287        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
17288        // on the two-list dep-graph serialized-key axis — the sibling
17289        // pin covers the multi-word `deps_dev → depsDev` camelCase
17290        // arm, this pin covers the single-word `deps → deps` no-op arm
17291        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
17292        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
17293        // axis and the emitted JSON key equals the source-side field
17294        // name byte-for-byte). A future [`crate::Caixa::deps`] field
17295        // rename (`deps` → `dependencies` matching Cargo's verbatim
17296        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
17297        // hypothetical per-runtime-target vocabulary flip) OR an added
17298        // `#[serde(rename = "…")]` explicit override lands as an edit
17299        // to exactly one const AND one derive-attr / field name — the
17300        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
17301        // pin ties the const to the emitted JSON key, so a rebrand
17302        // that touches only one side of the pair fails at caixa-core
17303        // build time.
17304        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
17305    }
17306
17307    #[test]
17308    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
17309        // Load-bearing invariant on the single-word `deps` top-level
17310        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
17311        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
17312        // `serde_json::to_value(self)` step emits. Serialize a
17313        // populated [`Caixa`] whose `:deps` slot carries at least one
17314        // entry (the `#[serde(default)]` attribute on the field emits
17315        // an empty `[]` even without members, but a non-empty vec
17316        // additionally covers the codec's per-`Dep`-entry emission
17317        // path) and pin that `"deps"` appears verbatim in the JSON
17318        // emission — a future accidental `rename_all = "snake_case"` /
17319        // `"kebab-case"` flip at the derive attribute (or an added
17320        // `#[serde(rename = "…")]` explicit override on the field, or
17321        // a Rust field rename) would break every [`Caixa::to_lisp`]
17322        // round-trip and the future M4 operator-side manifest ingest's
17323        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
17324        // build-time test failure at `manifest.rs`, not as an
17325        // apply-time `.get(<stale-canonical-const>)` returning `None`
17326        // far from the drift's commit. Peer of the sibling
17327        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
17328        // multi-word pin on the same M0 [`Caixa`] top-level
17329        // serialized-key axis, extended here to the single-word arm
17330        // the multi-word test's `rename_all = "camelCase"` sweep can't
17331        // reach (single-word `deps → deps` is a no-op the multi-word
17332        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
17333        // `\"restartWindow\"` byte-scan can never observe).
17334        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17335        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
17336        let json = serde_json::to_string(&c).unwrap();
17337        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
17338        assert!(
17339            json.contains(&quoted),
17340            "serialized Caixa must carry the lifted top-level `deps` \
17341             byte-sequence {quoted} verbatim in the JSON emission (got: \
17342             {json})",
17343        );
17344    }
17345
17346    #[test]
17347    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
17348        // Cross-axis drift-detection pin on the two-list dep-graph
17349        // renderer-side wire-key axis: a future collapse of the
17350        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
17351        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
17352        // same value (e.g. an accidental copy-paste flip of
17353        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
17354        // reroute every downstream `Value::get(<key>)` probe on one
17355        // axis onto the sibling axis's dep-list and pass every
17356        // propagation-probe test that expected only the stale axis's
17357        // value — a dev-only dep would land in the runtime closure at
17358        // publish time, or a runtime dep would be excluded from the
17359        // published lacre. Peer of the sibling four-way distinct pin
17360        // on the top-level multi-word tetrad
17361        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
17362        // and the two-way pin on the sibling
17363        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
17364        // author-facing arm (4da6fba's test), extended here to the
17365        // renderer-side wire-key arm of the same two-list dep-graph
17366        // axis so both halves of the "one canonical byte-string per
17367        // typed axis per (author, wire)" grid carry the same
17368        // distinct-ness discipline.
17369        assert_ne!(
17370            crate::render::CAIXA_KEY_DEPS,
17371            crate::render::CAIXA_KEY_DEPS_DEV,
17372            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
17373             canonical byte-sequences on the two-list dep-graph \
17374             renderer-side wire-key axis"
17375        );
17376    }
17377
17378    // ── DepList / Caixa::push_dep pin ────────────────────────────────
17379    //
17380    // The compounding pin: the two-arm closed-set typed enum
17381    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
17382    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
17383    // consumer of the top-level manifest's dep-mutation surface reads
17384    // through, and the typed dispatch [`Caixa::push_dep`] on the
17385    // substrate primitive folds the "select list → check within-list
17386    // dup → push" cascade onto one method call. Prior to this landing
17387    // the two axes lived across two `&'static str` constants
17388    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
17389    // set type carrying the pair; the `feira add` mutation site's
17390    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
17391    // caixa.deps }` dispatch expressed no compile-time link back to
17392    // the substrate primitive, and a future third dep-list axis would
17393    // have silently split at every open-coded mutation site.
17394
17395    #[test]
17396    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
17397        // Every arm returns the same `&'static str` the substrate's
17398        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
17399        // constants carry. A future rebrand on either constant reaches
17400        // the enum through one edit; a regression to inline literals
17401        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
17402        // quotes from the wire-format constants every consumer routes
17403        // through and this pin flags it at build time.
17404        assert_eq!(
17405            crate::dep::DepList::Prod.as_str(),
17406            crate::render::DEP_AUTHOR_KEY_DEPS
17407        );
17408        assert_eq!(
17409            crate::dep::DepList::Dev.as_str(),
17410            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
17411        );
17412    }
17413
17414    #[test]
17415    fn dep_list_display_routes_through_as_str() {
17416        // Same as-str-through-Display convergence discipline the
17417        // sibling closed-set typed enums carry — a `format!("{list}")`
17418        // call must land byte-for-byte on the accessor's return so a
17419        // future consumer that formats the enum for a diagnostic line
17420        // reaches the same wire-format constant the wire-format
17421        // producers do.
17422        assert_eq!(
17423            format!("{}", crate::dep::DepList::Prod),
17424            crate::dep::DepList::Prod.as_str()
17425        );
17426        assert_eq!(
17427            format!("{}", crate::dep::DepList::Dev),
17428            crate::dep::DepList::Dev.as_str()
17429        );
17430    }
17431
17432    #[test]
17433    fn dep_list_all_enumerates_every_variant_once() {
17434        // Exhaustive-iteration pin — every arm appears exactly once in
17435        // `ALL`, matching the closed set the compiler enforces on the
17436        // sibling `match self` arms. A future variant addition that
17437        // extends only one method's match without extending `ALL`
17438        // would silently drop the new arm from every consumer that
17439        // iterates the slice.
17440        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
17441        assert!(variants.contains(&crate::dep::DepList::Prod));
17442        assert!(variants.contains(&crate::dep::DepList::Dev));
17443        assert_eq!(variants.len(), 2);
17444    }
17445
17446    #[test]
17447    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
17448        // Reverse projection on the two-list dep-graph axis: the
17449        // author-surface wire tag the sibling `as_str` emitter walks
17450        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
17451        // `Some(DepList::Prod)`. A regression that hand-rolled the
17452        // per-arm match without routing through the lifted
17453        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
17454        // future wire-tag rebrand and this pin flags it at build time.
17455        assert_eq!(
17456            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
17457            Some(crate::dep::DepList::Prod)
17458        );
17459    }
17460
17461    #[test]
17462    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
17463        // Peer of the `Prod`-arm pin on the dev-only axis: the
17464        // author-surface wire tag the sibling `as_str` emitter walks
17465        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
17466        // back to `Some(DepList::Dev)`. Same drift-detection posture
17467        // as the peer arm — the sibling method `match` arms are
17468        // compiler-checked exhaustive so a future variant addition
17469        // trips at build time.
17470        assert_eq!(
17471            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
17472            Some(crate::dep::DepList::Dev)
17473        );
17474    }
17475
17476    #[test]
17477    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
17478        // Every input outside the closed-set arm-string set the
17479        // sibling `as_str` emitter walks lands on the terminal `None`
17480        // fallback — no silent-accept surface. Sweeps a set of
17481        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
17482        // rebrand candidates, foreign wire tags, empty string) so a
17483        // future variant addition that widened one wire form without
17484        // extending the emitter's arm-set would trip the sibling
17485        // round-trip pin below rather than silently accepting the new
17486        // form here.
17487        for candidate in [
17488            "",
17489            "deps",
17490            "deps-dev",
17491            ":deps ",
17492            ":Deps",
17493            ":DEPS",
17494            ":build-dep",
17495            ":tool-dep",
17496            "prod",
17497            "dev",
17498        ] {
17499            assert_eq!(
17500                crate::dep::DepList::from_wire(candidate),
17501                None,
17502                "from_wire({candidate:?}) must return None; every input outside \
17503                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
17504                 the sibling as_str emitter walks lands on the terminal fallback",
17505            );
17506        }
17507    }
17508
17509    #[test]
17510    fn dep_list_round_trips_through_as_str_and_from_wire() {
17511        // Load-bearing round-trip pin: every arm the `ALL` iteration
17512        // exposes survives the `as_str` → `from_wire` composition
17513        // byte-for-byte. Same discipline the sibling closed-set enums
17514        // carry — `CaixaKind` /
17515        // `RestartStrategy` / `RestartPolicy` /
17516        // `PlacementStrategy` — extended onto the two-list dep-graph
17517        // axis. A future variant addition that extends `ALL` +
17518        // `as_str` without extending `from_wire` (or vice versa)
17519        // trips at build time on this iteration because the compiler
17520        // enforces exhaustiveness on the sibling `match self` arms.
17521        for &list in crate::dep::DepList::ALL {
17522            assert_eq!(
17523                crate::dep::DepList::from_wire(list.as_str()),
17524                Some(list),
17525                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
17526                 a silent split between the forward emitter and the reverse parser \
17527                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
17528            );
17529        }
17530    }
17531
17532    #[test]
17533    fn push_dep_routes_to_deps_slot_on_prod_arm() {
17534        // The `Prod` arm dispatches to the runtime-closure `:deps`
17535        // slot every downstream lacre-pipeline consumer resolves at
17536        // build time. A future arm that regressed to inline `&mut
17537        // self.deps_dev` on the `Prod` path would silently reroute
17538        // every runtime dep into the dev-only closure at publish time
17539        // — this pin refuses that regression.
17540        let src = Caixa::template("host");
17541        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17542        let before_deps = caixa.deps().len();
17543        let before_deps_dev = caixa.deps_dev().len();
17544        let dep = Dep {
17545            nome: "caixa-teia".to_string(),
17546            versao: "^0.1".to_string(),
17547            fonte: None,
17548            opcional: false,
17549            caracteristicas: Vec::new(),
17550        };
17551        caixa
17552            .push_dep(crate::dep::DepList::Prod, dep)
17553            .expect("first push into :deps succeeds");
17554        assert_eq!(caixa.deps().len(), before_deps + 1);
17555        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
17556        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
17557    }
17558
17559    #[test]
17560    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
17561        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
17562        // must dispatch to the dev-only-closure `:deps-dev` slot every
17563        // downstream test-facing artifact resolver reads. A future
17564        // regression that inverted the two arms would silently route
17565        // every dev-only dep into the runtime closure at publish time
17566        // and this pin catches it before the drift ships.
17567        let src = Caixa::template("host");
17568        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17569        let dep = Dep {
17570            nome: "tatara-check".to_string(),
17571            versao: "*".to_string(),
17572            fonte: None,
17573            opcional: false,
17574            caracteristicas: Vec::new(),
17575        };
17576        caixa
17577            .push_dep(crate::dep::DepList::Dev, dep)
17578            .expect("first push into :deps-dev succeeds");
17579        assert!(caixa.deps().is_empty());
17580        assert_eq!(caixa.deps_dev().len(), 1);
17581        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
17582    }
17583
17584    #[test]
17585    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
17586        // Within-list dup check routes through the canonical
17587        // [`DepError::DuplicateNome`] carrier — the substrate's typed
17588        // diagnostic for the same axis [`Caixa::validate_deps`]'s
17589        // parse-time [`crate::render::insert_first_seen`] walk raises
17590        // on. Prior to the lift the mutation site's inline
17591        // `bail!("dep '{}' already declared", …)` string-diagnostic
17592        // path expressed no through-line back to the typed error;
17593        // routing every dep-list refusal through one carrier means an
17594        // author reading a `feira add` refusal and a `feira build`
17595        // refusal reaches for the same corrective surface without
17596        // switching diagnostic idioms.
17597        let src = Caixa::template("host");
17598        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17599        let dep = Dep {
17600            nome: "caixa-teia".to_string(),
17601            versao: "^0.1".to_string(),
17602            fonte: None,
17603            opcional: false,
17604            caracteristicas: Vec::new(),
17605        };
17606        caixa
17607            .push_dep(crate::dep::DepList::Prod, dep.clone())
17608            .expect("first push succeeds");
17609        let dup = Dep {
17610            nome: "caixa-teia".to_string(),
17611            versao: "^0.2".to_string(),
17612            fonte: None,
17613            opcional: false,
17614            caracteristicas: Vec::new(),
17615        };
17616        let err = caixa
17617            .push_dep(crate::dep::DepList::Prod, dup)
17618            .expect_err("second push with same :nome refuses");
17619        assert_eq!(
17620            err,
17621            DepError::DuplicateNome {
17622                nome: "caixa-teia".to_string(),
17623                list: crate::render::DEP_AUTHOR_KEY_DEPS,
17624            }
17625        );
17626        // The refused mutation must not corrupt the target list —
17627        // exactly one entry lives past the refusal, matching the
17628        // canonical single-source-of-truth invariant `Caixa::deps()`
17629        // carries.
17630        assert_eq!(caixa.deps().len(), 1);
17631    }
17632
17633    #[test]
17634    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
17635        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
17636        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
17637        // `list` payload so a future author reading the refusal grep's
17638        // for the correct `:deps-dev` block in their `caixa.lisp`,
17639        // not the sibling `:deps` block the runtime closure resolves.
17640        let src = Caixa::template("host");
17641        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17642        let dep = Dep {
17643            nome: "tatara-check".to_string(),
17644            versao: "*".to_string(),
17645            fonte: None,
17646            opcional: false,
17647            caracteristicas: Vec::new(),
17648        };
17649        caixa
17650            .push_dep(crate::dep::DepList::Dev, dep.clone())
17651            .expect("first push succeeds");
17652        let err = caixa
17653            .push_dep(crate::dep::DepList::Dev, dep)
17654            .expect_err("second push with same :nome refuses");
17655        assert!(matches!(
17656            err,
17657            DepError::DuplicateNome {
17658                ref nome,
17659                list,
17660            } if nome == "tatara-check"
17661                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
17662        ));
17663    }
17664
17665    #[test]
17666    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
17667        // The within-list dup check is scoped to the target arm — a
17668        // caixa may legitimately carry the same `:nome` under both
17669        // `:deps` and `:deps-dev` (though the substrate's peer
17670        // [`crate::Caixa::validate_deps`] walk still refuses the
17671        // shape at parse time; the mutation-site refusal is scoped to
17672        // the mutation-site's list to match the peer parse-time
17673        // per-list [`crate::render::insert_first_seen`] discipline).
17674        // The two arms hold independent seen-sets.
17675        let src = Caixa::template("host");
17676        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17677        let dep_prod = Dep {
17678            nome: "shared".to_string(),
17679            versao: "^0.1".to_string(),
17680            fonte: None,
17681            opcional: false,
17682            caracteristicas: Vec::new(),
17683        };
17684        let dep_dev = Dep {
17685            nome: "shared".to_string(),
17686            versao: "*".to_string(),
17687            fonte: None,
17688            opcional: false,
17689            caracteristicas: Vec::new(),
17690        };
17691        caixa
17692            .push_dep(crate::dep::DepList::Prod, dep_prod)
17693            .expect("push into :deps succeeds");
17694        caixa
17695            .push_dep(crate::dep::DepList::Dev, dep_dev)
17696            .expect("push same :nome into :deps-dev succeeds");
17697        assert_eq!(caixa.deps().len(), 1);
17698        assert_eq!(caixa.deps_dev().len(), 1);
17699    }
17700
17701    #[test]
17702    fn deps_of_prod_returns_the_deps_slot_verbatim() {
17703        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
17704        // accessor must project onto the runtime-closure `:deps` slot —
17705        // element-equal and length-equal to the sibling per-slot
17706        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
17707        // A future arm that regressed to `self.deps_dev()` on the `Prod`
17708        // path would silently reroute every downstream typed-dispatch
17709        // walker (the [`Caixa::validate_deps`] per-list
17710        // [`crate::render::insert_first_seen`] dedup walk, any future
17711        // per-axis-parametrised consumer) into the sibling dev-only
17712        // closure and this pin refuses that regression.
17713        let src = Caixa::template("host");
17714        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17715        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
17716        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
17717        let dep = Dep {
17718            nome: "caixa-teia".to_string(),
17719            versao: "^0.1".to_string(),
17720            fonte: None,
17721            opcional: false,
17722            caracteristicas: Vec::new(),
17723        };
17724        caixa
17725            .push_dep(crate::dep::DepList::Prod, dep.clone())
17726            .expect("push into :deps succeeds");
17727        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
17728        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
17729        assert_eq!(
17730            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
17731            "caixa-teia"
17732        );
17733    }
17734
17735    #[test]
17736    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
17737        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
17738        // [`Caixa::deps_of`] must project onto the dev-only-closure
17739        // `:deps-dev` slot, element-equal and length-equal to the
17740        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
17741        // future regression that inverted the two arms would silently
17742        // route every dev-list walker onto the runtime closure and this
17743        // pin catches it before the drift ships.
17744        let src = Caixa::template("host");
17745        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17746        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
17747        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
17748        let dep = Dep {
17749            nome: "tatara-check".to_string(),
17750            versao: "*".to_string(),
17751            fonte: None,
17752            opcional: false,
17753            caracteristicas: Vec::new(),
17754        };
17755        caixa
17756            .push_dep(crate::dep::DepList::Dev, dep)
17757            .expect("push into :deps-dev succeeds");
17758        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
17759        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
17760        assert_eq!(
17761            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
17762            "tatara-check"
17763        );
17764    }
17765
17766    #[test]
17767    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
17768        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
17769        // [`Caixa::deps_of`] must land on the same two-slot partition the
17770        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
17771        // expose — the canonical dispatch a future per-axis-parametrised
17772        // walker (a future `feira app graph` per-list dep summary, a
17773        // future M4 per-cluster dev-closure-audit overlay the CR
17774        // materializer resolves per-CR) reads through. Prior to the
17775        // lift the two-block iteration lived open-coded at every walker,
17776        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
17777        // §I) would have had to grow a third block at every consumer.
17778        // A regression that dropped the `Dev` arm from `ALL` would flip
17779        // the collected pairs to `[(":deps", &[])]` alone and this pin
17780        // refuses that shape.
17781        let src = Caixa::template("host");
17782        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17783        let prod_dep = Dep {
17784            nome: "caixa-teia".to_string(),
17785            versao: "^0.1".to_string(),
17786            fonte: None,
17787            opcional: false,
17788            caracteristicas: Vec::new(),
17789        };
17790        let dev_dep = Dep {
17791            nome: "tatara-check".to_string(),
17792            versao: "*".to_string(),
17793            fonte: None,
17794            opcional: false,
17795            caracteristicas: Vec::new(),
17796        };
17797        caixa
17798            .push_dep(crate::dep::DepList::Prod, prod_dep)
17799            .expect("push into :deps succeeds");
17800        caixa
17801            .push_dep(crate::dep::DepList::Dev, dev_dep)
17802            .expect("push into :deps-dev succeeds");
17803        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
17804            .iter()
17805            .map(|&list| {
17806                let slice = caixa.deps_of(list);
17807                (list.as_str(), slice.len(), slice[0].nome())
17808            })
17809            .collect();
17810        assert_eq!(
17811            collected,
17812            vec![
17813                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
17814                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
17815            ]
17816        );
17817    }
17818
17819    #[test]
17820    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
17821        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
17822        // must route its per-list [`crate::render::insert_first_seen`]
17823        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
17824        // rather than the pre-lift open-coded two-block iteration over
17825        // `self.deps()` + `self.deps_dev()`. A regression that dropped
17826        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
17827        // stop refusing within-list dups on the sibling arm; a
17828        // regression that flipped the arm-to-list-key mapping
17829        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
17830        // diagnostic surface. Both drifts surface here through a paired
17831        // duplicate-name refusal per arm plus an offending-list-key
17832        // check on the emitted [`DepError::DuplicateNome`] carrier.
17833        for &list in crate::dep::DepList::ALL {
17834            let src = Caixa::template("host");
17835            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17836            let dup = Dep {
17837                nome: "twin".to_string(),
17838                versao: "^0.1".to_string(),
17839                fonte: None,
17840                opcional: false,
17841                caracteristicas: Vec::new(),
17842            };
17843            match list {
17844                crate::dep::DepList::Prod => {
17845                    caixa.deps.push(dup.clone());
17846                    caixa.deps.push(dup);
17847                }
17848                crate::dep::DepList::Dev => {
17849                    caixa.deps_dev.push(dup.clone());
17850                    caixa.deps_dev.push(dup);
17851                }
17852            }
17853            let err = caixa
17854                .validate_deps()
17855                .expect_err("within-list duplicate :nome must refuse");
17856            assert_eq!(
17857                err,
17858                DepError::DuplicateNome {
17859                    nome: "twin".to_string(),
17860                    list: list.as_str(),
17861                },
17862                "validate_deps on {list} arm must emit \
17863                 DepError::DuplicateNome carrying the arm's own \
17864                 as_str() diagnostic — the arm-to-list-key mapping \
17865                 flowed through DepList::ALL + Caixa::deps_of"
17866            );
17867        }
17868    }
17869}