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` **resolved-OCI-chart-ref** composer
764    /// — returns the caixa's canonical `oci://<registry>/lareira-<nome>`
765    /// per-Servico Helm chart OCI artifact reference as an owned
766    /// [`String`], derived by dispatching through the substrate-canonical
767    /// [`crate::oci_chart_ref`] helper (which itself composes
768    /// [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied `registry` +
769    /// [`crate::lareira_chart_name`]-of-[`Self::nome`]) against the
770    /// caller-supplied `registry` and the typed [`Self::nome`] byte-string.
771    /// Every substrate-side consumer that resolves "which OCI chart
772    /// artifact does this caixa publish under, in this registry?" reaches
773    /// for exactly one typed dispatch on the substrate primitive — the raw
774    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step compose
775    /// every prior caller re-derived collapses onto one canonical arm on
776    /// the single-`(&Caixa, &str)` dispatch.
777    ///
778    /// Fourth member of the paired per-`Caixa` published-artifact-identity
779    /// axis alongside [`Self::canonical_git_url`] (124f864) /
780    /// [`Self::publish_tag`] (07e05b8) / [`Self::lareira_chart_name`]
781    /// (a8f0bee) — same "close the composed substrate-primitive at one
782    /// canonical arm on the single-`&Caixa` dispatch, converge every
783    /// prior open-coded caller onto the arm" discipline extended from the
784    /// resolved-URL / resolved-tag / resolved-chart-name projections of
785    /// the `:repositorio` / `:versao` / `:nome` axes onto the resolved-
786    /// OCI-ref projection over the paired `(registry, :nome)` inputs. The
787    /// four accessors jointly close the per-`Caixa` published-artifact-
788    /// identity surface every downstream consumer of a caixa's published
789    /// deploy artifacts keys off (git source URL via
790    /// [`Self::canonical_git_url`], git source tag via
791    /// [`Self::publish_tag`], per-Servico Helm chart identity via
792    /// [`Self::lareira_chart_name`], per-registry OCI chart artifact
793    /// reference via [`Self::oci_chart_ref`]) at the substrate primitive
794    /// — a downstream consumer that reaches through all four reads the
795    /// complete deploy-artifact identity of a caixa through four typed
796    /// dispatches, not eight open-coded compositions across four renderer
797    /// crates. The unique-signature dispatch (`(&Caixa, &str)` on this
798    /// method vs. `&Caixa` on the sibling three) reflects the extra input
799    /// axis this composer folds in: unlike the git-URL / git-tag / chart-
800    /// name axes (each derived purely from a `&Caixa`), the OCI-ref axis
801    /// pairs the caixa's per-`:nome` chart identity with the caller-
802    /// supplied per-registry authority segment, so the accessor threads
803    /// the registry byte-string through as a positional `&str`.
804    ///
805    /// The reader-side (one production site at the time of the lift —
806    /// [`caixa-tatara::process_for_aplicacao`]'s `derive_chart_ref` helper
807    /// at caixa-tatara/src/lib.rs:333 that composes the emitted
808    /// `AplicacaoIntent.chart_ref` scalar the tatara-reconciler feeds into
809    /// `helm install`, plus every future per-Servico OCI publish emitter
810    /// the CAIXA-SDLC §II `caixa-publish.yml` reusable workflow's
811    /// `skopeo push` step keys off, the future per-cluster snapshot bundle
812    /// emitter's per-CR `oci://…` field-fill on the M4 registry-alignment
813    /// slot, the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
814    /// materializer's per-member `chart_ref` slot on the tatara `Process`
815    /// intent, the `FluxCD` `HelmRelease` `spec.chart.spec.chart` field-fill
816    /// on the OCI-source path an M4 per-cluster registry-rewrite overlay
817    /// applies per-CR) — always resolves the OCI ref under the canonical
818    /// [`crate::OCI_SCHEME_PREFIX`] scheme prefix + the canonical
819    /// [`Self::lareira_chart_name`] chart-name segment; this method
820    /// encodes that reader-side convention.
821    ///
822    /// The composition body is the exact byte-image of the prior inline
823    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step form
824    /// every prior caller re-derived — pinned by the sibling caixa-tatara
825    /// byte-parity test
826    /// `derive_chart_ref_routes_through_caixa_oci_chart_ref_accessor`
827    /// against a future implementation of this method that reordered the
828    /// composition arguments, migrated the `<scheme>` segment to a
829    /// different constant (the [`crate::OCI_SCHEME_PREFIX`] axis a future
830    /// substrate-side registry-protocol rebrand may split off — the
831    /// constant's own docstring anticipates a substrate-side move once
832    /// Helm 3 / `FluxCD` introduce a successor scheme past `oci://`),
833    /// migrated the `<chart>` segment off the paired
834    /// [`crate::lareira_chart_name`] composer (a per-registry
835    /// namespace-qualification an M4 CR materializer might apply per-CR),
836    /// interposed a canonicalization pass on the `registry` axis (an OCI-
837    /// authority normalization once the M4 registry-alignment slot lands),
838    /// or silently absorbed an empty `:nome` arm (which cannot occur past
839    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
840    /// on the accessor path must not silently paper over).
841    ///
842    /// Owns per-call [`String`] allocation via the single
843    /// [`crate::oci_chart_ref`] `format!` invocation — the by-value return
844    /// matches every downstream consumer's field-fill shape (the caixa-
845    /// tatara `AplicacaoIntent.chart_ref: String` field-fill, every
846    /// future `intent.aplicacao.chart_ref: String` field-fill on the M4
847    /// CR materializer's chart-ref-carrier slot, every future
848    /// `HelmRelease.spec.chart.spec.chart: String` field-fill on the OCI-
849    /// source path).
850    #[must_use]
851    pub fn oci_chart_ref(&self, registry: &str) -> String {
852        crate::oci_chart_ref(registry, self.nome())
853    }
854
855    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
856    /// chart-description scalar accessor every consumer of the top-level
857    /// manifest's Chart.yaml `description:` axis keys off — returns the
858    /// author-declared `:descricao` byte-string verbatim as an
859    /// `Option<&str>`, borrowed from the typed slot's own
860    /// `Option<String>` storage. `None` when the slot is absent (the
861    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
862    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
863    /// omitted slot through a `format!("Generated chart for caixa Servico
864    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
865    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
866    /// and [`caixa-feira`]'s `render_flake` folds it through a
867    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
868    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
869    ///
870    /// The `:descricao` slot carries the universal-axis free-form-prose
871    /// chart-description identifier every kind of caixa emits under
872    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
873    /// supplies) — the typed slot's `Option<String>` accept-set
874    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
875    /// chart-description-shape-invalid rejected through
876    /// [`ManifestError::DescricaoInvalid`] past the shared
877    /// [`crate::render::is_chart_description_shape`] predicate the peer
878    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
879    /// load-bearing downstream consumers:
880    ///
881    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
882    ///     gate binding — the universal-axis identity gate wired at
883    ///     caixa-build time.
884    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
885    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
886    ///     chart's `Chart.yaml` `description:` field, which
887    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
888    ///     `WARNING [chart.metadata.description]: description is required`
889    ///     when absent) and which every registry that ingests the chart
890    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
891    ///     chart's canonical one-line prose descriptor.
892    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
893    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
894    ///     header directly beneath the `# <chart-name>` title, which
895    ///     every author who inspects the rendered chart bundle lands at.
896    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
897    ///     top-level fold — the emitted `flake.nix`'s `description`
898    ///     field, which every Nix consumer (`nix flake show`,
899    ///     `nix flake metadata`, downstream flake-registry ingestors)
900    ///     surfaces as the flake's canonical descriptor.
901    ///
902    /// Prior to this lift the `.descricao` field was accessed inline at
903    /// four production sites — [`Self::validate_descricao`]'s
904    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
905    /// caixa-helm `build_chart_yaml`
906    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
907    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
908    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
909    /// `README.md` header fold, and the caixa-feira `render_flake`
910    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
911    /// `description = ""` fold — four open-coded field-accesses that
912    /// expressed no compile-time link back to the typed slot. A future
913    /// extension of the `:descricao` axis to a richer author surface —
914    /// a per-`:descricao` locale-tagged multi-language descriptor map
915    /// (the "one caixa, N language-tagged prose descriptions" arm
916    /// author-tooling internationalization anticipates), a
917    /// per-registry-target length-and-shape overlay the M4 CR
918    /// materializer resolves per-CR (the "ArtifactHub caps description
919    /// at 512 bytes but the internal registry caps at 256" arm), a
920    /// promotion of the plain `Option<String>` byte-string to a richer
921    /// `ChartDescription` newtype guaranteeing the
922    /// `is_chart_description_shape` predicate at the type level — would
923    /// have had to be threaded through all four open-coded copies in
924    /// lockstep or the validate gate and the three emit paths would
925    /// silently disagree on which prose string a given [`Caixa`]
926    /// resolves to (an author's
927    /// `:descricao "Checkout flow orchestration."` would satisfy
928    /// validate while one of the emit paths silently rendered a stale
929    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
930    /// resolution to a typed method on the substrate primitive means
931    /// every downstream consumer of the caixa's per-`Caixa`
932    /// chart-description surface reaches for exactly one typed dispatch
933    /// — the resolver's accept-set migrates as a unit on any future
934    /// axis addition.
935    ///
936    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
937    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
938    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
939    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
940    /// lift folds on. Same "one typed dispatch on the substrate
941    /// primitive, thin projections at each consumer" discipline the
942    /// peer per-`:placement`
943    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
944    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
945    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
946    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
947    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
948    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
949    /// typed-slot atom axes, extended here to the third outer top-level
950    /// `Caixa` universal-axis surface. Named `descricao()` to match the
951    /// storage field's name; the accessor's identity maps onto the
952    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
953    /// carries. The one remaining universal `Option<String>` slot
954    /// (`:edicao`) folds on this pattern next.
955    #[must_use]
956    pub fn descricao(&self) -> Option<&str> {
957        self.descricao.as_deref()
958    }
959
960    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
961    /// accessor every consumer of the top-level manifest's tatara-lisp
962    /// edition-selector axis keys off — returns the author-declared
963    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
964    /// the typed slot's own `Option<String>` storage. `None` when the
965    /// slot is absent (the canonical "omit the slot to defer to the
966    /// substrate's default edition" shape every existing
967    /// [`caixa-resolver`] integration test fixture carries via
968    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
969    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
970    /// arm by construction, so an author-omitted `:edicao` round-trips
971    /// to a build without triggering the year-shape predicate).
972    ///
973    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
974    /// decimal-year language-edition identifier every kind of caixa
975    /// emits under (CAIXA-SDLC §I — the author-facing surface every
976    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
977    /// accept-set (empty-string rejected through
978    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
979    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
980    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
981    /// onto one load-bearing downstream consumer today
982    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
983    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
984    /// future edition-aware substrate consumer the CAIXA-SDLC §I
985    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
986    /// selector every edition-aware build step keys off, the future
987    /// per-edition compatibility-flag overlay the M4 CR materializer
988    /// resolves per-CR, the peer [`Caixa::template`] canonical
989    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
990    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
991    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
992    /// carry `edicao: Some("2026".into())` by construction).
993    ///
994    /// Prior to this lift the `.edicao` field was accessed inline at
995    /// one production site — [`Self::validate_edicao`]'s
996    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
997    /// open-coded field-access that expressed no compile-time link
998    /// back to the typed slot. A future extension of the `:edicao`
999    /// axis to a richer author surface — a per-`:edicao` known-
1000    /// edition allowlist (the future tightening
1001    /// [`Self::validate_edicao`]'s docstring acknowledges past the
1002    /// structural year-shape floor, rejecting year-shaped values that
1003    /// don't name a tatara-lisp edition the substrate actually
1004    /// understands — `"1999"` is year-shaped but no `1999` edition
1005    /// exists), a per-edition compatibility-flag overlay the M4 CR
1006    /// materializer resolves per-CR (the "edition `"2026"` enables
1007    /// macro-surface features the sibling `"2018"` gates behind a
1008    /// feature flag" arm the edition-selector story anticipates), a
1009    /// promotion of the plain `Option<String>` byte-string to a
1010    /// richer `CaixaEdition` enum discriminated on year once a sibling
1011    /// edition to `"2026"` lands — would have had to be threaded
1012    /// through the open-coded copy in lockstep with every future
1013    /// edition-aware consumer, or the validate gate and the future
1014    /// edition-aware consumer path would silently disagree on which
1015    /// edition a given [`Caixa`] resolves to (an author's
1016    /// `:edicao "2026"` would satisfy validate while a future
1017    /// edition-aware consumer silently defaulted to a stale edition,
1018    /// or vice versa). Lifting the resolution to a typed method on
1019    /// the substrate primitive means every downstream consumer of the
1020    /// caixa's per-`Caixa` edition surface reaches for exactly one
1021    /// typed dispatch — the resolver's accept-set migrates as a unit
1022    /// on any future axis addition.
1023    ///
1024    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
1025    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
1026    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
1027    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
1028    /// `Option<&str>` scalar" projection pattern this lift folds on.
1029    /// Same "one typed dispatch on the substrate primitive, thin
1030    /// projections at each consumer" discipline the peer per-`:placement`
1031    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1032    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1033    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1034    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1035    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1036    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1037    /// typed-slot atom axes, extended here to close the outer top-level
1038    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
1039    /// slot. Named `edicao()` to match the storage field's name; the
1040    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1041    /// vocabulary the slot's docstring already carries.
1042    #[must_use]
1043    pub fn edicao(&self) -> Option<&str> {
1044        self.edicao.as_deref()
1045    }
1046
1047    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
1048    /// label caixa-identity scalar accessor every consumer of the top-
1049    /// level manifest's identity axis keys off — returns the author-
1050    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
1051    /// the typed slot's own `String` storage. Non-optional (`:nome` is
1052    /// a required-axis scalar every `defcaixa` form must supply; the
1053    /// [`Self::from_lisp`] derive rejects an omitted / non-string
1054    /// `:nome` at parse time, so a `Caixa` past parse definitionally
1055    /// carries a non-`None` `:nome`).
1056    ///
1057    /// The `:nome` slot carries the universal-axis DNS-1123-label
1058    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
1059    /// the primary identity axis every `defcaixa` form supplies
1060    /// alongside `:versao` / `:kind`; the substrate-wide identity every
1061    /// other typed surface that names a caixa reaches through — `:deps`
1062    /// entries, `:membros` entries, `:children` entries, the
1063    /// `lareira-<nome>` Helm chart name every per-Servico renderer
1064    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
1065    /// renderer emits) — the typed slot's `String` accept-set (empty
1066    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
1067    /// invalid rejected through [`ManifestError::NomeInvalid`] past
1068    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
1069    /// the peer name axes each land on, joint-length-with-`lareira-`-
1070    /// prefix rejected through
1071    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
1072    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
1073    /// load-bearing downstream consumer the substrate carries — the
1074    /// two universal-axis validate gates at caixa-build time
1075    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
1076    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
1077    /// derivation every per-Servico renderer keys off, the caixa-helm
1078    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
1079    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
1080    /// `HTTPRoute` per-Aplicacao name axes at
1081    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
1082    /// [`crate::pleme_program_selector`] /
1083    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
1084    /// derivations, and every future substrate renderer that emits an
1085    /// artifact keyed by the caixa's identity.
1086    ///
1087    /// Prior to this lift the `.nome` field was accessed inline at a
1088    /// dozen production sites across `caixa-core` (the two universal-
1089    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
1090    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
1091    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
1092    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
1093    /// entry `name:` fold, the `flux_kustomization_source_subtree`
1094    /// per-cluster subpath derivation), and `caixa-mesh` (the
1095    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
1096    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
1097    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
1098    /// insert) — a dozen open-coded field-accesses that expressed no
1099    /// compile-time link back to the typed slot. A future extension of
1100    /// the `:nome` axis to a richer author surface — a per-`:nome`
1101    /// structured `CaixaIdentity` newtype that carries the joint-
1102    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
1103    /// enforces at the type level (rather than as a validate-time
1104    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
1105    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
1106    /// `partner-org/checkout` collision" arm the multi-tenant-registry
1107    /// story acknowledges), a promotion of the plain `String` byte-
1108    /// string to a richer `CaixaNome` newtype discriminated on
1109    /// namespace prefix — would have had to be threaded through every
1110    /// open-coded copy in lockstep or the two validate gates and the
1111    /// dozen emit paths would silently disagree on which identity a
1112    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
1113    /// would satisfy validate while one of the emit paths silently
1114    /// rendered a drifted other identity, or vice versa). Lifting the
1115    /// resolution to a typed method on the substrate primitive means
1116    /// every downstream consumer of the caixa's per-`Caixa` identity
1117    /// surface reaches for exactly one typed dispatch — the resolver's
1118    /// accept-set migrates as a unit on any future axis addition.
1119    ///
1120    /// First outer top-level [`Caixa`] `&str`-return required-scalar
1121    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
1122    /// projection pattern the sibling per-`Caixa` `:versao` future lift
1123    /// folds on. Sibling in shape to the peer per-`:membros`
1124    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
1125    /// [`crate::aplicacao::WitContract::source`] /
1126    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
1127    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
1128    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
1129    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
1130    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
1131    /// per-sub-struct required-axis accessors carry on the sibling M3
1132    /// mesh-slot-atom scalar-value axes, extended here to open the
1133    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
1134    /// Named `nome()` 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 nome(&self) -> &str {
1139        &self.nome
1140    }
1141
1142    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
1143    /// pinned-version scalar accessor every consumer of the top-level
1144    /// manifest's version axis keys off — returns the author-declared
1145    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
1146    /// typed slot's own `String` storage. Non-optional (`:versao` is a
1147    /// required-axis scalar every `defcaixa` form must supply alongside
1148    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
1149    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
1150    /// parse definitionally carries a non-`None` `:versao`).
1151    ///
1152    /// The `:versao` slot carries the universal-axis SemVer-2
1153    /// concrete-version body every kind of caixa emits under
1154    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
1155    /// supplies alongside `:nome` / `:kind`; the substrate-wide
1156    /// pinned-version every downstream artifact-emitting consumer
1157    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
1158    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
1159    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
1160    /// prefix composes on top of, the programs.yaml entry's `versao:`
1161    /// value the `lareira-fleet-programs` aggregator carries onto each
1162    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
1163    /// tags every substrate-side `skopeo push` writes, the lacre
1164    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
1165    /// prior-version references peers in the exact same SemVer-2 shape).
1166    /// The typed slot's `String` accept-set (empty rejected through
1167    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
1168    /// through [`ManifestError::VersaoInvalid`] past
1169    /// [`semver::Version::parse`]) maps onto every load-bearing
1170    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
1171    /// universal-axis validate gate at caixa-build time, the
1172    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
1173    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
1174    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
1175    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
1176    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
1177    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
1178    /// tag derivation (`format!("{prefix}{versao}")`), and every future
1179    /// substrate renderer that emits an artifact keyed by the caixa's
1180    /// pinned version.
1181    ///
1182    /// Prior to this lift the `.versao` field was accessed inline at a
1183    /// dozen production sites across `caixa-core` (the universal-axis
1184    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
1185    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
1186    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
1187    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
1188    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
1189    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
1190    /// (the `feira publish` git-tag derivation + the `feira app graph` /
1191    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
1192    /// field-accesses that expressed no compile-time link back to the
1193    /// typed slot. A future extension of the `:versao` axis to a richer
1194    /// author surface — a per-`:versao` structured `CaixaVersion` at the
1195    /// storage layer (the substrate already carries a `CaixaVersion`
1196    /// newtype at [`crate::version::CaixaVersion`], deferred until the
1197    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
1198    /// a per-registry `:versao` immutability overlay the M4 CR
1199    /// materializer enforces per-CR, a promotion of the plain `String`
1200    /// byte-string to a richer `PinnedVersao` newtype discriminated on
1201    /// SemVer-2 pre-release / build-metadata presence — would have had
1202    /// to be threaded through every open-coded copy in lockstep or the
1203    /// validate gate and the dozen emit paths would silently disagree
1204    /// on which version a given [`Caixa`] resolves to (an author's
1205    /// `:versao "0.1.0"` would satisfy validate while one of the emit
1206    /// paths silently rendered a drifted other version, or vice versa).
1207    /// Lifting the resolution to a typed method on the substrate
1208    /// primitive means every downstream consumer of the caixa's
1209    /// per-`Caixa` pinned-version surface reaches for exactly one typed
1210    /// dispatch — the resolver's accept-set migrates as a unit on any
1211    /// future axis addition.
1212    ///
1213    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
1214    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
1215    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
1216    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
1217    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
1218    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
1219    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
1220    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
1221    /// on the sibling per-typed-slot version-carrier axes, extended here
1222    /// to close the second outer top-level [`Caixa`] required-`&str`-
1223    /// carrying axis so the two universal-axis identity-carrying
1224    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
1225    /// share the same "one typed dispatch per axis" discipline. Named
1226    /// `versao()` to match the storage field's name; the accessor's
1227    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1228    /// slot's docstring already carries.
1229    #[must_use]
1230    pub fn versao(&self) -> &str {
1231        &self.versao
1232    }
1233
1234    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
1235    /// closed-set-enum discriminant accessor every consumer of the top-
1236    /// level manifest's kind axis keys off — returns the author-declared
1237    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
1238    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
1239    /// (`:kind` is a required-axis discriminant every `defcaixa` form
1240    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
1241    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
1242    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
1243    /// variant).
1244    ///
1245    /// The `:kind` slot carries the universal-axis closed-set typed-
1246    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
1247    /// §I — the primary shape gate every renderer / verifier /
1248    /// operator branches on; the five variants `Biblioteca` /
1249    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
1250    /// the caixa surface into disjoint runtime contracts) — the typed
1251    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
1252    /// values through the derive-macro's symbol-arm gate, exhaustively
1253    /// matched at every downstream dispatch site) maps onto every
1254    /// load-bearing downstream consumer the substrate carries:
1255    ///
1256    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
1257    ///     predicate — the canonical two-line
1258    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
1259    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
1260    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
1261    ///     ComputeUnit` CR materializer) runs at its entry-point,
1262    ///     alongside the [`crate::render::KindMismatch`] error carrier's
1263    ///     `actual:` field the diagnostic surfaces to name the offending
1264    ///     caixa's variant.
1265    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
1266    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
1267    ///     `_view` composers that fold the flat mesh-slot / supervisor-
1268    ///     slot columns into their typed sub-spec only when the kind
1269    ///     matches (returns `None` otherwise); the future per-Servico
1270    ///     M2-view composer (`servico_view`) will follow the same shape.
1271    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
1272    ///     coherence gate — the `!self.kind.requires_exe()` /
1273    ///     `!self.kind.requires_servicos()` predicates that fence
1274    ///     each code-surface slot from the wrong owning kind.
1275    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
1276    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
1277    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
1278    ///     coherence error carriers (`SupervisorOwnsCode` /
1279    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
1280    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
1281    ///     / `ForeignCodeSlot`) which each name the offending caixa's
1282    ///     variant in their `kind:` field.
1283    ///
1284    /// Prior to this lift the `.kind` field was accessed inline at
1285    /// twenty-plus production sites across `caixa-core` (the
1286    /// [`crate::render::require_kind`] entry-gate predicate + the
1287    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
1288    /// composers, the `declared_foreign_code_slots` per-slot kind-
1289    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
1290    /// kind ↔ code-surface predicates + four error carriers) — a score
1291    /// of open-coded field-accesses that expressed no compile-time link
1292    /// back to the typed slot. A future extension of the `:kind` axis
1293    /// to a richer author surface — a per-`:kind` sub-variant discriminant
1294    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
1295    /// variant across the wasm-component / legacy-container / native-
1296    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
1297    /// kind-overlay the M4 CR materializer resolves per-CR (the
1298    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
1299    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
1300    /// enum to a richer `KindWithRuntime` discriminated on the
1301    /// component-model world axis — would have had to be threaded
1302    /// through every open-coded copy in lockstep or the entry gate,
1303    /// the view composers, and the layout invariants would silently
1304    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
1305    /// the resolution to a typed method on the substrate primitive
1306    /// means every downstream consumer of the caixa's per-`Caixa`
1307    /// kind surface reaches for exactly one typed dispatch — the
1308    /// resolver's accept-set migrates as a unit on any future axis
1309    /// addition.
1310    ///
1311    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
1312    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
1313    /// required-discriminant" projection pattern. Sibling in shape to
1314    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
1315    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
1316    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
1317    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
1318    /// on the sibling nested-spec typed-slot discriminator axes,
1319    /// extended here to the outer top-level [`Caixa`] universal-axis
1320    /// surface. Named `kind()` to match the storage field's name;
1321    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
1322    /// vocabulary the slot's docstring already carries.
1323    #[must_use]
1324    pub fn kind(&self) -> CaixaKind {
1325        self.kind
1326    }
1327
1328    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
1329    /// maintainer-name-list slice-accessor every consumer of the top-
1330    /// level manifest's maintainer axis keys off — returns the author-
1331    /// declared `:autores` list verbatim as a `&[String]` slice-view over
1332    /// the same backing buffer the raw `self.autores.as_slice()` field
1333    /// access borrows from. Empty-list-carrying (`:autores` is a default-
1334    /// empty axis every `defcaixa` form supplies with an empty `()` when
1335    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
1336    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1337    /// parse definitionally carries a `Vec<String>` slot — possibly
1338    /// empty — and the returned `&[String]` degenerates to an empty
1339    /// slice on that arm without any silent `None` collapse).
1340    ///
1341    /// The `:autores` slot carries the universal-axis maintainer-name
1342    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1343    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1344    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1345    /// every downstream registry-facing artifact emits under) — the
1346    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1347    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1348    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1349    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1350    /// onto every load-bearing downstream consumer the substrate carries
1351    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1352    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1353    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1354    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1355    /// name, email: None }` record, every future per-`Caixa` registry-
1356    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1357    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1358    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1359    /// the future per-cluster author-notification overlay the M4 CR
1360    /// materializer resolves per-CR).
1361    ///
1362    /// Prior to this lift the `.autores` field was accessed inline at
1363    /// two production sites — [`Self::validate_autores`]'s `for autor
1364    /// in &self.autores` walk that gates every entry through
1365    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1366    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1367    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1368    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1369    /// two open-coded field-accesses that expressed no compile-time link
1370    /// back to the typed slot. A future extension of the `:autores` axis
1371    /// to a richer author surface — a per-`:autores` structured
1372    /// `Maintainer { name, email, url }` at the storage layer once the
1373    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1374    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1375    /// enforces per-CR (the "cluster policy demands every author declare
1376    /// an on-file `mailto:` contact" arm), a promotion of the plain
1377    /// `Vec<String>` byte-string list to a richer
1378    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1379    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1380    /// predicate already resolves through — would have had to be
1381    /// threaded through both open-coded copies in lockstep or the
1382    /// validate gate and the caixa-helm emit path would silently
1383    /// disagree on which authors a given [`Caixa`] resolves to (an
1384    /// author's `:autores ("alice" "bob")` would satisfy validate while
1385    /// the caixa-helm emit path silently rendered a drifted other
1386    /// maintainer list, or vice versa). Lifting the resolution to a
1387    /// typed method on the substrate primitive means every downstream
1388    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1389    /// for exactly one typed dispatch — the resolver's accept-set
1390    /// migrates as a unit on any future axis addition.
1391    ///
1392    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1393    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1394    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1395    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1396    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1397    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1398    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1399    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1400    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1401    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1402    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1403    /// per-M3 typed-slot list axes, extended here to the outer top-level
1404    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1405    /// `&Vec<String>`) because every downstream consumer of the author
1406    /// list treats it as a read-only sequence — the slice-view is the
1407    /// narrowest borrow that supports every present + roadmapped consumer
1408    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1409    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1410    /// reaches for (the storage-side `Vec` remains reachable through the
1411    /// `pub autores` field for the mutation-carrying serde round-trip and
1412    /// per-test fixture-mutation paths). Named `autores()` to match the
1413    /// storage field's name; the accessor's identity maps onto the
1414    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1415    /// carries.
1416    #[must_use]
1417    pub fn autores(&self) -> &[String] {
1418        self.autores.as_slice()
1419    }
1420
1421    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1422    /// registry-search-tag-list slice-accessor every consumer of the
1423    /// top-level manifest's topical-tag axis keys off — returns the
1424    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1425    /// slice-view over the same backing buffer the raw
1426    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1427    /// list-carrying (`:etiquetas` is a default-empty axis every
1428    /// `defcaixa` form supplies with an empty `()` when unset; the
1429    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1430    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1431    /// definitionally carries a `Vec<String>` slot — possibly empty —
1432    /// and the returned `&[String]` degenerates to an empty slice on
1433    /// that arm without any silent `None` collapse).
1434    ///
1435    /// The `:etiquetas` slot carries the universal-axis topical-tag
1436    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1437    /// author-facing surface every `defcaixa` form supplies alongside
1438    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1439    /// search-facing axis every downstream registry-facing artifact
1440    /// emits under) — the typed slot's `Vec<String>` accept-set
1441    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1442    /// non-chart-keyword-shape rejected through
1443    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1444    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1445    /// every load-bearing downstream consumer the substrate carries —
1446    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1447    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1448    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1449    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1450    /// `Chart.yaml` `keywords:` array (chained with the
1451    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1452    /// dedup'd through a `BTreeSet` at emit time), every future per-
1453    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1454    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1455    /// annotation, the future per-cluster tag-notification overlay the
1456    /// M4 CR materializer resolves per-CR).
1457    ///
1458    /// Prior to this lift the `.etiquetas` field was accessed inline at
1459    /// two production sites — [`Self::validate_etiquetas`]'s `for
1460    /// etiqueta in &self.etiquetas` walk that gates every entry through
1461    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1462    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1463    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1464    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1465    /// two open-coded field-accesses that expressed no compile-time
1466    /// link back to the typed slot. A future extension of the
1467    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1468    /// structured `ChartKeyword { name, uri, category }` at the storage
1469    /// layer once the substrate absorbs `artifacthub.io/keywords`
1470    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1471    /// CR materializer enforces per-CR (the "cluster policy demands
1472    /// every tag come from a substrate-approved taxonomy" arm), a
1473    /// promotion of the plain `Vec<String>` byte-string list to a
1474    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1475    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1476    /// already resolves through — would have had to be threaded through
1477    /// both open-coded copies in lockstep or the validate gate and the
1478    /// caixa-helm emit path would silently disagree on which tags a
1479    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1480    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1481    /// path silently rendered a drifted other keyword list, or vice
1482    /// versa). Lifting the resolution to a typed method on the
1483    /// substrate primitive means every downstream consumer of the
1484    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1485    /// typed dispatch — the resolver's accept-set migrates as a unit
1486    /// on any future axis addition.
1487    ///
1488    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1489    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1490    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1491    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1492    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1493    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1494    /// fold onto the same pattern in future lifts. Sibling in shape to
1495    /// the peer per-`:supervisor`
1496    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1497    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1498    /// (a6e18d7), per-`:membros`
1499    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1500    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1501    /// (0dcc926), and per-`:upgrade-from :instructions`
1502    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1503    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1504    /// typed-slot list axes, extended here to the outer top-level
1505    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1506    /// `&Vec<String>`) because every downstream consumer of the tag
1507    /// list treats it as a read-only sequence — the slice-view is the
1508    /// narrowest borrow that supports every present + roadmapped
1509    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1510    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1511    /// the typed view reaches for (the storage-side `Vec` remains
1512    /// reachable through the `pub etiquetas` field for the mutation-
1513    /// carrying serde round-trip and per-test fixture-mutation paths).
1514    /// Named `etiquetas()` to match the storage field's name; the
1515    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1516    /// vocabulary the slot's docstring already carries.
1517    #[must_use]
1518    pub fn etiquetas(&self) -> &[String] {
1519        self.etiquetas.as_slice()
1520    }
1521
1522    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1523    /// library-source-path-list slice-accessor every consumer of the
1524    /// top-level manifest's Biblioteca-source axis keys off — returns
1525    /// the author-declared `:bibliotecas` list verbatim as a
1526    /// `&[String]` slice-view over the same backing buffer the raw
1527    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1528    /// list-carrying (`:bibliotecas` is a default-empty axis every
1529    /// `defcaixa` form supplies with an empty `()` when unset; the
1530    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1531    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1532    /// parse definitionally carries a `Vec<String>` slot — possibly
1533    /// empty — and the returned `&[String]` degenerates to an empty
1534    /// slice on that arm without any silent `None` collapse).
1535    ///
1536    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1537    /// entry-path list every `:kind Biblioteca` caixa emits under
1538    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1539    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1540    /// substrate-wide library-carrier axis every downstream
1541    /// authoring-facing consumer keys off) — the typed slot's
1542    /// `Vec<String>` accept-set (empty-per-entry rejected through
1543    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1544    /// non-sandboxed-relative-shape rejected through
1545    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1546    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1547    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1548    /// maps onto every load-bearing downstream consumer the substrate
1549    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1550    /// empty-check + per-entry file-exists loop at
1551    /// caixa-core/src/layout.rs that gates each entry through
1552    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1553    /// [`Self::validate_code_paths`] per-slot shape gate at
1554    /// caixa-core/src/manifest.rs that walks each entry through the
1555    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1556    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1557    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1558    /// declared library file for lexical / structural errors before
1559    /// downstream `importar` resolution, every future per-`Caixa`
1560    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1561    /// (the future `tatara-lispc` compilation entry the docstring at
1562    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1563    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1564    /// the future `caixa-lsp` per-library semantic-token stream the
1565    /// caixa-lsp docstring roadmaps).
1566    ///
1567    /// Prior to this lift the `.bibliotecas` field was accessed inline
1568    /// at three production sites — [`crate::LayoutInvariants`]'s
1569    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1570    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1571    /// declared library path through the on-disk-existence check,
1572    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1573    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1574    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1575    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1576    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1577    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1578    /// coded field-accesses that expressed no compile-time link back
1579    /// to the typed slot. A future extension of the `:bibliotecas`
1580    /// axis to a richer library surface — a per-`:bibliotecas`
1581    /// structured `BibliotecaEntry { path, edition, exports }` at the
1582    /// storage layer once the substrate absorbs the per-library
1583    /// language-edition + explicit-exports tuple the tatara-lisp
1584    /// module-system roadmap acknowledges, a per-registry
1585    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1586    /// per-CR (the "cluster policy demands every biblioteca declare
1587    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1588    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1589    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1590    /// [`crate::render::is_sandboxed_relative_path`] +
1591    /// [`crate::render::is_lisp_extension`] predicates already resolve
1592    /// through — would have had to be threaded through all three
1593    /// open-coded copies in lockstep or the layout gate, the shape
1594    /// validator, and the `feira build` phase-1 parse walk would
1595    /// silently disagree on which library paths a given [`Caixa`]
1596    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1597    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1598    /// silently parsed a drifted other list, or vice versa). Lifting
1599    /// the resolution to a typed method on the substrate primitive
1600    /// means every downstream consumer of the caixa's per-`Caixa`
1601    /// library-source surface reaches for exactly one typed dispatch
1602    /// — the resolver's accept-set migrates as a unit on any future
1603    /// axis addition.
1604    ///
1605    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1606    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1607    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1608    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1609    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1610    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1611    /// `:children` / `:membros` / `:contratos`) fold onto the same
1612    /// pattern in future lifts. Sibling in shape to the peer
1613    /// per-`:supervisor`
1614    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1615    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1616    /// (a6e18d7), per-`:membros`
1617    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1618    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1619    /// (0dcc926), and per-`:upgrade-from :instructions`
1620    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1621    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1622    /// typed-slot list axes, extended here to the outer top-level
1623    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1624    /// `&Vec<String>`) because every downstream consumer of the
1625    /// library-source list treats it as a read-only sequence — the
1626    /// slice-view is the narrowest borrow that supports every
1627    /// present + roadmapped consumer (`.iter()`, `.len()`,
1628    /// `.is_empty()`) without leaking the backing `Vec`'s
1629    /// grow/push/reserve surface no consumer of the typed view
1630    /// reaches for (the storage-side `Vec` remains reachable through
1631    /// the `pub bibliotecas` field for the mutation-carrying serde
1632    /// round-trip and per-test fixture-mutation paths). Named
1633    /// `bibliotecas()` to match the storage field's name; the
1634    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1635    /// vocabulary the slot's docstring already carries.
1636    #[must_use]
1637    pub fn bibliotecas(&self) -> &[String] {
1638        self.bibliotecas.as_slice()
1639    }
1640
1641    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1642    /// nix-built-executable-entry-path-list slice-accessor every consumer
1643    /// of the top-level manifest's Binario-executable axis keys off —
1644    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1645    /// slice-view over the same backing buffer the raw
1646    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1647    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1648    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1649    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1650    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1651    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1652    /// degenerates to an empty slice on that arm without any silent
1653    /// `None` collapse).
1654    ///
1655    /// The `:exe` slot carries the universal-axis nix-built executable
1656    /// entry-path list every `:kind Binario` caixa emits under
1657    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1658    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1659    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1660    /// downstream flake-build-facing consumer keys off) — the typed
1661    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1662    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1663    /// non-sandboxed-relative-shape rejected through
1664    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1665    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1666    /// directory paths rejected past the layout's
1667    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1668    /// onto every load-bearing downstream consumer the substrate carries
1669    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1670    /// per-entry file-exists + `exe/`-directory-fence loop at
1671    /// caixa-core/src/layout.rs that gates each entry through
1672    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1673    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1674    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1675    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1676    /// that fences code-surface slots off from the two no-code kinds,
1677    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1678    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1679    /// fences the `:exe` code surface off from every non-Binario code-
1680    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1681    /// that walks each entry through the sandbox-relative / cross-entry
1682    /// duplicate gates, every future per-`Caixa` executable-facing
1683    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1684    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1685    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1686    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1687    /// future `feira nix` per-executable Binario-target emit path).
1688    ///
1689    /// Prior to this lift the `.exe` field was accessed inline at three
1690    /// production sites — the compound-code-path `has_code =
1691    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1692    /// !caixa.servicos.is_empty()` OR-fold on the
1693    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1694    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1695    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1696    /// gate, the per-entry `for p in &caixa.exe`
1697    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1698    /// [`Self::declared_foreign_code_slots`]'s
1699    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1700    /// open-coded field-accesses that expressed no compile-time link
1701    /// back to the typed slot. A future extension of the `:exe` axis
1702    /// to a richer executable surface — a per-`:exe` structured
1703    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1704    /// layer once the substrate absorbs the per-executable
1705    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1706    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1707    /// the M4 CR materializer enforces per-CR (the "cluster policy
1708    /// demands every Binario declare an explicit `:wrapper`" arm), a
1709    /// promotion of the plain `Vec<String>` byte-string list to a
1710    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1711    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1712    /// fence already resolves through — would have had to be threaded
1713    /// through all four open-coded copies in lockstep or the layout
1714    /// gate, the shape validator, and the `feira nix` emit path would
1715    /// silently disagree on which executable paths a given [`Caixa`]
1716    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1717    /// satisfy layout while `feira nix` silently packaged a drifted
1718    /// other list, or vice versa). Lifting the resolution to a typed
1719    /// method on the substrate primitive means every downstream
1720    /// consumer of the caixa's per-`Caixa` executable-source surface
1721    /// reaches for exactly one typed dispatch — the resolver's accept-
1722    /// set migrates as a unit on any future axis addition.
1723    ///
1724    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1725    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1726    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1727    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1728    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1729    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1730    /// future lift closes onto (per the trio of code-surface list slots
1731    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1732    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1733    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1734    /// last unlifted code-surface slot). Sibling in shape to the peer
1735    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1736    /// (bc92bce), per-`:placement`
1737    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1738    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1739    /// (6c77e36), per-`:contratos`
1740    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1741    /// per-`:upgrade-from :instructions`
1742    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1743    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1744    /// typed-slot list axes, extended here to the outer top-level
1745    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1746    /// `&Vec<String>`) because every downstream consumer of the
1747    /// executable-source list treats it as a read-only sequence — the
1748    /// slice-view is the narrowest borrow that supports every
1749    /// present + roadmapped consumer (`.iter()`, `.len()`,
1750    /// `.is_empty()`) without leaking the backing `Vec`'s
1751    /// grow/push/reserve surface no consumer of the typed view
1752    /// reaches for (the storage-side `Vec` remains reachable through
1753    /// the `pub exe` field for the mutation-carrying serde
1754    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1755    /// to match the storage field's name; the accessor's identity
1756    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1757    /// docstring already carries.
1758    #[must_use]
1759    pub fn exe(&self) -> &[String] {
1760        self.exe.as_slice()
1761    }
1762
1763    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1764    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1765    /// of the top-level manifest's Servico-component axis keys off —
1766    /// returns the author-declared `:servicos` list verbatim as a
1767    /// `&[String]` slice-view over the same backing buffer the raw
1768    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1769    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1770    /// form supplies with an empty `()` when unset; the
1771    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1772    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1773    /// definitionally carries a `Vec<String>` slot — possibly empty —
1774    /// and the returned `&[String]` degenerates to an empty slice on
1775    /// that arm without any silent `None` collapse).
1776    ///
1777    /// The `:servicos` slot carries the universal-axis
1778    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1779    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1780    /// author-facing surface every `defcaixa` form supplies alongside
1781    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1782    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1783    /// Servico-facing renderer keys off) — the typed slot's
1784    /// `Vec<String>` accept-set (empty-per-entry rejected through
1785    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1786    /// non-sandboxed-relative-shape rejected through
1787    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1788    /// extension rejected through
1789    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1790    /// entry duplicate rejected through
1791    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1792    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1793    /// renderer entry-points, out-of-`servicos/`-directory paths
1794    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1795    /// `starts_with` fence) maps onto every load-bearing downstream
1796    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1797    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1798    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1799    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1800    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1801    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1802    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1803    /// that fences code-surface slots off from the two no-code kinds,
1804    /// [`Self::declared_foreign_code_slots`]'s
1805    /// `!self.servicos.is_empty()` arm on the
1806    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1807    /// `:servicos` code surface off from every non-Servico code-running
1808    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1809    /// walks each entry through the sandbox-relative / `.computeunit.
1810    /// yaml`-extension / cross-entry duplicate gates, the
1811    /// [`crate::require_single_servico`] V0 singularity gate every
1812    /// per-Servico renderer entry-point runs through
1813    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1814    /// `feira deploy` per-verb `first_servico_path` walk at
1815    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1816    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1817    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1818    /// per-Servico OCI packager, the future M4
1819    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1820    /// per-Servico OTel collector-config emit).
1821    ///
1822    /// Prior to this lift the `.servicos` field was accessed inline at
1823    /// five production sites — the compound-code-path `has_code =
1824    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1825    /// !caixa.servicos.is_empty()` OR-fold on the
1826    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1827    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1828    /// `caixa.servicos.is_empty()`
1829    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1830    /// per-entry `for p in &caixa.servicos`
1831    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1832    /// [`Self::declared_foreign_code_slots`]'s
1833    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1834    /// and the [`crate::require_single_servico`] V0 count gate's
1835    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1836    /// projection (both the accept-arm predicate and the
1837    /// diagnostic-carrying `ServicoCountMismatch { count }`
1838    /// projection) — five open-coded field-accesses across three
1839    /// crates that expressed no compile-time link back to the typed
1840    /// slot. A future extension of the `:servicos` axis to a richer
1841    /// component surface — a per-`:servicos` structured
1842    /// `ServicoEntry { path, world, capabilities }` at the storage
1843    /// layer once the substrate absorbs the per-component WIT-world +
1844    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1845    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1846    /// materializer enforces per-CR (the "cluster policy demands every
1847    /// Servico declare an explicit `:world`" arm), a promotion of the
1848    /// plain `Vec<String>` byte-string list to a richer
1849    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1850    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1851    /// `starts_with(servicos_dir)` fence and the
1852    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1853    /// already resolve through, a promotion of the V0 singleton
1854    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1855    /// component-model multi-world boundary — would have had to be
1856    /// threaded through all five open-coded copies in lockstep or the
1857    /// layout gate, the shape validator, the V0 count gate, and the
1858    /// `feira chart` / `feira deploy` entry-point walks would silently
1859    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1860    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1861    /// yaml")` would satisfy layout while `feira chart` silently
1862    /// packaged a drifted other list, or vice versa). Lifting the
1863    /// resolution to a typed method on the substrate primitive means
1864    /// every downstream consumer of the caixa's per-`Caixa`
1865    /// ComputeUnit-CR-source surface reaches for exactly one typed
1866    /// dispatch — the resolver's accept-set migrates as a unit on any
1867    /// future axis addition.
1868    ///
1869    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1870    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1871    /// projection pattern [`Self::autores`] (b5d813f) opened,
1872    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1873    /// (8a36c23) closed the universal-axis text-tag family of, and
1874    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1875    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1876    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1877    /// a substrate-canonical slice accessor, the trio of code-surface
1878    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1879    /// tuple carries is complete on the typed dispatch surface (the
1880    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1881    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1882    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1883    /// per-element accessor swap in isolation — a future companion lift
1884    /// promotes the tuple's element type to `&[String]` and threads the
1885    /// triple of typed dispatches through as a unit). Sibling in shape
1886    /// to the peer per-`:supervisor`
1887    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1888    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1889    /// (a6e18d7), per-`:membros`
1890    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1891    /// per-`:contratos`
1892    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1893    /// per-`:upgrade-from :instructions`
1894    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1895    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1896    /// typed-slot list axes, extended here to the outer top-level
1897    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1898    /// `&Vec<String>`) because every downstream consumer of the
1899    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1900    /// the slice-view is the narrowest borrow that supports every
1901    /// present + roadmapped consumer (`.iter()`, `.len()`,
1902    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1903    /// grow/push/reserve surface no consumer of the typed view reaches
1904    /// for (the storage-side `Vec` remains reachable through the
1905    /// `pub servicos` field for the mutation-carrying serde round-trip
1906    /// and per-test fixture-mutation paths, and for the
1907    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1908    /// homogeneous-element-type shape carries the raw field access
1909    /// until the trio-closure lift promotes the tuple as a unit).
1910    /// Named `servicos()` to match the storage field's name; the
1911    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1912    /// vocabulary the slot's docstring already carries.
1913    #[must_use]
1914    pub fn servicos(&self) -> &[String] {
1915        self.servicos.as_slice()
1916    }
1917
1918    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
1919    /// runtime-dependency-declaration-list slice-accessor every consumer
1920    /// of the top-level manifest's runtime-dep-graph axis keys off —
1921    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
1922    /// slice-view over the same backing buffer the raw
1923    /// `self.deps.as_slice()` field access borrows from. Empty-list-
1924    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
1925    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1926    /// derive folds an omitted `:deps` through `#[serde(default)]` to
1927    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1928    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
1929    /// degenerates to an empty slice on that arm without any silent
1930    /// `None` collapse).
1931    ///
1932    /// The `:deps` slot carries the universal-axis runtime dependency
1933    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1934    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1935    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
1936    /// every downstream resolver-facing artifact emits under) — the
1937    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
1938    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
1939    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
1940    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
1941    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
1942    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
1943    /// maps onto every load-bearing downstream consumer the substrate
1944    /// carries — the [`Self::validate_deps`] per-entry
1945    /// [`Dep::validate`] + within-list dedup walk at
1946    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
1947    /// cross-list self-reference gate at caixa-core/src/layout.rs that
1948    /// checks each entry against the caixa's own `:nome`, the
1949    /// caixa-resolver `for dep in &root.deps` closure walk at
1950    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
1951    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
1952    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
1953    /// caixa-crd/src/conversion.rs that materializes each entry into the
1954    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
1955    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1956    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
1957    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
1958    /// closure emit walk the caixa-resolver docstring roadmaps).
1959    ///
1960    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1961    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1962    /// sibling `:deps-dev` future lift closes on. Peer of the closed
1963    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
1964    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
1965    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
1966    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
1967    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
1968    /// pattern onto a novel element-type axis (`Dep` composite vs the
1969    /// prior sibling family's `String` scalar). Sibling in shape to the
1970    /// peer per-`:supervisor`
1971    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1972    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1973    /// (a6e18d7), per-`:membros`
1974    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1975    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1976    /// (0dcc926), and per-`:upgrade-from :instructions`
1977    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1978    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1979    /// typed-slot list axes, extended here to the outer top-level
1980    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
1981    /// (not `&Vec<Dep>`) because every downstream consumer of the
1982    /// runtime-dep list treats it as a read-only sequence — the slice-
1983    /// view is the narrowest borrow that supports every present +
1984    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1985    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1986    /// of the typed view reaches for (the storage-side `Vec` remains
1987    /// reachable through the `pub deps` field for the mutation-carrying
1988    /// serde round-trip and per-test fixture-mutation paths). Named
1989    /// `deps()` to match the storage field's name; the accessor's
1990    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1991    /// slot's docstring already carries.
1992    #[must_use]
1993    pub fn deps(&self) -> &[Dep] {
1994        self.deps.as_slice()
1995    }
1996
1997    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
1998    /// development-only-dependency-declaration-list slice-accessor every
1999    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
2000    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
2001    /// slice-view over the same backing buffer the raw
2002    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
2003    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
2004    /// form supplies with an empty `()` when unset; the
2005    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
2006    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
2007    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
2008    /// the returned `&[Dep]` degenerates to an empty slice on that arm
2009    /// without any silent `None` collapse).
2010    ///
2011    /// The `:deps-dev` slot carries the universal-axis dev-only
2012    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
2013    /// the author-facing sibling of `:deps` that every `defcaixa` form
2014    /// supplies to declare tests / lint / bench closures the runtime
2015    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
2016    /// axis every downstream test-facing artifact emits under, matching
2017    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
2018    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
2019    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
2020    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
2021    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
2022    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
2023    /// within-list duplicate `:nome` rejected through
2024    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
2025    /// load-bearing downstream consumer the substrate carries — the
2026    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
2027    /// dedup walk at caixa-core/src/manifest.rs, the
2028    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
2029    /// gate at caixa-core/src/layout.rs that checks each entry against
2030    /// the caixa's own `:nome`, the caixa-resolver
2031    /// `for dep in &root.deps_dev` closure walk at
2032    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
2033    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
2034    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
2035    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
2036    /// overlay the M4 CR materializer resolves per-CR, the future
2037    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
2038    /// roadmaps).
2039    ///
2040    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2041    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2042    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
2043    /// jointly close the two-list dep-graph surface every downstream
2044    /// resolver-facing consumer keys off (runtime `:deps` +
2045    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
2046    /// pair the [`Self::validate_deps`] gate already walks in canonical
2047    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
2048    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
2049    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
2050    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
2051    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
2052    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
2053    /// dev-dep composite-element axis (`Dep` composite, matching the
2054    /// [`Self::deps`] element type). Sibling in shape to the peer
2055    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
2056    /// (bc92bce), per-`:placement`
2057    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
2058    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
2059    /// (6c77e36), per-`:contratos`
2060    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
2061    /// per-`:upgrade-from :instructions`
2062    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2063    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2064    /// typed-slot list axes, folded here to the outer top-level
2065    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
2066    /// (not `&Vec<Dep>`) because every downstream consumer of the
2067    /// dev-dep list treats it as a read-only sequence — the slice-view
2068    /// is the narrowest borrow that supports every present +
2069    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2070    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2071    /// of the typed view reaches for (the storage-side `Vec` remains
2072    /// reachable through the `pub deps_dev` field for the mutation-
2073    /// carrying serde round-trip and per-test fixture-mutation paths).
2074    /// Named `deps_dev()` to match the storage field's `snake_case` name;
2075    /// the kebab-case author-surface tag `:deps-dev` is the same axis
2076    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
2077    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
2078    /// docstring already carries.
2079    #[must_use]
2080    pub fn deps_dev(&self) -> &[Dep] {
2081        self.deps_dev.as_slice()
2082    }
2083
2084    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
2085    /// every consumer that walks one of the two dep-list axes keyed on a
2086    /// [`crate::dep::DepList`] discriminant reaches for — routes the
2087    /// `(list: DepList) -> &[Dep]` projection through one typed method on
2088    /// the substrate primitive rather than the prior open-coded
2089    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
2090    /// inline dispatch every per-axis walker would otherwise carry.
2091    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
2092    /// `&[Dep]` slice-view over the same backing buffer the sibling
2093    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
2094    /// accessors borrow from, preserving the empty-list-carrying invariant
2095    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
2096    /// are default-empty axes every `defcaixa` form supplies with an empty
2097    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
2098    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
2099    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
2100    /// returned `&[Dep]` degenerates to an empty slice on either arm
2101    /// without any silent `None` collapse).
2102    ///
2103    /// The [`crate::dep::DepList`] closed-set typed enum is the
2104    /// substrate's canonical discriminator for the "runtime-closure
2105    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
2106    /// consumer dispatches on — the compiler-checked exhaustiveness on
2107    /// the enum's `match` arms is the build-time guarantee that no future
2108    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
2109    /// that a future third dep-list axis (a `:deps-build` build-only
2110    /// closure once the substrate grows cross-artifact heterogeneous
2111    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
2112    /// consumer. Prior to this the read side carried two per-slot
2113    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
2114    /// typed dispatch that a per-axis walker could parametrise on, so
2115    /// every per-list walker (the [`Self::validate_deps`] per-list
2116    /// [`crate::render::insert_first_seen`] dedup walk, a future
2117    /// `feira app graph` per-list dep summary, a future M4 per-cluster
2118    /// dev-closure-audit overlay the CR materializer resolves per-CR)
2119    /// open-coded the same two-block "run over `:deps`, then run over
2120    /// `:deps-dev`" pattern — a silent duplication that a future third
2121    /// dep-list axis would have had to grow a third block at every site.
2122    ///
2123    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
2124    /// (359fba5) — closes the two-side dispatch symmetry on the outer
2125    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
2126    /// side, `deps_of` on the read side, both keyed on the same
2127    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
2128    /// the substrate primitive, thin projections at each consumer"
2129    /// discipline the sibling per-slot read accessors ([`Self::nome`]
2130    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
2131    /// the outer-[`Caixa`] typed-dispatch read surface.
2132    #[must_use]
2133    pub fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
2134        match list {
2135            crate::dep::DepList::Prod => self.deps(),
2136            crate::dep::DepList::Dev => self.deps_dev(),
2137        }
2138    }
2139
2140    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
2141    /// consumer that appends to one of the two dep-list axes keys off
2142    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
2143    /// method on the substrate primitive rather than the prior
2144    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
2145    /// else { &mut caixa.deps }` inline dispatch + open-coded
2146    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
2147    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
2148    /// a within-list name collision — the same `list: &'static str`
2149    /// diagnostic shape [`Self::validate_deps`]'s per-list
2150    /// [`crate::render::insert_first_seen`] walk raises on the peer
2151    /// parse-time within-list dedup axis, so a future author reading a
2152    /// `feira add` refusal and a `feira build` refusal reaches for the
2153    /// same corrective surface without switching diagnostic idioms.
2154    ///
2155    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
2156    /// closed-set typed carrier for the "runtime-closure `:deps` vs
2157    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
2158    /// dispatches on — the compiler-checked exhaustiveness on the
2159    /// enum's `match` arms is the build-time guarantee that no future
2160    /// per-list mutation-site regresses to a bare-`bool`-flag
2161    /// (`is_dev: bool`) inline dispatch that a future third
2162    /// dep-list axis (a `:deps-build` build-only closure once the
2163    /// substrate grows cross-artifact heterogeneous dep-graphs, per
2164    /// CAIXA-SDLC §I) would silently split at every consumer.
2165    ///
2166    /// Same "one typed dispatch on the substrate primitive, thin
2167    /// projections at each consumer" discipline the sibling per-slot
2168    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
2169    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
2170    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
2171    /// the substrate's first typed-mutation dispatch on the top-level
2172    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
2173    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
2174    /// diagnostic path routed no through-line back to the typed slot,
2175    /// so a future extension of either dep-list axis to a richer author
2176    /// surface (a per-cluster override the operator pins through a
2177    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
2178    /// roadmap acknowledges, an M4
2179    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
2180    /// admission-webhook that normalized the list at admission time)
2181    /// would have had to be threaded through the `feira add` mutation
2182    /// site in lockstep with every read consumer or one path would
2183    /// silently disagree with the other on which list a given dep lands
2184    /// in. Lifting the resolution rule to a typed method on the
2185    /// substrate primitive means every downstream dep-list-mutating
2186    /// consumer of the top-level manifest reaches for exactly one typed
2187    /// dispatch — the resolver's accept-set migrates as a unit on any
2188    /// future axis addition.
2189    ///
2190    /// # Errors
2191    ///
2192    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
2193    /// when another entry in the same list already carries the same
2194    /// `:nome` — the mutation is refused and the caller can surface the
2195    /// typed diagnostic to the author (the `feira add` verb routes the
2196    /// error through `anyhow::Error::from`, which preserves the
2197    /// canonical `#[error(...)]`-templated diagnostic body).
2198    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
2199        let target = match list {
2200            crate::dep::DepList::Prod => &mut self.deps,
2201            crate::dep::DepList::Dev => &mut self.deps_dev,
2202        };
2203        if target.iter().any(|d| d.nome() == dep.nome()) {
2204            return Err(DepError::DuplicateNome {
2205                nome: dep.nome().to_string(),
2206                list: list.as_str(),
2207            });
2208        }
2209        target.push(dep);
2210        Ok(())
2211    }
2212
2213    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
2214    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
2215    /// composite-reference accessor every consumer of the top-level
2216    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
2217    /// off — returns the author-declared `:limits` typed composite
2218    /// verbatim as an `Option<&LimitsSpec>` reference over the same
2219    /// backing storage the raw `self.limits.as_ref()` field access
2220    /// borrows from, with `None` naming the "no `:limits` block
2221    /// authored — every per-axis Lunatic-sandbox cap defers to the
2222    /// wasm-engine-default arm named on the per-axis
2223    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
2224    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
2225    /// docstrings" partition every downstream Servico-M2-overlay
2226    /// emitter treats as "emit nothing" and the sibling
2227    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
2228    /// treats as "skip the per-axis
2229    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
2230    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
2231    ///
2232    /// The outer `:limits` slot carries the M2 Servico-runtime typed
2233    /// composite — the load-bearing container of every Lunatic-shaped
2234    /// per-process wasm32-sandbox cap axis every long-running wasm
2235    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
2236    /// Lunatic per-process linear-memory / fuel / wall-clock /
2237    /// millicore cap primitives translated onto pleme-io's typed
2238    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
2239    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2240    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2241    /// chart both fan on). Every per-`:limits` axis threads through a
2242    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
2243    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
2244    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
2245    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
2246    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
2247    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
2248    /// consumer that reaches for a limits axis first passes through
2249    /// this outer accessor onto the composite and then dispatches
2250    /// onto the per-axis accessor — the two-level dispatch means
2251    /// every per-`:limits` reader now routes through a typed dispatch
2252    /// on the substrate primitive at both altitudes.
2253    ///
2254    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
2255    /// was accessed inline at three production sites — the
2256    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
2257    /// `if let Some(l) = &caixa.limits { … }` traversal head
2258    /// (caixa-core/src/layout.rs:882, which drives the per-axis
2259    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
2260    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
2261    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
2262    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
2263    /// [`LimitsSpec::validate`] fans onto), the
2264    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
2265    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
2266    /// head (caixa-core/src/render.rs:18504, which drives the
2267    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
2268    /// projection every `caixa-helm` / `caixa-flux` Servico values-
2269    /// block emitter fans on), and the
2270    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2271    /// set enumerator's `self.limits.is_some()` presence probe
2272    /// (caixa-core/src/manifest.rs:1788, which drives the
2273    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
2274    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2275    /// gate reads) — three open-coded outer-field accesses that
2276    /// expressed no compile-time link back to the typed slot at the
2277    /// [`Caixa`] altitude. A future extension of the `:limits` outer
2278    /// axis to a richer author surface (a multi-`:limits` list the M4
2279    /// CR materializer resolves per-CR at admission time so a Servico
2280    /// can expose a compute-heavy + IO-heavy limits pair, a per-
2281    /// cluster `:limits-overrides` slot the operator pins so a
2282    /// cluster-specific policy can tighten a caixa-declared cap
2283    /// without re-authoring the `caixa.lisp`, a promotion of the
2284    /// plain `Option<LimitsSpec>` to a richer
2285    /// `{static, dynamic}` partition once the wasm-engine's runtime-
2286    /// resolved dynamic-cap surface lands) would have had to be
2287    /// threaded through all three open-coded copies in lockstep or
2288    /// one consumer would silently disagree with the peers on which
2289    /// limits composite a given Caixa resolves to — the layout gate's
2290    /// per-axis bracket-dispatch seed reading the raw slot while the
2291    /// peer `servico_m2_overlay` emitter read an operator-resolved
2292    /// slot would silently split the build-time sandbox-shape gate
2293    /// from the runtime `ComputeUnit` CR emission gate, a three-
2294    /// consumer split at the layout gate, the M2 overlay emitter, and
2295    /// the declared-slot enumerator far from the source `caixa.lisp`
2296    /// with no field naming the limits-drift root cause. Lifting the
2297    /// resolution rule to a typed method on the substrate primitive
2298    /// means every downstream consumer of the caixa's per-`Caixa`
2299    /// Lunatic-sandboxing outer-composite surface reaches for exactly
2300    /// one typed dispatch — the resolver's accept-set migrates as a
2301    /// unit on any future axis addition.
2302    ///
2303    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
2304    /// composite-reference accessor — opens the outer-`Caixa`
2305    /// `Option<&Composite>` composite-reference projection pattern the
2306    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
2307    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
2308    /// [`crate::aplicacao::Placement`] / `:entrada`
2309    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
2310    /// fold on. Peer of the M3 mesh-slot outer-composite family the
2311    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2312    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2313    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2314    /// accessors already close on the outer [`crate::AplicacaoSpec`]
2315    /// altitude — extends that "one typed dispatch on the substrate
2316    /// primitive, thin projections at each consumer" discipline onto
2317    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2318    /// runtime slot family's outer-composite axis. Returns
2319    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2320    /// clone) because every downstream consumer of the limits
2321    /// composite treats it as a read-only per-axis dispatch source —
2322    /// the reference-view is the narrowest borrow that supports every
2323    /// present + roadmapped consumer (per-axis accessor dispatch,
2324    /// `.is_empty()`-gated overlay projection, presence-probe early
2325    /// return on the "author-omitted `:limits` ⇒ engine-default
2326    /// applies" partition) without cloning the composite through
2327    /// every consumer's fast path. The `Option` half of the return-
2328    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2329    /// engine-default applies" partition (not a default composite the
2330    /// downstream must reject on emptiness) — the accessor projects
2331    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2332    /// reference-return unchanged. Named `limits()` to match the
2333    /// storage field's name verbatim and the tatara-lisp author-
2334    /// surface term (`:limits`) the field's own docstring already
2335    /// carries.
2336    #[must_use]
2337    pub fn limits(&self) -> Option<&LimitsSpec> {
2338        self.limits.as_ref()
2339    }
2340
2341    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2342    /// composite OTP-`gen_server`-shaped callback-table optional-
2343    /// composite-reference accessor every consumer of the top-level
2344    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2345    /// keys off — returns the author-declared `:behavior` typed
2346    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2347    /// the same backing storage the raw `self.behavior.as_ref()` field
2348    /// access borrows from, with `None` naming the "no `:behavior`
2349    /// block authored — every per-callback OTP-shaped hook defers to
2350    /// the wasm-engine's runtime default arm named on the per-axis
2351    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2352    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2353    /// [`BehaviorSpec::on_state_change`] /
2354    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2355    /// partition every downstream Servico-M2-overlay emitter treats as
2356    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2357    /// per-`:behavior` shape gate treats as "skip the per-arm
2358    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2359    /// per-callback on-disk `MissingEntry` existence check".
2360    ///
2361    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2362    /// composite — the load-bearing container of every OTP-shaped
2363    /// per-Servico lifecycle-callback path axis every long-running wasm
2364    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2365    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2366    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2367    /// translated onto pleme-io's typed `:behavior :on-init` /
2368    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2369    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2370    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2371    /// chart both fan on). Every per-`:behavior` axis threads through a
2372    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2373    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2374    /// Every downstream consumer that reaches for a behavior axis
2375    /// first passes through this outer accessor onto the composite
2376    /// and then dispatches onto the per-callback accessor — the
2377    /// two-level dispatch means every per-`:behavior` reader now
2378    /// routes through a typed dispatch on the substrate primitive at
2379    /// both altitudes.
2380    ///
2381    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2382    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2383    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2384    /// keys the "per-version `:state-change` instruction must have a
2385    /// `:on-state-change` callback" precondition off this accessor's
2386    /// composite (the callback-side counterpart to the
2387    /// `:upgrade-from :instructions :state-change :script` refusal at
2388    /// the appup-side). Threading that gate's traversal input through
2389    /// this accessor closes the cross-slot invariant on the substrate
2390    /// primitive, not on the raw field.
2391    ///
2392    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2393    /// composite was accessed inline at four production sites — the
2394    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2395    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2396    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2397    /// `BehaviorError` refusal cascade + the per-callback on-disk
2398    /// [`crate::LayoutError::MissingEntry`] existence check under
2399    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2400    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2401    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2402    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2403    /// drives the `:state-change` ↔ `:on-state-change` precondition
2404    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2405    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2406    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2407    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2408    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2409    /// Servico values-block emitter fans on), and the
2410    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2411    /// set enumerator's `self.behavior.is_some()` presence probe
2412    /// (caixa-core/src/manifest.rs:1919, which drives the
2413    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2414    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2415    /// gate reads) — four open-coded outer-field accesses that
2416    /// expressed no compile-time link back to the typed slot at the
2417    /// [`Caixa`] altitude. A future extension of the `:behavior`
2418    /// outer axis to a richer author surface (a per-callback overlay
2419    /// resolver the operator materializes at admission time so a
2420    /// cluster-specific policy can inject a per-callback tracing
2421    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2422    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2423    /// dynamic}` partition once a runtime-resolved behavior-swap
2424    /// surface lands, the M4 per-callback middleware chain the
2425    /// caixa-operator's per-Servico admission webhook keys off) would
2426    /// have had to be threaded through all four open-coded copies in
2427    /// lockstep or one consumer would silently disagree with the
2428    /// peers on which behavior composite a given Caixa resolves to —
2429    /// the layout gate's per-callback existence-check seed reading
2430    /// the raw slot while the peer `servico_m2_overlay` emitter read
2431    /// an operator-resolved slot would silently split the build-time
2432    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2433    /// gate from the cross-slot `:state-change` composition gate from
2434    /// the M2 declared-slot enumerator, a four-consumer split far
2435    /// from the source `caixa.lisp` with no field naming the
2436    /// behavior-drift root cause. Lifting the resolution rule to a
2437    /// typed method on the substrate primitive means every downstream
2438    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2439    /// composite surface reaches for exactly one typed dispatch — the
2440    /// resolver's accept-set migrates as a unit on any future axis
2441    /// addition.
2442    ///
2443    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2444    /// composite-reference accessor — sibling to the opening
2445    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2446    /// `Option<&Composite>` composite-reference sub-family, extends
2447    /// the "one typed dispatch on the substrate primitive, thin
2448    /// projections at each consumer" discipline onto the second of
2449    /// the three M2 Servico-runtime slots. The remaining
2450    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2451    /// altitude — the M3 mesh-slot family (`:politicas`,
2452    /// `:placement`, `:entrada` — already closed on the inner
2453    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2454    /// d32111c) — remain the future sibling lifts on the outer
2455    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2456    /// the owning composite by copy or clone) because every
2457    /// downstream consumer of the behavior composite treats it as a
2458    /// read-only per-callback dispatch source — the reference-view is
2459    /// the narrowest borrow that supports every present + roadmapped
2460    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2461    /// overlay projection, presence-probe early return on the
2462    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2463    /// partition, cross-slot `:state-change` composition input)
2464    /// without cloning the composite through every consumer's fast
2465    /// path. The `Option` half of the return-type preserves the
2466    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2467    /// applies" partition (not a default composite the downstream
2468    /// must reject on emptiness) — the accessor projects the raw
2469    /// `Option<BehaviorSpec>` slot's presence bit through the
2470    /// reference-return unchanged. Named `behavior()` to match the
2471    /// storage field's name verbatim and the tatara-lisp author-
2472    /// surface term (`:behavior`) the field's own docstring already
2473    /// carries.
2474    #[must_use]
2475    pub fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2476        self.behavior.as_ref()
2477    }
2478
2479    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2480    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2481    /// reference accessor every consumer of the top-level manifest's
2482    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2483    /// reader keys off — returns the author-declared `:politicas` typed
2484    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2485    /// same backing storage the raw `self.politicas.as_ref()` field
2486    /// access borrows from, with `None` naming the "no `:politicas`
2487    /// block authored — every per-axis mesh-policy scalar defers to the
2488    /// cluster-default arm named on the per-axis
2489    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2490    /// [`crate::aplicacao::MeshPolicy::retries`] /
2491    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2492    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2493    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2494    /// docstrings" partition every downstream caixa-mesh /
2495    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2496    /// "emit no per-`:politicas` overlay" and the sibling
2497    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2498    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2499    /// arm.
2500    ///
2501    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2502    /// Aplicacao typed composite — the load-bearing container of every
2503    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2504    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2505    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2506    /// composite; §V — the "no infinite blocking" per-call deadline +
2507    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2508    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2509    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2510    /// threads through a lifted per-slot accessor on the
2511    /// [`crate::aplicacao::MeshPolicy`] type: the
2512    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2513    /// mTLS-enforcement toggle, the
2514    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2515    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2516    /// (7073d0f) Gateway-API per-call deadline, the
2517    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2518    /// Envoy-outlier-detection composite. Every downstream consumer
2519    /// that reaches for a mesh-policy axis first passes through this
2520    /// outer accessor onto the composite and then dispatches onto the
2521    /// per-axis accessor — the two-level dispatch means every per-
2522    /// `:politicas` reader now routes through a typed dispatch on the
2523    /// substrate primitive at both altitudes.
2524    ///
2525    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2526    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2527    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2528    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2529    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2530    /// composite whether or not the author declared the outer slot.
2531    /// The outer accessor preserves the "author-omitted vs authored-
2532    /// empty" partition the inner accessor's `is_empty()`-gated
2533    /// renderer overlay collapses — routing the presence bit through
2534    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2535    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2536    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2537    ///
2538    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2539    /// composite was accessed inline at two production sites — the
2540    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2541    /// `self.politicas.clone().unwrap_or_default()` traversal head
2542    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2543    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2544    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2545    /// then observes), and the [`Self::declared_mesh_slots`] M3
2546    /// declared-slot-set enumerator's `self.politicas.is_some()`
2547    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2548    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2549    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2550    /// coherence gate reads) — two open-coded outer-field accesses
2551    /// that expressed no compile-time link back to the typed slot at
2552    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2553    /// outer axis to a richer author surface (a per-cluster
2554    /// `:politicas-overrides` slot the operator materializes at
2555    /// admission time so a cluster-specific policy can tighten the
2556    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2557    /// promotion of the plain `Option<MeshPolicy>` to a richer
2558    /// `{static, dynamic}` partition once the M4 per-edge
2559    /// contrato-scoped policy-override surface lands, the M5 traffic-
2560    /// shaping composition the caixa-operator's per-Aplicacao mesh
2561    /// admission webhook keys off) would have had to be threaded
2562    /// through both open-coded copies in lockstep or the Aplicacao-
2563    /// composition seed's default-fold arm would silently disagree
2564    /// with the M3 declared-slot enumerator on which policy composite
2565    /// a given Caixa resolves to — the seed reading an operator-
2566    /// resolved slot while the enumerator's presence probe read the
2567    /// raw slot would silently split the build-time mesh-artifact
2568    /// emission gate from the M3 declared-slot enumerator's kind-
2569    /// coherence gate, a two-consumer split far from the source
2570    /// `caixa.lisp` with no field naming the policy-drift root cause.
2571    /// Lifting the resolution rule to a typed method on the substrate
2572    /// primitive means every downstream consumer of the caixa's per-
2573    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2574    /// reaches for exactly one typed dispatch — the resolver's
2575    /// accept-set migrates as a unit on any future axis addition.
2576    ///
2577    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2578    /// composite-reference accessor — sibling to the opening
2579    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2580    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2581    /// reference sub-family, extends the "one typed dispatch on the
2582    /// substrate primitive, thin projections at each consumer"
2583    /// discipline onto the first of the three M3 mesh-slot axes.
2584    /// Peer of the closed inner mesh-slot outer-composite family the
2585    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2586    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2587    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2588    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2589    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2590    /// mesh-slot arm of the composite-reference family the remaining
2591    /// two axes (`:placement`, `:entrada`) fold onto in future
2592    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2593    /// composite by copy or clone) because every downstream consumer
2594    /// of the mesh-policy composite treats it as a read-only per-axis
2595    /// dispatch source — the reference-view is the narrowest borrow
2596    /// that supports every present + roadmapped consumer (per-axis
2597    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2598    /// presence-probe early return on the "author-omitted `:politicas`
2599    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2600    /// seed's default-fold arm) without cloning the composite through
2601    /// every consumer's fast path. The `Option` half of the return-
2602    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2603    /// cluster-default applies" partition (not a default composite
2604    /// the downstream must reject on emptiness) — the accessor
2605    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2606    /// through the reference-return unchanged. Named `politicas()` to
2607    /// match the storage field's name verbatim and the tatara-lisp
2608    /// author-surface term (`:politicas`) the field's own docstring
2609    /// already carries.
2610    #[must_use]
2611    pub fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2612        self.politicas.as_ref()
2613    }
2614
2615    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2616    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2617    /// reference accessor every consumer of the top-level manifest's
2618    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2619    /// reader keys off — returns the author-declared `:placement` typed
2620    /// composite verbatim as an `Option<&Placement>` reference over the
2621    /// same backing storage the raw `self.placement.as_ref()` field
2622    /// access borrows from, with `None` naming the "no `:placement`
2623    /// block authored — every per-axis placement scalar defers to the
2624    /// cluster-default arm named on the per-axis
2625    /// [`crate::aplicacao::Placement::estrategia`] /
2626    /// [`crate::aplicacao::Placement::clusters`] /
2627    /// [`crate::aplicacao::Placement::affinity`] /
2628    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2629    /// docstrings" partition every downstream caixa-mesh /
2630    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2631    /// "emit no per-`:placement` overlay" and the sibling
2632    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2633    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2634    ///
2635    /// The outer `:placement` slot carries the M3 mesh-slot per-
2636    /// Aplicacao typed distribution composite — the load-bearing
2637    /// container of every where-does-this-Aplicacao-run axis every
2638    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2639    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2640    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2641    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2642    /// Aplicacao's typed distribution composite; §V CSE invariants —
2643    /// "distribution is a first-class typed composite, not a runtime
2644    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2645    /// typed inter-Servico contrato-edge overlay the per-cluster
2646    /// mesh renderer keys off). Every per-`:placement` axis threads
2647    /// through a lifted per-slot accessor on the
2648    /// [`crate::aplicacao::Placement`] type: the
2649    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2650    /// MESH-COMPOSITION distribution-strategy scalar, the
2651    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2652    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2653    /// M3-Adaptive-compression-hint optional-scalar, and the
2654    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2655    /// sharding extractor-expression optional-scalar. Every downstream
2656    /// consumer that reaches for a placement axis first passes through
2657    /// this outer accessor onto the composite and then dispatches onto
2658    /// the per-axis accessor — the two-level dispatch means every per-
2659    /// `:placement` reader now routes through a typed dispatch on the
2660    /// substrate primitive at both altitudes.
2661    ///
2662    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2663    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2664    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2665    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2666    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2667    /// whether or not the author declared the outer slot. The outer
2668    /// accessor preserves the "author-omitted vs authored-empty" partition
2669    /// the inner accessor collapses at the cluster-default fold —
2670    /// routing the presence bit through this accessor keeps the
2671    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2672    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2673    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2674    /// dispatch.
2675    ///
2676    /// Prior to this lift the `.placement` `Option<Placement>`
2677    /// composite was accessed inline at two production sites — the
2678    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2679    /// `self.placement.clone().unwrap_or_default()` traversal head
2680    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2681    /// the [`crate::aplicacao::Placement::default`] cluster-default
2682    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2683    /// then observes), and the [`Self::declared_mesh_slots`] M3
2684    /// declared-slot-set enumerator's `self.placement.is_some()`
2685    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2686    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2687    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2688    /// coherence gate reads) — two open-coded outer-field accesses
2689    /// that expressed no compile-time link back to the typed slot at
2690    /// the [`Caixa`] altitude. A future extension of the `:placement`
2691    /// outer axis to a richer author surface (a per-cluster
2692    /// `:placement-overrides` slot the operator materializes at
2693    /// admission time so a cluster-specific placement can tighten the
2694    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2695    /// per-tenant placement-alias table the M4
2696    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2697    /// per-CR at admission time, a promotion of the plain
2698    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2699    /// once Orleans-style virtual-actor dynamic placement comes into
2700    /// typed scope) would have had to be threaded through both open-
2701    /// coded copies in lockstep or the Aplicacao-composition seed's
2702    /// default-fold arm would silently disagree with the M3 declared-
2703    /// slot enumerator on which distribution composite a given Caixa
2704    /// resolves to — the seed reading an operator-resolved slot while
2705    /// the enumerator's presence probe read the raw slot would
2706    /// silently split the build-time distribution-artifact emission
2707    /// gate from the M3 declared-slot enumerator's kind-coherence
2708    /// gate, a two-consumer split far from the source `caixa.lisp`
2709    /// with no field naming the distribution-drift root cause.
2710    /// Lifting the resolution rule to a typed method on the substrate
2711    /// primitive means every downstream consumer of the caixa's per-
2712    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2713    /// reaches for exactly one typed dispatch — the resolver's
2714    /// accept-set migrates as a unit on any future axis addition.
2715    ///
2716    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2717    /// composite-reference accessor — sibling to the opening
2718    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2719    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2720    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2721    /// composite-reference sub-family, folds on the "one typed
2722    /// dispatch on the substrate primitive, thin projections at each
2723    /// consumer" discipline extended onto the second of the three M3
2724    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2725    /// composite family the sibling
2726    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2727    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2728    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2729    /// accessor pins already close on the inner
2730    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2731    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2732    /// [`Self::politicas`] opened, extending the discipline onto the
2733    /// second of the three M3 mesh-slot axes. The remaining M3
2734    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2735    /// discipline in the final sibling lift, closing the outer top-
2736    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2737    /// Returns `Option<&Placement>` (not the owning composite by copy
2738    /// or clone) because every downstream consumer of the placement
2739    /// composite treats it as a read-only per-axis dispatch source —
2740    /// the reference-view is the narrowest borrow that supports every
2741    /// present + roadmapped consumer (per-axis accessor dispatch,
2742    /// serde composite-serialization on the programs.yaml overlay,
2743    /// presence-probe early return on the "author-omitted `:placement`
2744    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2745    /// seed's default-fold arm) without cloning the composite through
2746    /// every consumer's fast path. The `Option` half of the return-
2747    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2748    /// cluster-default applies" partition (not a default composite
2749    /// the downstream must reject on emptiness) — the accessor
2750    /// projects the raw `Option<Placement>` slot's presence bit
2751    /// through the reference-return unchanged. Named `placement()` to
2752    /// match the storage field's name verbatim and the tatara-lisp
2753    /// author-surface term (`:placement`) the field's own docstring
2754    /// already carries.
2755    #[must_use]
2756    pub fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2757        self.placement.as_ref()
2758    }
2759
2760    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2761    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2762    /// composite-reference accessor every consumer of the top-level
2763    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2764    /// composite reader keys off — returns the author-declared
2765    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2766    /// reference over the same backing storage the raw
2767    /// `self.entrada.as_ref()` field access borrows from, with `None`
2768    /// naming the "no `:entrada` block authored — this Aplicacao is
2769    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2770    /// partition every downstream caixa-mesh Gateway-API artifact
2771    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2772    /// backend for this Aplicacao" and the sibling
2773    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2774    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2775    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2776    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2777    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2778    /// the same `Option<&Entrada>` presence bit unchanged).
2779    ///
2780    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2781    /// Aplicacao typed external-gateway composite — the load-bearing
2782    /// container of every how-does-the-outside-world-reach-this-
2783    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2784    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2785    /// external-entry composite; §V CSE invariants — "the external
2786    /// gateway is a first-class typed composite, not a per-Servico
2787    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2788    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2789    /// API renderer keys off). Every per-`:entrada` axis threads
2790    /// through a lifted per-slot accessor on the
2791    /// [`crate::aplicacao::Entrada`] type: the
2792    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2793    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2794    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2795    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2796    /// backend `trigger.service.port` scalar, and the
2797    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2798    /// resolver every HTTPRoute-aware renderer consumes. Every
2799    /// downstream consumer that reaches for an entry axis first passes
2800    /// through this outer accessor onto the composite and then
2801    /// dispatches onto the per-axis accessor — the two-level dispatch
2802    /// means every per-`:entrada` reader now routes through a typed
2803    /// dispatch on the substrate primitive at both altitudes.
2804    ///
2805    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2806    /// seed: the Aplicacao-view builder forwards the outer `Option`
2807    /// arm verbatim (no default fold — `:entrada` is inherently
2808    /// optional; a cluster-internal Aplicacao has no external gateway
2809    /// at all, not "an external gateway that defaults to nothing"), so
2810    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2811    /// `Option<&Entrada>`-return accessor observes the same presence
2812    /// bit whether or not the author declared the outer slot. Routing
2813    /// the presence bit through this accessor keeps the
2814    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2815    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2816    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2817    /// hostname/backend/path emission dispatch.
2818    ///
2819    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2820    /// was accessed inline at two production sites — the
2821    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2822    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2823    /// which drives the forward onto the peer inner
2824    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2825    /// Gateway-API fan-out then observes), and the
2826    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2827    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2828    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2829    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2830    /// kind-coherence gate reads) — two open-coded outer-field
2831    /// accesses that expressed no compile-time link back to the typed
2832    /// slot at the [`Caixa`] altitude. A future extension of the
2833    /// `:entrada` outer axis to a richer author surface (a per-cluster
2834    /// `:entrada-overrides` slot the operator materializes at admission
2835    /// time so a cluster-specific hostname can pin the caixa-declared
2836    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2837    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2838    /// CR materializer resolves per-CR at admission time, a promotion
2839    /// of the plain `Option<Entrada>` to a richer
2840    /// `{public, private, internal}` partition once Cilium-identity-
2841    /// scoped internal gateways come into typed scope) would have had
2842    /// to be threaded through both open-coded copies in lockstep or the
2843    /// Aplicacao-composition seed's forward arm would silently
2844    /// disagree with the M3 declared-slot enumerator on which external-
2845    /// gateway composite a given Caixa resolves to — the seed reading
2846    /// an operator-resolved slot while the enumerator's presence probe
2847    /// read the raw slot would silently split the build-time gateway-
2848    /// artifact emission gate from the M3 declared-slot enumerator's
2849    /// kind-coherence gate, a two-consumer split far from the source
2850    /// `caixa.lisp` with no field naming the entry-drift root cause.
2851    /// Lifting the resolution rule to a typed method on the substrate
2852    /// primitive means every downstream consumer of the caixa's per-
2853    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2854    /// surface reaches for exactly one typed dispatch — the resolver's
2855    /// accept-set migrates as a unit on any future axis addition.
2856    ///
2857    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2858    /// return composite-reference accessor — closes the outer-`Caixa`
2859    /// `Option<&Composite>` composite-reference sub-family opened by
2860    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2861    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2862    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2863    /// folds on the "one typed dispatch on the substrate primitive,
2864    /// thin projections at each consumer" discipline extended onto the
2865    /// third and final M3 mesh-slot axis. Peer of the closed inner
2866    /// mesh-slot outer-composite family the sibling
2867    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2868    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2869    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2870    /// accessor pins already close on the inner
2871    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2872    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2873    /// altitudes of the outer-composite reference-return discipline
2874    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2875    /// slot presence) now carry the full five-arm accept-set behind a
2876    /// typed dispatch on the substrate primitive. Returns
2877    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2878    /// because every downstream consumer of the entrada composite
2879    /// treats it as a read-only per-axis dispatch source — the
2880    /// reference-view is the narrowest borrow that supports every
2881    /// present + roadmapped consumer (per-axis accessor dispatch,
2882    /// serde composite-serialization on the programs.yaml overlay,
2883    /// presence-probe early return on the "author-omitted `:entrada`
2884    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2885    /// seed's forward arm) without cloning the composite through every
2886    /// consumer's fast path. The `Option` half of the return-type
2887    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2888    /// cluster-internal Aplicacao" partition (not a default composite
2889    /// the downstream must reject on emptiness — a cluster-internal
2890    /// Aplicacao has no external gateway at all, not "a default gateway
2891    /// that emits nothing"); the accessor projects the raw
2892    /// `Option<Entrada>` slot's presence bit through the reference-
2893    /// return unchanged. Named `entrada()` to match the storage field's
2894    /// name verbatim and the tatara-lisp author-surface term
2895    /// (`:entrada`) the field's own docstring already carries.
2896    #[must_use]
2897    pub fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
2898        self.entrada.as_ref()
2899    }
2900
2901    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
2902    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
2903    /// an `Option<&CiRun>`, borrowed from the typed slot's own
2904    /// `Option<CiRun>` storage. `None` when the slot is absent (every
2905    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
2906    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
2907    /// not silently accepted).
2908    ///
2909    /// Named `ci()` to match the storage field's name and the
2910    /// tatara-lisp author surface (`:ci`); mirrors the sibling
2911    /// `Option<&Composite>` accessors on this same `Caixa` altitude
2912    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
2913    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
2914    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
2915    /// at every consumer.
2916    #[must_use]
2917    pub fn ci(&self) -> Option<&canteiro_types::CiRun> {
2918        self.ci.as_ref()
2919    }
2920
2921    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
2922    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
2923    /// accessor every consumer of the top-level manifest's per-Supervisor
2924    /// restart-strategy axis keys off — returns the author-declared
2925    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
2926    /// `Copy`-projected from the typed slot's own
2927    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
2928    /// (`:estrategia` is a flat-spread supervisor-only slot every
2929    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
2930    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2931    /// still omit to defer to [`RestartStrategy::default`] —
2932    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
2933    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
2934    /// [`SupervisorSpec::default`]-inherited strategy without any silent
2935    /// promotion to a fresh explicit variant at the accessor boundary).
2936    ///
2937    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
2938    /// restart-strategy discriminant every substrate-side per-Supervisor
2939    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
2940    /// closed-set `one_for_one | one_for_all | rest_for_one |
2941    /// simple_one_for_one` algebra translated onto pleme-io's typed
2942    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
2943    /// slot algebra the operator's hierarchical reconciliation scheduler
2944    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
2945    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
2946    /// supervisor slots are flat on Caixa (vs nested under a
2947    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
2948    /// level of nesting"), so the accessor's altitude is the outer
2949    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
2950    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
2951    /// (eafb619) accessor keys off. The two typed axes — the outer
2952    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
2953    /// (author-omitted arm carried as `None`) and the inner post-
2954    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
2955    /// (`Option` collapsed through the [`Self::supervisor_view`]
2956    /// `unwrap_or_default()` fold) — now share one accessor discipline for
2957    /// the shared substrate concept "the author-declared OTP-shaped
2958    /// sibling-restart-strategy variant that partitions the downstream
2959    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
2960    /// `None` arm is the pre-composition presence bit every declared-slot
2961    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
2962    /// inner-altitude non-`Option` `RestartStrategy` is the post-
2963    /// composition partition-dispatch input every strategy-arm consumer
2964    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
2965    /// Supervisor sibling-restart branch, the future M4
2966    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
2967    /// webhook) fans on.
2968    ///
2969    /// Prior to this lift the `.estrategia` field was accessed inline at
2970    /// two production sites in `caixa-core/src/manifest.rs` — the
2971    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
2972    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
2973    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2974    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
2975    /// `SupervisorSpec` construction site at `estrategia:
2976    /// self.estrategia.unwrap_or_default()` (which composes the flat-
2977    /// spread outer author-surface `Option<RestartStrategy>` onto the
2978    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
2979    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
2980    /// coded field-accesses that expressed no compile-time link back to
2981    /// the typed slot. A future extension of the outer `:estrategia` axis
2982    /// to a richer author surface (a per-cluster strategy override the
2983    /// operator pins through a future `:estrategia-overrides` overlay the
2984    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
2985    /// a per-tenant strategy-alias table the M4 CR materializer resolves
2986    /// per-CR, a per-Supervisor dynamic strategy derivation the future
2987    /// adaptive-supervision engine computes from child-failure-history
2988    /// topology, a per-child-cohort strategy split the future
2989    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
2990    /// absorption roadmap acknowledges, a promotion of the plain
2991    /// `Option<RestartStrategy>` to a richer
2992    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
2993    /// operator-resolved overlay lands) would have had to be threaded
2994    /// through both open-coded copies in lockstep or the enumerator's
2995    /// presence probe and the composition site's `unwrap_or_default()`
2996    /// fold would silently disagree on which strategy a given [`Caixa`]
2997    /// resolves to (an author's `:estrategia OneForAll` would satisfy
2998    /// the enumerator's presence probe while the composition site
2999    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
3000    /// the resolution rule to a typed method on the substrate primitive
3001    /// means every downstream consumer of the caixa's per-`Caixa` outer-
3002    /// altitude sibling-restart-strategy surface reaches for exactly one
3003    /// typed dispatch — the resolver's accept-set migrates as a unit on
3004    /// any future axis addition.
3005    ///
3006    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3007    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3008    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
3009    /// projection pattern the sibling per-`Caixa` `:max-restarts`
3010    /// `Option<u32>` and (through the future duration-newtype landing)
3011    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
3012    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
3013    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
3014    /// the post-composition [`SupervisorSpec`] altitude — same "one
3015    /// typed dispatch on the substrate primitive, thin projections at
3016    /// each consumer" discipline extended onto the pre-composition outer
3017    /// author-surface [`Caixa`] altitude for the same OTP-shaped
3018    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
3019    /// `Option<&Composite>` composite-reference family the sibling
3020    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3021    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3022    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
3023    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
3024    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
3025    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
3026    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
3027    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
3028    /// pins on the inner-altitude per-`:placement` composite. Named
3029    /// `estrategia()` to match the storage field's name and the
3030    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
3031    /// / per-[`crate::aplicacao::Placement`] peer
3032    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
3033    /// verbatim; the accessor's identity name maps onto the canonical
3034    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
3035    /// docstring already carries.
3036    #[must_use]
3037    pub fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
3038        self.estrategia
3039    }
3040
3041    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
3042    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
3043    /// scalar accessor every consumer of the top-level manifest's per-
3044    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
3045    /// returns the author-declared `:max-restarts` typed `Option<u32>`
3046    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
3047    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
3048    /// accessor returns by value; no borrow of `&self` past the call).
3049    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
3050    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
3051    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3052    /// still omit to defer to the [`Self::supervisor_view`]
3053    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
3054    ///
3055    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
3056    /// `MaxIntensity` restart-budget count that pairs with the sibling
3057    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
3058    /// restart-intensity ratio the supervisor trips its own escalation on
3059    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
3060    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
3061    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
3062    /// reconciliation scheduler fans on). The slot is *flat-spread* on
3063    /// the outer top-level `Caixa` (per the field-shape docstring at
3064    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
3065    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
3066    /// accessor's altitude is the outer [`Caixa`] surface rather than the
3067    /// composed [`SupervisorSpec`] altitude the sibling
3068    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
3069    /// off. The two typed axes — the outer author-surface `Option<u32>`
3070    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
3071    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
3072    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
3073    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
3074    /// shared substrate concept "the author-declared OTP-shaped
3075    /// restart-budget count every downstream per-Supervisor consumer's
3076    /// restart-intensity budget-vs-count comparator fans on".
3077    ///
3078    /// Prior to this lift the `.max_restarts` field was accessed inline
3079    /// at two production sites in `caixa-core/src/manifest.rs` — the
3080    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
3081    /// presence-probe arm at `if self.max_restarts.is_some()` (which
3082    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3083    /// kind-coherence gate's per-slot label push) and the
3084    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
3085    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
3086    /// flat-spread outer author-surface `Option<u32>` onto the inner
3087    /// post-composition [`SupervisorSpec`] `u32` field the
3088    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
3089    /// coded field-accesses that expressed no compile-time link back to
3090    /// the typed slot. A future extension of the outer `:max-restarts`
3091    /// axis to a richer author surface (a per-cluster restart-budget
3092    /// override the operator pins through a future `:max-restarts-overrides`
3093    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
3094    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
3095    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
3096    /// budget derivation the future adaptive-supervision engine computes
3097    /// from child-failure-history topology, a promotion of the plain
3098    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
3099    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
3100    /// per-child-cohort roadmap lands) would have had to be threaded
3101    /// through both open-coded copies in lockstep or the enumerator's
3102    /// presence probe and the composition site's `unwrap_or(5)` fold
3103    /// would silently disagree on which restart-budget a given [`Caixa`]
3104    /// resolves to (an author's `:max-restarts 10` would satisfy the
3105    /// enumerator's presence probe while the composition site silently
3106    /// composed the OTP-canonical `5`, or vice versa). Lifting the
3107    /// resolution rule to a typed method on the substrate primitive means
3108    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
3109    /// restart-budget-count surface reaches for exactly one typed dispatch
3110    /// — the resolver's accept-set migrates as a unit on any future axis
3111    /// addition.
3112    ///
3113    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3114    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3115    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
3116    /// projection pattern the sibling per-`Caixa`
3117    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
3118    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
3119    /// Peer of the inner-altitude
3120    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
3121    /// on the post-composition [`SupervisorSpec`] altitude — same "one
3122    /// typed dispatch on the substrate primitive, thin projections at
3123    /// each consumer" discipline extended onto the pre-composition outer
3124    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
3125    /// shaped restart-budget-count axis. Named `max_restarts()` to match
3126    /// the storage field's name and the per-[`SupervisorSpec`] peer
3127    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
3128    /// discipline verbatim; the accessor's identity maps onto the
3129    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
3130    /// field's docstring already carries.
3131    #[must_use]
3132    pub const fn max_restarts(&self) -> Option<u32> {
3133        self.max_restarts
3134    }
3135
3136    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
3137    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
3138    /// denominator raw-duration-string scalar accessor every consumer of
3139    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
3140    /// window axis keys off — returns the author-declared `:restart-window`
3141    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
3142    /// from the typed slot's own `Option<String>` storage. `None` when
3143    /// the slot is absent (the canonical "never reset — every restart
3144    /// across the supervisor's lifetime counts against the sibling
3145    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
3146    /// `defcaixa` carries by `#[serde(default)]` and every
3147    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
3148    /// [`Self::supervisor_view`] `restart_window: None` composition
3149    /// through the [`crate::supervisor::duration_codec::parse`] soft-
3150    /// swallow `.and_then(|s| … .ok())` fold).
3151    ///
3152    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
3153    /// shaped `Period` sliding-observation-interval duration string that
3154    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
3155    /// budget count to form the `MaxIntensity / Period` restart-intensity
3156    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
3157    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
3158    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
3159    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
3160    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
3161    /// holds an `Option<Duration>` routed through the shared
3162    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
3163    /// — so the outer altitude's accessor returns `Option<&str>` (raw
3164    /// authoring surface) while the inner altitude's
3165    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
3166    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
3167    /// is closed by the sibling [`Self::validate_restart_window`] gate
3168    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
3169    /// the offending value; the view-construction path
3170    /// [`Self::supervisor_view`] soft-swallows the same parse error to
3171    /// `None` to keep the view best-effort.
3172    ///
3173    /// Prior to this lift the `.restart_window` field was accessed inline
3174    /// at three production sites in `caixa-core/src/manifest.rs` — the
3175    /// [`Self::declared_supervisor_slots`]
3176    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
3177    /// `if self.restart_window.is_some()` (which drives the
3178    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3179    /// coherence gate's per-slot label push), the
3180    /// [`Self::validate_restart_window`] `let Some(s) =
3181    /// self.restart_window.as_deref()` empty-and-shape gate binding
3182    /// (which folds the raw string through the shared
3183    /// [`crate::supervisor::duration_codec::parse`] to surface
3184    /// [`ManifestError::RestartWindowMalformed`] naming the offending
3185    /// value), and the [`Self::supervisor_view`] `self.restart_window
3186    /// .as_deref().and_then(…)` view-construction fold (which composes
3187    /// the flat-spread outer author-surface `Option<String>` onto the
3188    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
3189    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
3190    /// three open-coded field-accesses that expressed no compile-time
3191    /// link back to the typed slot. A future extension of the outer
3192    /// `:restart-window` axis to a richer author surface (a per-cluster
3193    /// window override, a per-tenant window-alias table, a per-Supervisor
3194    /// dynamic window derivation the future adaptive-supervision engine
3195    /// computes from child-failure-history topology, a promotion of the
3196    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
3197    /// once the future author-surface parser lands at the [`Caixa`]
3198    /// altitude and the raw-string form is retired) would have had to be
3199    /// threaded through every open-coded copy in lockstep or the three
3200    /// consumers would silently disagree on which raw string a given
3201    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
3202    /// method on the substrate primitive means every downstream consumer
3203    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
3204    /// string surface reaches for exactly one typed dispatch — the
3205    /// resolver's accept-set migrates as a unit on any future axis
3206    /// addition.
3207    ///
3208    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
3209    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
3210    /// spread projection pattern the sibling per-`Caixa`
3211    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
3212    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
3213    /// the sub-family onto the sibling `Option<&str>` raw-duration-
3214    /// string arm (the outer altitude's raw-string form; the inner
3215    /// altitude's parsed [`Duration`] form is the peer
3216    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
3217    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
3218    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
3219    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
3220    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
3221    /// sub-family already carries — same "one typed dispatch on the
3222    /// substrate primitive, thin projections at each consumer"
3223    /// discipline extended onto the M2 supervisor-tree flat-spread
3224    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
3225    /// to match the storage field's name and the per-[`SupervisorSpec`]
3226    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
3227    /// method-name discipline verbatim; the accessor's identity maps
3228    /// onto the canonical OTP-shape supervision vocabulary the
3229    /// `:restart-window` field's docstring already carries.
3230    #[must_use]
3231    pub fn restart_window(&self) -> Option<&str> {
3232        self.restart_window.as_deref()
3233    }
3234
3235    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
3236    /// outer-composite OTP-appup-shaped per-prior-version migration-
3237    /// entry-list slice accessor every consumer of the top-level
3238    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
3239    /// slice-view keys off — returns the author-declared `:upgrade-from`
3240    /// typed `Vec<UpgradeFromEntry>` verbatim as a
3241    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
3242    /// the raw `self.upgrade_from.as_slice()` field access borrows
3243    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
3244    /// arm every `defcaixa` without an `:upgrade-from` block carries;
3245    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
3246    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
3247    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
3248    /// possibly empty — and the returned `&[UpgradeFromEntry]`
3249    /// degenerates to an empty slice on that arm without any silent
3250    /// `None` collapse).
3251    ///
3252    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
3253    /// migration block — the load-bearing container of every per-
3254    /// prior-`:versao` migration-instruction list the wasm-operator
3255    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
3256    /// `.appup` per-prior-version `LoadModule | StateChange |
3257    /// SoftPurge | Purge | Restart` instruction algebra translated
3258    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
3259    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
3260    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
3261    /// threads through a lifted per-entry accessor on the
3262    /// [`UpgradeFromEntry`] type: the
3263    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
3264    /// version scalar accessor and the
3265    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
3266    /// return per-entry instruction-list accessor (0137e5a). Every
3267    /// downstream consumer of the hot-upgrade path first passes
3268    /// through this outer accessor onto the slice and then dispatches
3269    /// per-entry through the inner accessors — the two-level dispatch
3270    /// means every per-`:upgrade-from` reader now routes through a
3271    /// typed dispatch on the substrate primitive at both altitudes.
3272    ///
3273    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
3274    /// slot was accessed inline at production sites across three
3275    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
3276    /// enumerator's `self.upgrade_from.is_empty()` presence probe
3277    /// (caixa-core/src/manifest.rs, which drives the
3278    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
3279    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
3280    /// gate reads), the [`crate::StandardLayout::verify`] per-
3281    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
3282    /// layout.rs, which fans onto the
3283    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
3284    /// cross-entry duplicate gate, the
3285    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
3286    /// SemVer-precedence cross-slot gate, the
3287    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
3288    /// `:state-change` ↔ `:on-state-change` cross-slot composition
3289    /// gate, and the per-instruction script-path existence-probe walk
3290    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
3291    /// resolve every declared migration script against the layout
3292    /// root), and the [`crate::render::servico_m2_overlay`] per-
3293    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
3294    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
3295    /// projection (caixa-core/src/render.rs, which drives the
3296    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
3297    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
3298    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
3299    /// A future extension of the outer `:upgrade-from` axis (a per-
3300    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
3301    /// resolves at admission time so a cluster-specific migration
3302    /// policy can tighten a caixa-declared step without re-authoring
3303    /// the `caixa.lisp`, promotion of the plain
3304    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
3305    /// partition once runtime-resolved hot-upgrade instructions land,
3306    /// per-entry priority annotation once multi-strategy fan-out
3307    /// lands) would have had to be threaded through all six open-
3308    /// coded copies in lockstep or one consumer would silently
3309    /// disagree with the peers on which upgrade slice a given Caixa
3310    /// resolves to — a six-consumer split at the enumerator, the
3311    /// three-stage validate pass, the script-path probe walk, and the
3312    /// M2 overlay emitter, far from the source `caixa.lisp` with no
3313    /// field naming the upgrade-drift root cause. Lifting the
3314    /// resolution rule to a typed method on the substrate primitive
3315    /// means every downstream consumer of the caixa's per-`Caixa`
3316    /// OTP-appup outer-slice surface reaches for exactly one typed
3317    /// dispatch — the resolver's accept-set migrates as a unit on any
3318    /// future axis addition.
3319    ///
3320    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3321    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3322    /// outer-`Caixa` `&[Composite]` composite-slice projection
3323    /// pattern the sibling `:children`
3324    /// [`crate::supervisor::ChildSpec`] / `:membros`
3325    /// [`crate::aplicacao::Membro`] / `:contratos`
3326    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3327    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3328    /// `Option<&Composite>` composite-reference family the sibling
3329    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3330    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3331    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3332    /// `Option<&Composite>` altitude, extended here to the outer-
3333    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3334    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3335    /// (0137e5a) — same "one typed dispatch on the substrate
3336    /// primitive, thin projections at each consumer" discipline
3337    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3338    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3339    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3340    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3341    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3342    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3343    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3344    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3345    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3346    /// slice" projection pattern onto the sibling M2 typed-composite-
3347    /// element axis (`UpgradeFromEntry` composite, matching the
3348    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3349    /// different altitude).
3350    ///
3351    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3352    /// because every downstream consumer of the hot-upgrade list
3353    /// treats it as a read-only sequence — the slice-view is the
3354    /// narrowest borrow that supports every present + roadmapped
3355    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3356    /// serialization through
3357    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3358    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3359    /// the typed view reaches for (the storage-side `Vec` remains
3360    /// reachable through the `pub upgrade_from` field for the
3361    /// mutation-carrying serde round-trip and per-test fixture-
3362    /// mutation paths). Named `upgrade_from()` to match the storage
3363    /// field's `snake_case` name; the kebab-case author-surface tag
3364    /// `:upgrade-from` is the same axis after tatara-lisp's
3365    /// kebab↔snake fold and the accessor's identity maps onto the
3366    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3367    /// already carries.
3368    #[must_use]
3369    pub fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3370        self.upgrade_from.as_slice()
3371    }
3372
3373    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3374    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3375    /// slice accessor every consumer of the top-level manifest's per-
3376    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3377    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3378    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3379    /// the same backing buffer the raw `self.children.as_slice()` field
3380    /// access borrows from. Empty-slice-carrying (the "no static children
3381    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3382    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3383    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3384    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3385    /// on those arms without any silent `None` collapse).
3386    ///
3387    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3388    /// static-child list — the load-bearing container of every per-
3389    /// child `{caixa, versao, restart}` triple the wasm-operator's
3390    /// hierarchical reconciler dispatches on at supervisor-tree
3391    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3392    /// static-child list translated onto pleme-io's typed
3393    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3394    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3395    /// dispatch fans on). Every per-child axis threads through a lifted
3396    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3397    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3398    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3399    /// version-requirement scalar accessor, and the
3400    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3401    /// per-child post-exit restart-decision-policy discriminant
3402    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3403    /// tree path first passes through this outer accessor onto the
3404    /// slice and then dispatches per-child through the inner accessors
3405    /// — the two-level dispatch means every per-`:children` reader now
3406    /// routes through a typed dispatch on the substrate primitive at
3407    /// both altitudes.
3408    ///
3409    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3410    /// accessed inline at three production sites across two files —
3411    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3412    /// declared-slot enumerator's `!self.children.is_empty()` presence
3413    /// probe (caixa-core/src/manifest.rs, which drives the
3414    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3415    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3416    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3417    /// per-supervisor typed-view composer's `self.children.clone()`
3418    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3419    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3420    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3421    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3422    /// `:children :caixa` self-parent refusal probe's
3423    /// `&caixa.children`-borrowed
3424    /// [`crate::supervisor::validate_no_self_supervision`] input
3425    /// (caixa-core/src/layout.rs, which pins the "no child names the
3426    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3427    /// extension of the outer `:children` axis (a per-cluster
3428    /// `:children-overrides` overlay the wasm-engine operator resolves
3429    /// at admission time so a cluster-specific child-set can tighten
3430    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3431    /// promotion of the plain `Vec<ChildSpec>` to a richer
3432    /// `{static, dynamic}` partition once Erlang/OTP's
3433    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3434    /// axis, per-child priority annotation once multi-strategy fan-out
3435    /// lands) would have had to be threaded through all three open-
3436    /// coded copies in lockstep or one consumer would silently
3437    /// disagree with the peers on which child slice a given Caixa
3438    /// resolves to — the enumerator's presence probe reading the raw
3439    /// slot while the peer view-composer's fold-in path read an
3440    /// operator-resolved slot would silently split the paired
3441    /// declared-slot enumerator and typed-view composition, and the
3442    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3443    /// refusal probe reading a third borrow would silently drift the
3444    /// cross-slot coherence gate's traversal input from the two peers,
3445    /// a three-consumer split at the enumerator, the view composer,
3446    /// and the self-parent gate far from the source `caixa.lisp` with
3447    /// no field naming the child-set-drift root cause. Lifting the
3448    /// resolution rule to a typed method on the substrate primitive
3449    /// means every downstream consumer of the caixa's per-`Caixa`
3450    /// OTP-supervisor outer-slice surface reaches for exactly one
3451    /// typed dispatch — the resolver's accept-set migrates as a unit
3452    /// on any future axis addition.
3453    ///
3454    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3455    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3456    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3457    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3458    /// at the outer altitude of the closed inner-`SupervisorSpec`
3459    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3460    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3461    /// borrow-shared" outer-accessor discipline extended onto the
3462    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3463    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3464    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3465    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3466    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3467    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3468    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3469    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3470    /// M2 typed-composite-element axis
3471    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3472    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3473    /// different altitude).
3474    ///
3475    /// Returns `&[crate::supervisor::ChildSpec]` (not
3476    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3477    /// child list treats it as a read-only sequence — the slice-view
3478    /// is the narrowest borrow that supports every present +
3479    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3480    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3481    /// input, `serde` slice-serialization) without leaking the backing
3482    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3483    /// reaches for (the storage-side `Vec` remains reachable through
3484    /// the `pub children` field for the mutation-carrying serde round-
3485    /// trip and per-test fixture-mutation paths, including the
3486    /// [`Self::supervisor_view`] fold-in path that clones the slot
3487    /// into the typed view). Named `children()` to match the storage
3488    /// field's name verbatim and the tatara-lisp author-surface term
3489    /// (`:children`) the field's own docstring already carries; the
3490    /// accessor's identity maps onto the canonical OTP supervision
3491    /// vocabulary the [`Caixa::children`] field's docstring already
3492    /// reaches for ("Static children of a supervisor").
3493    #[must_use]
3494    pub fn children(&self) -> &[crate::supervisor::ChildSpec] {
3495        self.children.as_slice()
3496    }
3497
3498    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3499    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3500    /// accessor every consumer of the top-level manifest's per-Aplicacao
3501    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3502    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3503    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3504    /// same backing buffer the raw `self.membros.as_slice()` field access
3505    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3506    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3507    /// and every partially-authored Aplicacao carries before the
3508    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3509    /// `&[Membro]` degenerates to an empty slice on those arms without any
3510    /// silent `None` collapse).
3511    ///
3512    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3513    /// per-Aplicacao member list — the load-bearing container of every
3514    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3515    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3516    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3517    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3518    /// the `:entrada :para` external-gateway destination validates
3519    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3520    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3521    /// threads through a lifted per-entry accessor on the
3522    /// [`crate::aplicacao::Membro`] type: the
3523    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3524    /// identity scalar accessor (4a32abf) and the peer
3525    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3526    /// version-requirement scalar accessor (a40b0e3). Every downstream
3527    /// consumer of the mesh-graph path first passes through this outer
3528    /// accessor onto the slice and then dispatches per-member through
3529    /// the inner accessors — the two-level dispatch means every per-
3530    /// `:membros` reader now routes through a typed dispatch on the
3531    /// substrate primitive at both altitudes.
3532    ///
3533    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3534    /// inline at three production sites across two files — the
3535    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3536    /// enumerator's `!self.membros.is_empty()` presence probe
3537    /// (caixa-core/src/manifest.rs, which drives the
3538    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3539    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3540    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3541    /// composer's `self.membros.clone()` per-member fold-in path
3542    /// (caixa-core/src/manifest.rs, which materializes the typed
3543    /// [`crate::aplicacao::AplicacaoSpec`] view every
3544    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3545    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3546    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3547    /// [`crate::aplicacao::validate_no_self_membership`] input
3548    /// (caixa-core/src/layout.rs, which pins the "no member names the
3549    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3550    /// extension of the outer `:membros` axis (a per-cluster
3551    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3552    /// admission time so a cluster-specific member-set can tighten a
3553    /// caixa-declared list without re-authoring the `caixa.lisp`,
3554    /// promotion of the plain `Vec<Membro>` to a richer
3555    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3556    /// members land as a typed axis, per-member priority annotation once
3557    /// multi-strategy fan-out lands) would have had to be threaded
3558    /// through all three open-coded copies in lockstep or one consumer
3559    /// would silently disagree with the peers on which member slice a
3560    /// given Caixa resolves to — the enumerator's presence probe reading
3561    /// the raw slot while the peer view-composer's fold-in path read an
3562    /// operator-resolved slot would silently split the paired
3563    /// declared-slot enumerator and typed-view composition, and the
3564    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3565    /// refusal probe reading a third borrow would silently drift the
3566    /// cross-slot coherence gate's traversal input from the two peers, a
3567    /// three-consumer split at the enumerator, the view composer, and
3568    /// the self-membership gate far from the source `caixa.lisp` with no
3569    /// field naming the member-set-drift root cause. Lifting the
3570    /// resolution rule to a typed method on the substrate primitive
3571    /// means every downstream consumer of the caixa's per-`Caixa`
3572    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3573    /// typed dispatch — the resolver's accept-set migrates as a unit on
3574    /// any future axis addition.
3575    ///
3576    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3577    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3578    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3579    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3580    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3581    /// altitude. Peer at the outer altitude of the closed inner-
3582    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3583    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3584    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3585    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3586    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3587    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3588    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3589    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3590    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3591    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3592    /// pattern onto the sibling M3 typed-composite-element axis
3593    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3594    /// [`crate::AplicacaoSpec::membros`] element type at a different
3595    /// altitude).
3596    ///
3597    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3598    /// because every downstream consumer of the member list treats it
3599    /// as a read-only sequence — the slice-view is the narrowest borrow
3600    /// that supports every present + roadmapped consumer (`.iter()`,
3601    /// `.len()`, `.is_empty()`, the
3602    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3603    /// input, `serde` slice-serialization) without leaking the backing
3604    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3605    /// reaches for (the storage-side `Vec` remains reachable through the
3606    /// `pub membros` field for the mutation-carrying serde round-trip
3607    /// and per-test fixture-mutation paths, including the
3608    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3609    /// the typed view). Named `membros()` to match the storage field's
3610    /// name verbatim and the tatara-lisp author-surface term
3611    /// (`:membros`) the field's own docstring already carries; the
3612    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3613    /// vocabulary the [`Caixa::membros`] field's docstring already
3614    /// reaches for ("Member Servicos that make up this Aplicacao").
3615    #[must_use]
3616    pub fn membros(&self) -> &[crate::aplicacao::Membro] {
3617        self.membros.as_slice()
3618    }
3619
3620    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3621    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3622    /// inter-Servico contract-list slice accessor every consumer of the
3623    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3624    /// slice-view keys off — returns the author-declared `:contratos`
3625    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3626    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3627    /// backing buffer the raw `self.contratos.as_slice()` field access
3628    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3629    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3630    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3631    /// single member with no inter-Servico edge carries; the returned
3632    /// `&[WitContract]` degenerates to an empty slice on those arms
3633    /// without any silent `None` collapse).
3634    ///
3635    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3636    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3637    /// container of every per-edge `{de, para, wit, endpoint | subject |
3638    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3639    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3640    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3641    /// adjacency-list seed dispatch on at mesh-artifact materialization
3642    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3643    /// `:membros` vertex set resolves against, closed by the
3644    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3645    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3646    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3647    /// per-edge axis threads through a lifted per-entry accessor on the
3648    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3649    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3650    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3651    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3652    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3653    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3654    /// and the WIT-world discriminant. Every downstream consumer of the
3655    /// mesh-graph edge path first passes through this outer accessor
3656    /// onto the slice and then dispatches per-contract through the
3657    /// inner accessors — the two-level dispatch means every
3658    /// per-`:contratos` reader now routes through a typed dispatch on
3659    /// the substrate primitive at both altitudes.
3660    ///
3661    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3662    /// accessed inline at two production sites in
3663    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3664    /// mesh-slot declared-slot enumerator's
3665    /// `!self.contratos.is_empty()` presence probe (which drives the
3666    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3667    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3668    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3669    /// typed-view composer's `self.contratos.clone()` per-contract
3670    /// fold-in path (which materializes the typed
3671    /// [`crate::aplicacao::AplicacaoSpec`] view every
3672    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3673    /// downstream `caixa-mesh` renderer dispatches on). A future
3674    /// extension of the outer `:contratos` axis (a per-cluster
3675    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3676    /// at admission time so a cluster-specific edge-set can tighten a
3677    /// caixa-declared list without re-authoring the `caixa.lisp`,
3678    /// promotion of the plain `Vec<WitContract>` to a richer
3679    /// `{static, dynamic}` partition once runtime-resolved contract
3680    /// edges land, per-edge policy annotation once the M4 per-edge
3681    /// policy overlay axis lands) would have had to be threaded through
3682    /// both open-coded copies in lockstep or one consumer would
3683    /// silently disagree with the peer on which edge slice a given
3684    /// Caixa resolves to — the enumerator's presence probe reading the
3685    /// raw slot while the peer view-composer's fold-in path read an
3686    /// operator-resolved slot would silently split the paired
3687    /// declared-slot enumerator and typed-view composition, a
3688    /// two-consumer split at the enumerator and the view composer far
3689    /// from the source `caixa.lisp` with no field naming the edge-set-
3690    /// drift root cause. Lifting the resolution rule to a typed method
3691    /// on the substrate primitive means every downstream consumer of
3692    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3693    /// reaches for exactly one typed dispatch — the resolver's
3694    /// accept-set migrates as a unit on any future axis addition.
3695    ///
3696    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3697    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3698    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3699    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3700    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3701    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3702    /// mesh-slot arm of the composite-slice sub-family the sibling
3703    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3704    /// Peer at the outer altitude of the closed inner-
3705    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3706    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3707    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3708    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3709    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3710    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3711    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3712    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3713    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3714    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3715    /// pattern onto the sibling M3 typed-composite-element axis
3716    /// ([`crate::aplicacao::WitContract`] composite, matching the
3717    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3718    /// different altitude).
3719    ///
3720    /// Returns `&[crate::aplicacao::WitContract]` (not
3721    /// `&Vec<WitContract>`) because every downstream consumer of the
3722    /// contract list treats it as a read-only sequence — the slice-view
3723    /// is the narrowest borrow that supports every present + roadmapped
3724    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3725    /// discriminant dispatch, `serde` slice-serialization) without
3726    /// leaking the backing `Vec`'s grow/push/reserve surface no
3727    /// consumer of the typed view reaches for (the storage-side `Vec`
3728    /// remains reachable through the `pub contratos` field for the
3729    /// mutation-carrying serde round-trip and per-test fixture-mutation
3730    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3731    /// clones the slot into the typed view). Named `contratos()` to
3732    /// match the storage field's name verbatim and the tatara-lisp
3733    /// author-surface term (`:contratos`) the field's own docstring
3734    /// already carries; the accessor's identity maps onto the canonical
3735    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3736    /// docstring already reaches for ("WIT-typed inter-Servico
3737    /// contracts").
3738    #[must_use]
3739    pub fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3740        self.contratos.as_slice()
3741    }
3742
3743    /// Compose the Aplicacao-related flat slots into a single typed
3744    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3745    /// downstream renderer consumption. Returns `None` when the
3746    /// caixa isn't a `:kind Aplicacao`.
3747    #[must_use]
3748    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3749        if !self.kind().is_aplicacao() {
3750            return None;
3751        }
3752        Some(crate::aplicacao::AplicacaoSpec {
3753            membros: self.membros().to_vec(),
3754            contratos: self.contratos().to_vec(),
3755            politicas: self.politicas().cloned().unwrap_or_default(),
3756            placement: self.placement().cloned().unwrap_or_default(),
3757            entrada: self.entrada().cloned(),
3758        })
3759    }
3760
3761    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3762    /// *declares* a value on, in canonical declaration order
3763    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3764    /// `:entrada`). A slot counts as declared when its backing field
3765    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3766    ///
3767    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3768    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3769    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3770    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3771    /// caixa-flux / caixa-helm renderers only emit them for an
3772    /// Aplicacao. On any *other* kind a declared mesh slot is the
3773    /// manifest field's documented "ignored otherwise" (see the
3774    /// `:membros` … `:entrada` field docs): it silently passes
3775    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3776    /// rendered — far from the source caixa.lisp.
3777    /// [`crate::StandardLayout::verify`] consults this to reject that
3778    /// silent-drop at caixa-build time
3779    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3780    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3781    /// a slot foreign to the kind is a build error, not a silent drop.
3782    ///
3783    /// Lifted as a typed method (rather than an inline disjunction at
3784    /// the verify call site) so the mesh-slot set lives in one place —
3785    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3786    /// overlay, distributed-app takeover config) is one push here, and
3787    /// every consumer reaching for "which mesh slots are set" (the
3788    /// verify gate, a future `feira lint` kind-coherence advisory)
3789    /// inherits the canonical order without rolling its own.
3790    ///
3791    /// Each per-arm kebab-case label is routed through the peer
3792    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3793    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3794    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3795    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3796    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3797    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3798    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3799    /// kebab-case label + renderer-side artifact key) route through one
3800    /// canonical declaration per arm — same discipline the peer
3801    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3802    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3803    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3804    /// axis, extended here to close the M3 mesh-slot author-facing-label
3805    /// axis so both altitudes of the typed-slot algebra
3806    /// (per-Servico M2 + per-Aplicacao M3) share the same
3807    /// "one canonical byte-string per arm, next to the axis" discipline.
3808    #[must_use]
3809    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3810        let mut slots = Vec::new();
3811        if !self.membros().is_empty() {
3812            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3813        }
3814        if !self.contratos().is_empty() {
3815            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3816        }
3817        if self.politicas().is_some() {
3818            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3819        }
3820        if self.placement().is_some() {
3821            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3822        }
3823        if self.entrada().is_some() {
3824            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3825        }
3826        slots
3827    }
3828
3829    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3830    /// caixa *declares* a value on, in canonical declaration order
3831    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3832    /// `:children`). A slot counts as declared when its backing field
3833    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3834    ///
3835    /// The supervisor-tree slots compose the typed OTP supervisor of a
3836    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3837    /// `:children` field docs above). [`Self::supervisor_view`] only
3838    /// folds them into a validatable [`SupervisorSpec`] when the kind
3839    /// matches (returns `None` otherwise), and the wasm-operator's
3840    /// hierarchical reconciler only consumes them for a Supervisor. On
3841    /// any *other* kind a declared supervisor slot is the manifest
3842    /// field's documented "ignored otherwise" (see the `:estrategia` …
3843    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3844    /// and then vanishes — never validated, never reconciled — far from
3845    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3846    /// this to reject that silent-drop at caixa-build time
3847    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3848    /// exact mirror of the [`Self::declared_mesh_slots`] /
3849    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3850    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3851    /// error, not a silent drop.
3852    #[must_use]
3853    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3854        let mut slots = Vec::new();
3855        if self.estrategia().is_some() {
3856            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3857        }
3858        if self.max_restarts().is_some() {
3859            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3860        }
3861        if self.restart_window().is_some() {
3862            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3863        }
3864        if !self.children().is_empty() {
3865            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3866        }
3867        slots
3868    }
3869
3870    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3871    /// caixa *declares* a value on, in canonical declaration order
3872    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3873    /// declared when its backing field carries a value — a `Some(...)`,
3874    /// or a non-empty `Vec`.
3875    ///
3876    /// The M2 slots configure the runtime of a long-running wasm
3877    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3878    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3879    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3880    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3881    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3882    /// emit these slots for a Servico; on any *other* kind a declared M2
3883    /// slot is the manifest field's documented "ignored otherwise": its
3884    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3885    /// but the value is never rendered into a chart / programs.yaml entry
3886    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
3887    /// vanishes, far from the source caixa.lisp.
3888    /// [`crate::StandardLayout::verify`] consults this to reject that
3889    /// silent-drop at caixa-build time
3890    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
3891    /// mirror of the [`Self::declared_mesh_slots`] /
3892    /// [`Self::declared_supervisor_slots`] gates on the peer
3893    /// kind-exclusive slot sets: a slot foreign to the kind is a build
3894    /// error, not a silent drop.
3895    ///
3896    /// Each per-arm kebab-case label is routed through the peer
3897    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
3898    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
3899    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
3900    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
3901    /// both halves of the M2 top-level slot's dual axis (author-facing
3902    /// kebab-case label + renderer-side camelCase overlay-container wire
3903    /// key) route through one canonical declaration per arm — same
3904    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
3905    /// author-label consts (889dc18) establish on the sibling
3906    /// per-callback axis inside the `:behavior` overlay block.
3907    #[must_use]
3908    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
3909        let mut slots = Vec::new();
3910        if self.limits().is_some() {
3911            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
3912        }
3913        if self.behavior().is_some() {
3914            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
3915        }
3916        if !self.upgrade_from().is_empty() {
3917            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
3918        }
3919        slots
3920    }
3921
3922    /// The kebab-case `:slot` tags of every code-surface slot this caixa
3923    /// declares a value on that its [`CaixaKind`] doesn't natively own,
3924    /// in canonical declaration order (`:exe` → `:servicos`). A
3925    /// code-surface slot is owned by exactly one kind: `:exe` by
3926    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
3927    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
3928    /// `ComputeUnit` daemon surface).
3929    ///
3930    /// Each is silently ignored when declared on the wrong kind: the
3931    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
3932    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
3933    /// code-running kind a declared `:exe` / `:servicos` is the manifest
3934    /// field's documented "ignored otherwise" — its path is checked for
3935    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
3936    /// (which run after [`Caixa::from_lisp`]), but the value is never
3937    /// rendered into a build target or programs.yaml entry. It silently
3938    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
3939    /// caixa.lisp, with no field naming which slot is foreign.
3940    ///
3941    /// [`crate::StandardLayout::verify`] consults this to reject that
3942    /// silent-drop at caixa-build time
3943    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
3944    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
3945    /// gates ([`Self::declared_servico_slots`] /
3946    /// [`Self::declared_supervisor_slots`] /
3947    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
3948    /// axis to be closed on the typed surface. The Supervisor /
3949    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
3950    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
3951    /// diagnostics — they fire ahead of this gate on the same `verify`
3952    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
3953    /// and this method is moot. For Biblioteca / Binario / Servico, this
3954    /// gate fires when a code-running kind declares another code-running
3955    /// kind's exclusive code surface.
3956    ///
3957    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
3958    /// may legitimately ship a `lib/` helper that the underlying
3959    /// substrate (the nix flake for Binario, the wasm component build
3960    /// for Servico) bundles into its build, so the slot's
3961    /// declared-on-wrong-kind cardinality isn't a structural error on
3962    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
3963    /// is the native case (the slot's owning kind). Supervisor /
3964    /// Aplicacao declaring `:bibliotecas` is gated upstream by
3965    /// [`crate::LayoutError::SupervisorOwnsCode`] /
3966    /// [`crate::LayoutError::AplicacaoOwnsCode`].
3967    ///
3968    /// Lifted as a typed method (rather than an inline disjunction at
3969    /// the verify call site) so the foreign-code-slot set lives in one
3970    /// place — a future kind that gains its own code-surface slot is
3971    /// one push here, and every consumer reaching for "which code
3972    /// surfaces are foreign to this kind" (the verify gate, a future
3973    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
3974    /// per-caixa build-target classifier) inherits the canonical order
3975    /// without rolling its own.
3976    #[must_use]
3977    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
3978        let mut slots = Vec::new();
3979        if !self.exe().is_empty() && !self.kind().requires_exe() {
3980            slots.push(":exe");
3981        }
3982        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
3983            slots.push(":servicos");
3984        }
3985        slots
3986    }
3987
3988    /// Validate every entry of `:deps` and `:deps-dev` through
3989    /// [`Dep::validate`] — closing the parity loop with the per-axis
3990    /// `:versao` gates already wired into the typed-graph
3991    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
3992    /// 9888b13) and typed supervisor tree
3993    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
3994    ///
3995    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
3996    /// were the only `:versao` axes still untyped past
3997    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
3998    /// as a String without parsing it, so a malformed-but-non-empty
3999    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
4000    /// silently passed parse and the `semver::Error` surfaced at
4001    /// lacre-resolve time, far from the source caixa.lisp, with no
4002    /// field naming which `:deps` entry carried the typo. Lifting the
4003    /// gate here makes the four `:versao` typed surfaces (`:deps`,
4004    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
4005    /// every requirement string past `validate_deps` is round-trippable
4006    /// through [`crate::parse_requirement`] without re-checking at the
4007    /// resolver layer.
4008    ///
4009    /// Both lists run through the same per-entry validator so a typo
4010    /// in `:deps-dev` surfaces with the same diagnostic as one in
4011    /// `:deps` — neither axis is a second-class citizen of the typed
4012    /// surface.
4013    ///
4014    /// Within each list, [`DepError::DuplicateNome`] closes the
4015    /// set-not-multiset discipline on the `:nome` axis: two entries
4016    /// naming the same caixa carry two `:versao` / `:fonte` / feature
4017    /// triples that the caixa-resolver's lacre pipeline collapses to one
4018    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
4019    /// silently overwrites the first at `concrete_versao`-resolve time
4020    /// (the same "second wins / one silently overwrites the other"
4021    /// shape the peer typed-graph duplicate gates already close on every
4022    /// other Vec-shaped authoring surface that keys by name). The
4023    /// duplicate check fires per-list and runs *after* each per-entry
4024    /// [`Dep::validate`] call so a malformed-and-duplicated entry
4025    /// surfaces its narrower per-entry diagnostic
4026    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
4027    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
4028    /// diagnostic — the canonical "per-entry shape before cross-entry
4029    /// uniqueness" precedence the peer `:children :caixa`
4030    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
4031    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
4032    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
4033    /// ([`crate::AplicacaoSpec::validate_placement`]),
4034    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
4035    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
4036    /// and the within-`:upgrade-from`-entry per-instruction-class
4037    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
4038    /// [`crate::UpgradeError::DuplicateStateChange`],
4039    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
4040    ///
4041    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
4042    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
4043    /// same name in both tables (the dev table's pin overrides the
4044    /// runtime table's pin in test/dev contexts), and caixa's surface
4045    /// mirrors that convention until a deliberate choice retires the
4046    /// override pattern. Only within-list duplicates are structurally
4047    /// incoherent — those are what this gate closes.
4048    pub fn validate_deps(&self) -> Result<(), DepError> {
4049        for &list in crate::dep::DepList::ALL {
4050            let mut seen = std::collections::HashSet::new();
4051            for dep in self.deps_of(list) {
4052                dep.validate()?;
4053                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
4054                    DepError::DuplicateNome {
4055                        nome: dep.nome().to_string(),
4056                        list: list.as_str(),
4057                    }
4058                })?;
4059            }
4060        }
4061        Ok(())
4062    }
4063
4064    /// Reject `:nome` values the K8s apiserver would refuse at admission
4065    /// time. The top-level Caixa identity flows directly into every
4066    /// substrate-side artifact's `metadata.name` axis: the
4067    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
4068    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
4069    /// aggregator keys ComputeUnit derivation off
4070    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
4071    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
4072    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
4073    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
4074    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
4075    /// ([`caixa-mesh::lib::cilium_network_policies`],
4076    /// [`caixa-mesh::lib::gateway_routes`]), and the default
4077    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
4078    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
4079    /// schema enforces the DNS-1123 label rule on admission; a
4080    /// structurally invalid `:nome` (`"MyApp"` — the canonical
4081    /// "I copied the display name verbatim" footgun, `"my_app"` — the
4082    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
4083    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
4084    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
4085    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
4086    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
4087    /// failure surfaced at `kubectl apply` time as a `metadata.name:
4088    /// Invalid value` rejection on whichever derived artifact admitted
4089    /// first, far from the source `caixa.lisp` and without any field
4090    /// naming the offending `:nome`.
4091    ///
4092    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
4093    /// substrate-side predicate the per-axis name gates already share:
4094    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
4095    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
4096    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
4097    /// diagnostic is self-locating (the offending `:nome` is named
4098    /// verbatim) and the author can grep their `caixa.lisp` for
4099    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
4100    /// every per-axis sibling gate already exposes
4101    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
4102    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
4103    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
4104    ///
4105    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
4106    /// derive macro stores the raw String) is gated by the narrower
4107    /// [`ManifestError::NomeEmpty`] arm before the predicate is
4108    /// consulted, mirroring the empty-first cascade every per-axis
4109    /// name gate already uses (e.g. `MembroCaixaEmpty` before
4110    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
4111    pub fn validate_nome(&self) -> Result<(), ManifestError> {
4112        // Routes through the shared
4113        // [`crate::render::require_valid_dns_1123_label`] gate the peer
4114        // name axes each land on so drift between the eight axes'
4115        // accepted DNS-1123-label sets is structurally impossible.
4116        let nome = self.nome();
4117        crate::render::require_valid_dns_1123_label(
4118            nome,
4119            || ManifestError::NomeEmpty,
4120            |reason| ManifestError::NomeInvalid {
4121                nome: nome.to_string(),
4122                reason,
4123            },
4124        )
4125    }
4126
4127    /// Reject `:nome` values whose joint length with the canonical
4128    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4129    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4130    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4131    /// substrate carries materializes the caixa's `:nome` through the
4132    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4133    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4134    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4135    /// `ChartDir.name` + `Chart.yaml::name`
4136    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4137    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4138    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4139    /// `oci://<registry>/lareira-<nome>` chart ref
4140    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4141    /// admission rule strict-parses against DNS-1123-label, the Helm
4142    /// operator's tracking-secret name is derived from `release_name`
4143    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4144    /// K8s object `metadata.name` axes embed the chart name as a
4145    /// prefix — every one fails admission on a > 63-byte chart name.
4146    ///
4147    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4148    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4149    /// `:nome` of 56–63 bytes silently passed validate (the inner
4150    /// DNS-1123 check accepts the bare `:nome`) but produced a
4151    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4152    /// rejected at admission — far from the source `caixa.lisp`, with
4153    /// no field naming the overflow root cause. The
4154    /// [`lareira_chart_name`] helper's own doc comment
4155    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4156    /// "the M4 admission webhook will pin the joint-length invariant
4157    /// when it lands". This gate lands the invariant at the
4158    /// manifest-validate layer rather than waiting for the apiserver
4159    /// — the same fail-at-the-source posture every peer per-axis
4160    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4161    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4162    /// `:edicao`, etc.) takes.
4163    ///
4164    /// Thin wrapper around
4165    /// [`crate::render::is_lareira_chart_name_shape`] (the
4166    /// substrate-side predicate that composes [`lareira_chart_name`] +
4167    /// [`is_dns_1123_label`] via the lifted
4168    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4169    /// shared parser-shaped reason into the
4170    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4171    /// diagnostic is self-locating (the offending `:nome` is named
4172    /// verbatim alongside the rendered chart name and the budget) and
4173    /// the author can shorten in one edit. The gate runs across every
4174    /// `:kind` — `:nome` is the substrate-wide identity axis any
4175    /// future renderer the substrate adds can derive a
4176    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4177    /// the drift footgun where a future kind grows a chart-emitting
4178    /// render path while the validate cascade doesn't catch it.
4179    ///
4180    /// Runs *after* [`Self::validate_nome`] so the narrower
4181    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4182    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4183    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4184    /// specific shape error rather than the chart-name-budget error,
4185    /// preserving the legitimate "well-shaped `:nome` that happens to
4186    /// overflow the joint cap" arm for this gate.
4187    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4188        let nome = self.nome();
4189        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
4190            ManifestError::NomeChartNameBudgetExceeded {
4191                nome: nome.to_string(),
4192                reason,
4193            }
4194        })
4195    }
4196
4197    /// Reject `:versao` values that don't parse as [`semver::Version`].
4198    /// The top-level Caixa version flows directly into every
4199    /// substrate-side artifact that carries a "this is which version of
4200    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4201    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4202    /// SemVer-2-strict at `helm template` / `helm install` time per
4203    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4204    /// `feira publish` Zig-style `v<versao>` git tag
4205    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4206    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4207    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4208    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4209    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4210    /// `skopeo push`, the lacre closure's pinned versions
4211    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4212    /// `:upgrade-from :from` references peers in this exact `versao`
4213    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4214    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4215    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4216    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4217    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4218    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4219    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4220    /// into the version field a peer `:deps :versao` accepts;
4221    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4222    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4223    /// derive macro stores the raw String) and the failure surfaced at
4224    /// the *first* downstream consumer that strict-parses it: at
4225    /// `helm install` time as a chart-version rejection, at
4226    /// `feira publish` time as a malformed git tag, at lacre-resolve
4227    /// time as a `semver::Error` not naming the offending caixa, at
4228    /// `feira upgrade --to <versao>` time as an unresolvable
4229    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4230    /// and without any field naming the offending `:versao`.
4231    ///
4232    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4233    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4234    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4235    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4236    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4237    /// variant, carrying the offending `:versao` verbatim + a
4238    /// parser-shaped reason naming the specific violation, so the
4239    /// diagnostic is self-locating (the author can grep their
4240    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4241    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4242    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4243    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4244    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4245    /// now structurally equivalent (every value past validate is
4246    /// round-trippable through [`semver::Version::parse`] without
4247    /// re-checking at the renderer, resolver, or operator hot-upgrade
4248    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4249    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4250    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4251    ///
4252    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4253    /// the derive macro stores the raw String) is gated by the
4254    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4255    /// consulted, mirroring the empty-first cascade every per-axis
4256    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4257    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4258    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4259    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4260        let versao = self.versao();
4261        if versao.is_empty() {
4262            return Err(ManifestError::VersaoEmpty);
4263        }
4264        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
4265            versao: versao.to_string(),
4266            reason: e.to_string(),
4267        })?;
4268        Ok(())
4269    }
4270
4271    /// Reject `:restart-window` values the shared
4272    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4273    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4274    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4275    /// `Option<Duration>` routed through the shared codec via `with =
4276    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4277    /// view-construction path ([`Self::supervisor_view`]) folds the
4278    /// raw string through the same shared codec and soft-swallows the
4279    /// parse error as `None` to keep the view best-effort. Without
4280    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4281    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4282    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4283    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4284    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4285    /// edge case) silently produced a `SupervisorSpec` with
4286    /// `restart_window: None`, indistinguishable from the canonical
4287    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4288    /// `MaxIntensity / Period` invariant turns into a never-reset
4289    /// supervisor far from the source `caixa.lisp`, with no field
4290    /// naming the offending `:restart-window`. Lifting the gate to a
4291    /// Caixa-level validator mirrors the trajectory of the peer
4292    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4293    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4294    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4295    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4296    ///
4297    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4298    /// (the shared codec backing `:supervisor :restart-window` as
4299    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4300    /// `:politicas :circuit-breaker :window` — all three covered by
4301    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4302    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4303    /// variant, carrying the offending raw string + a parser-shaped
4304    /// reason naming the canonical authoring form, so the diagnostic
4305    /// is self-locating (the author can grep their `caixa.lisp` for
4306    /// `:restart-window "<value>"` and fix it in one edit) and
4307    /// uniform with every other manifest-level validate diagnostic.
4308    /// With this gate the four `:restart-window`-shaped surfaces (the
4309    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4310    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4311    /// now structurally equivalent — every value past the codec is in
4312    /// one accepted set, by construction.
4313    ///
4314    /// `None` (the canonical "omit the slot to express no reset"
4315    /// shape) is accepted trivially — the gate is a no-op when the
4316    /// author didn't author a window. The empty string is rejected by
4317    /// the shared codec (its digit-only gate refuses an empty
4318    /// magnitude), surfacing the same `RestartWindowMalformed`
4319    /// diagnostic as every other rejected non-canonical shape.
4320    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4321        let Some(s) = self.restart_window() else {
4322            return Ok(());
4323        };
4324        crate::supervisor::duration_codec::parse(s)
4325            .map(|_| ())
4326            .map_err(|reason| ManifestError::RestartWindowMalformed {
4327                restart_window: s.to_string(),
4328                reason,
4329            })
4330    }
4331
4332    /// Reject per-entry values on the three Caixa-level code-surface
4333    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
4334    /// layout checker's `root.join(p)` sandbox would silently subvert.
4335    /// Same three structural footguns the peer
4336    /// [`BehaviorSpec::validate`] (b0c8389) and
4337    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
4338    /// (26da2c7) already close on the M2 `:behavior :on-*` and
4339    /// `:upgrade-from :state-change :script` axes, here lifted onto
4340    /// the three top-level code-path axes through the shared
4341    /// [`is_sandboxed_relative_path`] predicate:
4342    ///
4343    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
4344    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
4345    ///     [`Path::join`] as the base itself — `root.join("")` ==
4346    ///     `root`, so the existence check (`self.exists(&root)`)
4347    ///     trivially passes (the project root exists), and the layout
4348    ///     silently treats the project root as a biblioteca / exe /
4349    ///     servico entry. The `:bibliotecas` loop then hands the root
4350    ///     to `tatara_lisp::read` at `feira build` time as if the root
4351    ///     directory itself were a Lisp source file — a parse error
4352    ///     far from the source `caixa.lisp` with no field naming the
4353    ///     offending entry.
4354    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
4355    ///     [`Path::join`] *replaces* the base when the right-hand side
4356    ///     is absolute, so `root.join("/etc/passwd")` resolves to
4357    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
4358    ///     The existence check then silently consults whatever the
4359    ///     escaped path resolves to — for `:bibliotecas`, the layout
4360    ///     has no `starts_with`-fence (only `:exe` is fenced under
4361    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
4362    ///     `:bibliotecas` entry that happens to resolve on disk
4363    ///     silently passes. For `:exe` / `:servicos` the fence catches
4364    ///     the absolute case downstream as `ExeOutsideDir` /
4365    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
4366    ///     doesn't exist), but with a downstream-shaped diagnostic
4367    ///     that names the resolved escape path rather than the
4368    ///     authoring footgun at the source.
4369    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
4370    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
4371    ///     [`std::path::Component::ParentDir`] anywhere round-trips
4372    ///     through [`Path::join`] as a traversal above the caixa root.
4373    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
4374    ///     *component-aware* (not canonical-path-aware), so
4375    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
4376    ///     is **true** even though the canonical resolution
4377    ///     `{parent of root}/escape.lisp` lives outside the caixa root
4378    ///     — the fence silently lets the parent-escape through, and
4379    ///     the existence check passes if that escape-target happens
4380    ///     to exist. Caught regardless of where the `..` sits
4381    ///     (leading, mid-path, trailing) so the gate matches the peer
4382    ///     predicate's full coverage.
4383    ///
4384    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
4385    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
4386    /// same per-slot diagnostic shape every peer per-axis path-gate
4387    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
4388    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
4389    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
4390    /// order [`Caixa::declared_foreign_code_slots`] uses for its
4391    /// canonical foreign-code-slot diagnostic, so a manifest with
4392    /// multiple malformed slots surfaces the lexicographically-earliest
4393    /// slot's diagnostic deterministically.
4394    ///
4395    /// Lifted to the typed surface as a Caixa-level validator (peer
4396    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
4397    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
4398    /// and wired into [`crate::StandardLayout::verify`] before the
4399    /// existence-check loops so the diagnostic names the offending
4400    /// slot at the source caixa.lisp rather than reporting a
4401    /// downstream `MissingEntry` / `ExeOutsideDir` /
4402    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
4403    /// The fourth typed code-path surface — every author-supplied
4404    /// path on the manifest — is now structurally accept-shaped
4405    /// past validate, peer with `:behavior :on-*` and
4406    /// `:upgrade-from :state-change :script`.
4407    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
4408        /// Per-slot file-type contract for the three Caixa-level
4409        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
4410        /// Each variant names the predicate the per-entry file-type
4411        /// gate consults; [`Self::None`] opts the slot out of any
4412        /// file-type contract. Lifted as a typed local enum so the
4413        /// per-slot dispatch is exhaustive at the `match` — adding a
4414        /// future axis to the typed-substrate `:` slot set (the
4415        /// future `:assets` resource axis the M5 roadmap names, the
4416        /// future `:nix-flake` derivation axis the caixa-flake
4417        /// emitter consults) lands as one variant + one `match` arm,
4418        /// not a coordinated rewrite of every per-slot bool flag.
4419        ///
4420        /// Peer of the typed-substrate per-slot variant disciplines
4421        /// already established on this surface
4422        /// ([`crate::supervisor::RestartStrategy`] +
4423        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
4424        /// supervision-tree axis,
4425        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
4426        /// placement axis, [`crate::aplicacao::WitTarget`] on the
4427        /// `:contratos` payload-target axis): the typed `enum` is
4428        /// the substrate's single source of truth for the per-axis
4429        /// dispatch, and every consumer (the per-arm body here, the
4430        /// future feira-lint per-slot diagnostic renderer, the M4
4431        /// per-axis admission webhook) reaches for the same typed
4432        /// surface rather than re-deriving the partition from inline
4433        /// flag combinations.
4434        enum CodePathFileType {
4435            /// `:exe` — nix-build derivation output, no terminating-
4436            /// extension contract (the canonical `"exe/<name>"`
4437            /// fixtures the layout's `ExeOutsideDir` error message
4438            /// documents carry no extension by convention).
4439            None,
4440            /// `:bibliotecas` — tatara-lisp source files the
4441            /// `feira build` loop reads through `tatara_lisp::read`
4442            /// at parse time. Routes to [`is_lisp_extension`].
4443            LispSource,
4444            /// `:servicos` — ComputeUnit-CR YAML files the
4445            /// caixa-helm / caixa-flux renderers consume through
4446            /// `serde_yaml::from_str`. Routes to
4447            /// [`is_computeunit_yaml_extension`].
4448            ComputeUnitYaml,
4449        }
4450
4451        // The per-slot [`CodePathFileType`] selects which axes carry the
4452        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
4453        // source axis (the `feira build` loop at
4454        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
4455        // `tatara_lisp::read` at parse time) — the lifted
4456        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
4457        // `:exe` is the nix-built executable surface (per the canonical
4458        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
4459        // error message documents and every in-tree
4460        // `caixa_with_code_paths` positive control uses) — its file-type
4461        // contract is "nix-build derivation output", not a typed source
4462        // file, so [`CodePathFileType::None`] opts the slot out of any
4463        // file-type gate. `:servicos` is the `.computeunit.yaml`
4464        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
4465        // renderers consume each entry through `serde_yaml::from_str` as
4466        // a typed `ComputeUnit` CR) — the lifted
4467        // [`is_computeunit_yaml_extension`] predicate gates the compound
4468        // `.computeunit.yaml` suffix. All three axes are surfaced through
4469        // the same iteration so the sandbox-shape + duplicate gates
4470        // apply uniformly; the typed file-type dispatch fires per-slot
4471        // exactly where the downstream consumer's accepted set demands
4472        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
4473        // compounding lift on the peer 64772a9 `:bibliotecas`
4474        // `.lisp`-gate trajectory — the second of the three code-path
4475        // axes to land on a typed compound-suffix gate, with the same
4476        // self-locating per-slot diagnostic shape every peer per-axis
4477        // file-type lift uses (`*NonLispExtension { slot, path }` /
4478        // `*NonComputeUnitYamlExtension { slot, path }`).
4479        for (slot, list, file_type) in [
4480            (
4481                ":bibliotecas",
4482                &self.bibliotecas,
4483                CodePathFileType::LispSource,
4484            ),
4485            (":exe", &self.exe, CodePathFileType::None),
4486            (
4487                ":servicos",
4488                &self.servicos,
4489                CodePathFileType::ComputeUnitYaml,
4490            ),
4491        ] {
4492            // Per-slot set-not-multiset gate on the typed code-path axis.
4493            // Every peer Vec-shaped author-supplied list past validate is
4494            // a set, not a multiset: `:membros :caixa`
4495            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
4496            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
4497            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
4498            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
4499            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
4500            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
4501            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
4502            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
4503            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
4504            // the three code-path lists are the last Vec-shaped author-
4505            // supplied slots on the typed Caixa surface still admitting a
4506            // duplicate entry silently. Scope is per-list (`:bibliotecas`
4507            // duplicates are flagged within `:bibliotecas`, not across
4508            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
4509            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
4510            // legitimate dev-vs-runtime shape on the dep axis, fenced
4511            // separately by [`crate::dep::validate_no_self_dep`]). On the
4512            // code-path axis a cross-slot collision is structurally
4513            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
4514            // fence — `:exe` and `:servicos` entries are confined to their
4515            // own directory trees, so the only way a string could appear
4516            // on two code-path lists is the (rare, structurally invalid)
4517            // case where `:bibliotecas` carries an `"exe/<x>"` or
4518            // `"servicos/<x>.yaml"`-shaped path.
4519            //
4520            // Without the gate three authoring footguns silently passed:
4521            //
4522            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
4523            //     canonical copy-paste-the-wrong-file footgun. `feira
4524            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
4525            //     list and re-parses the same file twice, wasting work
4526            //     and silently masking the author's intent to declare a
4527            //     *second* biblioteca.
4528            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
4529            //     Binario surface. The future `caixa-flake` `nix flake`
4530            //     emitter that materializes each `:exe` entry as a flake
4531            //     `packages.<exe-name>` derivation would collide on the
4532            //     duplicate package name and surface a flake-eval error
4533            //     far from the source `caixa.lisp`.
4534            //   - `:servicos ("servicos/x.computeunit.yaml"
4535            //     "servicos/x.computeunit.yaml")` — the same footgun on
4536            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
4537            //     renderers already refuse `:servicos.len() != 1` with
4538            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
4539            //     that diagnostic surfaces "too many servicos" without
4540            //     naming "duplicate entry" — the typed self-locating
4541            //     "which entry is the duplicate" framing only lands at
4542            //     this gate.
4543            //
4544            // Same `seen.insert(entry.as_str())` shape every peer per-list
4545            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
4546            // 86c769b, `:deps` 359fba5) and the same "structural shape
4547            // checks fire before the duplicate check on the same entry"
4548            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
4549            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
4550            // empty entry first, not the duplicate on the later pair).
4551            let mut seen = std::collections::HashSet::new();
4552            for entry in list {
4553                let path = Path::new(entry);
4554                match is_sandboxed_relative_path(path) {
4555                    Ok(()) => {}
4556                    Err(PathShapeViolation::Empty) => {
4557                        return Err(ManifestError::CodePathEmpty { slot });
4558                    }
4559                    Err(PathShapeViolation::Absolute) => {
4560                        return Err(ManifestError::CodePathAbsolute {
4561                            slot,
4562                            path: path.to_path_buf(),
4563                        });
4564                    }
4565                    Err(PathShapeViolation::ParentEscape) => {
4566                        return Err(ManifestError::CodePathParentEscape {
4567                            slot,
4568                            path: path.to_path_buf(),
4569                        });
4570                    }
4571                }
4572                // The per-slot file-type gate dispatched through the
4573                // typed [`CodePathFileType`] selector above. Each variant
4574                // routes to the lifted predicate the downstream consumer
4575                // demands:
4576                //
4577                //   - [`LispSource`] → [`is_lisp_extension`] for
4578                //     `:bibliotecas` (the `feira build` loop's
4579                //     `tatara_lisp::read` consumer);
4580                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
4581                //     for `:servicos` (the caixa-helm / caixa-flux
4582                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
4583                //     accepted set);
4584                //   - [`None`] for `:exe` — the nix-build derivation-
4585                //     output axis has no terminating-extension contract.
4586                //
4587                // Fires after the sandbox-shape arms so a path that is
4588                // *both* sandbox-escaping and wrong-extension surfaces
4589                // the more fundamental sandbox-shape diagnostic first
4590                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
4591                // `ParentEscape` → `NonLispExtension` arm-ordering on
4592                // `:behavior :on-*` c97815a, and `EmptyScript` →
4593                // `AbsoluteScript` → `ParentEscapeScript` →
4594                // `NonLispExtensionScript` on
4595                // `:upgrade-from :state-change :script` 33cc830), and
4596                // before the duplicate gate so the narrower per-entry
4597                // file-type shape dominates the cross-entry uniqueness
4598                // diagnostic (a
4599                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
4600                // `:servicos` surfaces
4601                // `CodePathNonComputeUnitYamlExtension` on the first
4602                // entry rather than `CodePathDuplicate` on the pair —
4603                // peer with the 64772a9 `:bibliotecas`
4604                // `("lib/x.txt" "lib/x.txt")` ordering).
4605                match file_type {
4606                    CodePathFileType::None => {}
4607                    CodePathFileType::LispSource => {
4608                        if !is_lisp_extension(path) {
4609                            return Err(ManifestError::CodePathNonLispExtension {
4610                                slot,
4611                                path: path.to_path_buf(),
4612                            });
4613                        }
4614                    }
4615                    CodePathFileType::ComputeUnitYaml => {
4616                        if !is_computeunit_yaml_extension(path) {
4617                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
4618                                slot,
4619                                path: path.to_path_buf(),
4620                            });
4621                        }
4622                    }
4623                }
4624                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
4625                    ManifestError::CodePathDuplicate {
4626                        slot,
4627                        path: path.to_path_buf(),
4628                    }
4629                })?;
4630            }
4631        }
4632        Ok(())
4633    }
4634
4635    /// Reject `:etiquetas` lists with an empty entry or with two entries
4636    /// agreeing on the same string. `:etiquetas` is the universal
4637    /// registry-search-tag axis on [`Caixa`] (every kind carries the
4638    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4639    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
4640    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
4641    /// a [`std::collections::BTreeSet`] alongside the four substrate-
4642    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
4643    /// Two authoring footguns silently passed validate without this gate:
4644    ///
4645    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
4646    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
4647    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
4648    ///     `chart.metadata.keywords` admits the value without a strict
4649    ///     parser-side gate, but the empty keyword has no operational
4650    ///     meaning — it indexes nothing in the future caixa-registry
4651    ///     search axis and clutters the rendered chart with a no-op tag.
4652    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
4653    ///     copy-paste-the-wrong-tag footgun) silently passed validate
4654    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
4655    ///     at chart render — a "second wins / one silently disappears"
4656    ///     shape divergent from every peer typed-graph set gate
4657    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
4658    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
4659    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4660    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4661    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4662    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
4663    ///     on `:upgrade-from`, the per-instruction-class singularity
4664    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
4665    ///     [`crate::UpgradeError::DuplicateStateChange`] /
4666    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
4667    ///     discipline is uniform: every Vec-shaped author-supplied list
4668    ///     past validate is set-not-multiset, by construction.
4669    ///
4670    /// Past the empty arm the gate enforces the chart-keyword shape
4671    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
4672    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
4673    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
4674    /// continuation. Closes the canonical paste-from-doc footguns the
4675    /// bare empty + duplicate arms left open: paste-from-aligned-doc
4676    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
4677    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
4678    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
4679    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
4680    /// — the author meant three separate list entries), path-separator
4681    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
4682    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
4683    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
4684    /// control bytes that would silently land as malformed search tags
4685    /// in the rendered Chart.yaml `keywords:` array and break the
4686    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
4687    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
4688    /// established on the sibling universal-axis `Vec<String>` surface
4689    /// — the second universal-axis Vec<String> surface to land the
4690    /// empty-first-then-shape-then-duplicate per-entry cascade.
4691    ///
4692    /// Same empty-first cascade discipline every peer per-axis gate
4693    /// uses: the per-entry empty arm fires before the per-entry shape
4694    /// arm fires before the cross-entry duplicate arm, so an
4695    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
4696    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
4697    /// has no value" defect) before either the shape or the duplicate
4698    /// diagnostic. Walks the list in declaration order so the
4699    /// first-collision diagnostic surfaces the lexicographically-
4700    /// earliest offending position, peer with every other duplicate
4701    /// gate on this surface.
4702    ///
4703    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
4704    /// caixa-build gate alongside the peer universal gates
4705    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4706    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
4707    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
4708    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4709    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4710    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4711    /// slot sets. The future caixa-registry search axis can reach for
4712    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
4713    /// chart-keyword-shaped string without re-deriving the precondition.
4714    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
4715        let mut seen = std::collections::HashSet::new();
4716        for etiqueta in self.etiquetas() {
4717            if etiqueta.is_empty() {
4718                return Err(ManifestError::EtiquetaEmpty);
4719            }
4720            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
4721                ManifestError::EtiquetaInvalid {
4722                    etiqueta: etiqueta.clone(),
4723                    reason,
4724                }
4725            })?;
4726            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
4727                ManifestError::EtiquetaDuplicate {
4728                    etiqueta: etiqueta.clone(),
4729                }
4730            })?;
4731        }
4732        Ok(())
4733    }
4734
4735    /// Reject `:autores` lists with an empty entry or with two entries
4736    /// agreeing on the same string. `:autores` is the universal
4737    /// maintainer-axis on [`Caixa`] (every kind carries the
4738    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4739    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
4740    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
4741    /// to a `Maintainer { name, email: None }` without dedup). Two
4742    /// authoring footguns silently passed validate without this gate:
4743    ///
4744    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
4745    ///     blank-doc footgun) rendered as
4746    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
4747    ///     empty maintainer name has no operational meaning — it
4748    ///     identifies no one in the substrate's authorship index and
4749    ///     clutters the rendered chart with a no-op maintainer.
4750    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
4751    ///     the copy-paste-the-wrong-author footgun) silently passed
4752    ///     validate and rendered as two identical maintainer entries.
4753    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
4754    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
4755    ///     rendered `keywords:` array at chart-render time), the
4756    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
4757    ///     entries stack verbatim in the chart, divergent from every
4758    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
4759    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
4760    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4761    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4762    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4763    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
4764    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
4765    ///     `:etiquetas`).
4766    ///
4767    /// Past the empty arm the gate enforces the chart-maintainer-name
4768    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
4769    /// the structural single-line printable-UTF-8 floor every realistic
4770    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
4771    /// or trailing whitespace, no ASCII control characters anywhere,
4772    /// Unicode bytes accepted. Closes the canonical paste-from-doc
4773    /// footguns the bare empty + duplicate arms left open:
4774    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
4775    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
4776    /// pasted a multi-line block of author records into one `:autores`
4777    /// entry instead of splitting into one entry per author),
4778    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
4779    /// and the paste-from-binary-blob control bytes that would silently
4780    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
4781    /// `maintainers:` array. Mirrors the shape-predicate cascade
4782    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4783    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
4784    /// establish past their own empty arms on the sibling universal-axis
4785    /// `Option<String>` surfaces — the first universal-axis Vec<String>
4786    /// surface to land the empty-first-then-shape-then-duplicate per-entry
4787    /// cascade.
4788    ///
4789    /// Same empty-first cascade discipline every peer per-axis gate
4790    /// uses: the per-entry empty arm fires before the per-entry shape
4791    /// arm before the cross-entry duplicate arm. Walks the list in
4792    /// declaration order so the first-collision diagnostic surfaces the
4793    /// lexicographically-earliest offending position, peer with every
4794    /// other duplicate gate on this surface.
4795    ///
4796    /// Universal-axis (every kind carries `:autores`), so wired at the
4797    /// caixa-build gate alongside the peer universal gates
4798    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4799    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4800    /// [`Self::validate_code_paths`] — before the kind-coherence gates
4801    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4802    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4803    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4804    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4805    /// slot sets.
4806    pub fn validate_autores(&self) -> Result<(), ManifestError> {
4807        let mut seen = std::collections::HashSet::new();
4808        for autor in self.autores() {
4809            if autor.is_empty() {
4810                return Err(ManifestError::AutorEmpty);
4811            }
4812            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
4813                ManifestError::AutorInvalid {
4814                    autor: autor.clone(),
4815                    reason,
4816                }
4817            })?;
4818            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
4819                ManifestError::AutorDuplicate {
4820                    autor: autor.clone(),
4821                }
4822            })?;
4823        }
4824        Ok(())
4825    }
4826
4827    /// Reject `:repositorio` values whose shape the shared
4828    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
4829    /// `repositorio: Option<String>` slot on [`Caixa`] is the
4830    /// universal git-shaped homepage axis every kind carries — the
4831    /// substrate routes the same string through two load-bearing
4832    /// consumers:
4833    ///
4834    ///   - [`caixa-helm`] folds it verbatim into the rendered
4835    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
4836    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
4837    ///     the chart `README.md` `repo = …` interpolation
4838    ///     (`caixa-helm/src/lib.rs:359`).
4839    ///   - [`caixa-flux`] folds it verbatim into the standalone
4840    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
4841    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
4842    ///     `GitRepository.spec.url` the cluster's source-controller
4843    ///     polls — the load-bearing deploy-time axis.
4844    ///
4845    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4846    /// substitute a placeholder when the slot is absent (`None` → the
4847    /// fallback fires); a `Some("")` *skips the fallback* and silently
4848    /// passes the empty string through to `Chart.yaml home: ""` /
4849    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
4850    /// controller both reject the empty URL far from the source
4851    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
4852    /// Similarly a malformed `:repositorio` (whitespace, control char,
4853    /// missing `:` separator, leading `-`) silently lands in the
4854    /// rendered artifacts and breaks at `git clone` / `helm template`
4855    /// / `flux reconcile` time.
4856    ///
4857    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
4858    /// same shared predicate the peer [`crate::DepSource::validate`]
4859    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
4860    /// gate the two `git URL`-shaped surfaces on the typed Caixa
4861    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
4862    /// structurally equivalent: every value past validate is
4863    /// guaranteed-acceptable by the predicate's union of constraints
4864    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
4865    /// control chars, ASCII only, no leading `:`, contains a `:`
4866    /// separator). The predicate accepts every documented authoring
4867    /// shape — `github:org/repo` shorthand, `https://host/path`,
4868    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
4869    /// scp-style SSH, `file:///path` — and refuses the canonical
4870    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
4871    /// injection footguns at validate time. Maps the predicate's
4872    /// `String` reason verbatim into the
4873    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
4874    /// offending value + parser-shaped reason so the diagnostic is
4875    /// self-locating (the author can grep their `caixa.lisp` for
4876    /// `:repositorio "<value>"` and fix it in one edit).
4877    ///
4878    /// `None` (the canonical "omit the slot to express no published
4879    /// homepage" shape) is accepted trivially — the gate is a no-op
4880    /// when the author didn't declare a value. `Some("")` is gated by
4881    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
4882    /// shape predicate is consulted, mirroring the empty-first cascade
4883    /// every peer per-axis identity gate uses
4884    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
4885    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
4886    /// [`crate::DepError::FonteRepoEmpty`] →
4887    /// [`crate::DepError::FonteRepoInvalid`]).
4888    ///
4889    /// Universal-axis (every kind carries `:repositorio`), so wired at
4890    /// the caixa-build gate alongside the peer universal gates
4891    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4892    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4893    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
4894    /// before the kind-coherence gates
4895    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4896    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4897    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4898    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4899    /// specific slot sets.
4900    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
4901        let Some(s) = self.repositorio() else {
4902            return Ok(());
4903        };
4904        if s.is_empty() {
4905            return Err(ManifestError::RepositorioEmpty);
4906        }
4907        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
4908            repositorio: s.to_string(),
4909            reason,
4910        })
4911    }
4912
4913    /// Reject `:descricao` values that are the empty string. The flat
4914    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
4915    /// free-form-prose homepage axis every kind carries — the
4916    /// substrate routes the same string through two load-bearing
4917    /// consumers in the [`caixa-helm`] renderer:
4918    ///
4919    ///   - `build_chart_yaml` folds it verbatim into the rendered
4920    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
4921    ///     field (`caixa-helm/src/lib.rs:232-235`).
4922    ///   - `build_readme` folds it verbatim into the rendered chart
4923    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
4924    ///
4925    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4926    /// substitute a `caixa.nome`-derived placeholder when the slot is
4927    /// absent (`None` → the fallback fires); a `Some("")` *skips the
4928    /// fallback* and silently passes the empty string through to
4929    /// `Chart.yaml description: ""` / a blank chart `README.md`
4930    /// header. Helm's chart spec requires a non-empty `description:`
4931    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
4932    /// `WARNING [chart.metadata.description]: description is required`),
4933    /// so the empty `Some("")` silently lands in the rendered
4934    /// artifacts and breaks at `helm lint` / `helm install` time far
4935    /// from the source `caixa.lisp`, with no field naming the
4936    /// offending `:descricao`.
4937    ///
4938    /// `None` (the canonical "omit the slot to defer to the renderer's
4939    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
4940    /// the gate is a no-op when the author didn't declare a value.
4941    /// `Some("")` is gated by the narrower
4942    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
4943    /// shape every peer per-axis empty gate uses
4944    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4945    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4946    /// [`ManifestError::RepositorioEmpty`]).
4947    ///
4948    /// Universal-axis (every kind carries `:descricao`), so wired at
4949    /// the caixa-build gate alongside the peer universal gates
4950    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4951    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4952    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4953    /// [`Self::validate_code_paths`] — before the kind-coherence
4954    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4955    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4956    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4957    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4958    /// specific slot sets.
4959    ///
4960    /// Past the empty arm the gate enforces the chart-description
4961    /// shape predicate via [`crate::render::is_chart_description_shape`]:
4962    /// the structural single-line UTF-8 floor every realistic chart
4963    /// description in the wild matches — 1..=512 bytes, no leading
4964    /// or trailing whitespace, no ASCII control characters anywhere
4965    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
4966    /// carriage return, and every other control byte), Unicode
4967    /// continuation bytes accepted (the canonical fixtures carry
4968    /// `→` and `—`). Closes the canonical paste-from-doc footguns
4969    /// the bare empty-arm gate left open: paste-from-aligned-doc
4970    /// leading / trailing whitespace (`" Checkout flow."`,
4971    /// `"Checkout flow. "`), paste-from-multiline-doc newline
4972    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
4973    /// (`"Checkout\rflow."`), tab-from-aligned-doc
4974    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
4975    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
4976    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
4977    /// [`Self::validate_edicao`] establish past their own empty arms
4978    /// on the sibling universal-axis `Option<String>` Caixa-level
4979    /// value-shape surfaces.
4980    ///
4981    /// The empty-first cascade discipline mirrors every peer per-axis
4982    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
4983    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
4984    /// diagnostic surfaces on `Some("")` rather than the broader
4985    /// shape-predicate diagnostic — peer with how
4986    /// [`ManifestError::LicencaEmpty`] runs before
4987    /// [`ManifestError::LicencaInvalid`],
4988    /// [`ManifestError::EdicaoEmpty`] runs before
4989    /// [`ManifestError::EdicaoInvalid`],
4990    /// [`ManifestError::RepositorioEmpty`] runs before
4991    /// [`ManifestError::RepositorioInvalid`].
4992    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
4993        let Some(s) = self.descricao() else {
4994            return Ok(());
4995        };
4996        if s.is_empty() {
4997            return Err(ManifestError::DescricaoEmpty);
4998        }
4999        crate::render::is_chart_description_shape(s).map_err(|reason| {
5000            ManifestError::DescricaoInvalid {
5001                descricao: s.to_string(),
5002                reason,
5003            }
5004        })?;
5005        Ok(())
5006    }
5007
5008    /// Reject `:licenca` values that are the empty string. The flat
5009    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
5010    /// SPDX-shaped license-expression axis every kind carries — the
5011    /// substrate routes the same string through the [`caixa-helm`]
5012    /// renderer's `build_readme` which folds it verbatim into the
5013    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
5014    /// section (`caixa-helm/src/lib.rs:361`) via
5015    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
5016    /// fallback only fires on `None`; a `Some("")` *skips the
5017    /// fallback* and silently passes the empty string through to a
5018    /// chart `README.md` whose `License` section renders as the bare
5019    /// trailing period (`.\n`) — peer footgun with the
5020    /// `Some("")`-skips-`unwrap_or_else` shape the
5021    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
5022    /// gates close on the sibling free-form-prose and git-URL axes.
5023    ///
5024    /// `None` (the canonical "omit the slot to defer to the
5025    /// renderer's `MIT` fallback" shape every existing fixture
5026    /// carries) is accepted trivially — the gate is a no-op when the
5027    /// author didn't declare a value. `Some("")` is gated by the
5028    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
5029    /// empty-arm shape every peer per-axis empty gate uses
5030    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5031    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5032    /// [`ManifestError::RepositorioEmpty`],
5033    /// [`ManifestError::DescricaoEmpty`]).
5034    ///
5035    /// Universal-axis (every kind carries `:licenca`), so wired at
5036    /// the caixa-build gate alongside the peer universal gates
5037    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5038    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5039    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5040    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
5041    /// — before the kind-coherence gates
5042    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5043    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5044    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5045    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5046    /// specific slot sets.
5047    ///
5048    /// Past the empty arm the gate enforces the SPDX-expression shape
5049    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
5050    /// structural alphabet floor every realistic SPDX expression in
5051    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
5052    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
5053    /// single ASCII space (token separator). Closes the canonical
5054    /// paste-from-doc footguns the bare empty-arm gate left open:
5055    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
5056    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
5057    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
5058    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
5059    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
5060    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
5061    /// Apache-2.0"`), and semicolon-list-separator confusion
5062    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
5063    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
5064    /// establish past their own empty arms.
5065    ///
5066    /// The empty-first cascade discipline mirrors every peer per-axis
5067    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
5068    /// [`ManifestError::LicencaInvalid`], so the narrower empty
5069    /// diagnostic surfaces on `Some("")` rather than the broader
5070    /// shape-predicate diagnostic — peer with how
5071    /// [`ManifestError::EdicaoEmpty`] runs before
5072    /// [`ManifestError::EdicaoInvalid`],
5073    /// [`ManifestError::RepositorioEmpty`] runs before
5074    /// [`ManifestError::RepositorioInvalid`].
5075    ///
5076    /// A future tightening on this axis can extend the alphabet
5077    /// floor into a full SPDX expression parser + license-id
5078    /// allowlist (rejecting alphabet-valid values that don't name a
5079    /// real SPDX license identifier — e.g., `"NotAReal"` is
5080    /// alphabet-valid but no `NotAReal` license-id exists). That
5081    /// parser only becomes meaningful past a real SPDX-spec
5082    /// dependency; this gate establishes the structural floor by
5083    /// refusing every non-SPDX-alphabet value at validate time.
5084    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
5085        let Some(s) = self.licenca() else {
5086            return Ok(());
5087        };
5088        if s.is_empty() {
5089            return Err(ManifestError::LicencaEmpty);
5090        }
5091        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
5092            ManifestError::LicencaInvalid {
5093                licenca: s.to_string(),
5094                reason,
5095            }
5096        })?;
5097        Ok(())
5098    }
5099
5100    /// Reject `:edicao` values that are the empty string. The flat
5101    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
5102    /// language-edition axis every kind carries — it determines the
5103    /// tatara-lisp macro surface + compatibility flags the substrate
5104    /// applies when building a caixa, and lands verbatim in the
5105    /// `Caixa::template` author-time scaffold (the canonical
5106    /// `:edicao "2026"` line every `feira init` emits via
5107    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
5108    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
5109    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
5110    /// `caixa-core/src/render.rs:2510`) via
5111    /// `edicao: Some("2026".into())`.
5112    ///
5113    /// `None` (the canonical "omit the slot to defer to the
5114    /// substrate's default edition" shape every existing
5115    /// [`caixa-resolver`] integration test fixture carries via
5116    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5117    /// is accepted trivially — the gate is a no-op when the author
5118    /// didn't declare a value. `Some("")` is gated by the narrower
5119    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
5120    /// shape every peer per-axis empty gate uses
5121    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5122    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5123    /// [`ManifestError::RepositorioEmpty`],
5124    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
5125    ///
5126    /// Universal-axis (every kind carries `:edicao`), so wired at
5127    /// the caixa-build gate alongside the peer universal gates
5128    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5129    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5130    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5131    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
5132    /// [`Self::validate_code_paths`] — before the kind-coherence
5133    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5134    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5135    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5136    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5137    /// specific slot sets.
5138    ///
5139    /// Past the empty arm the gate enforces the canonical year-shape
5140    /// predicate: every documented tatara-lisp edition is a 4-digit
5141    /// ASCII decimal year (`"2026"` is the only edition currently
5142    /// minted; future-introduced siblings will follow the same
5143    /// shape, peer with Cargo's `[package] edition` grammar which
5144    /// every value Cargo has ever accepted matches — `"2015"`,
5145    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
5146    /// 4 ASCII decimal bytes is rejected with the narrower
5147    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
5148    /// shape-predicate cascade [`Self::validate_repositorio`]
5149    /// establishes past its own empty arm
5150    /// ([`ManifestError::RepositorioEmpty`] →
5151    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
5152    /// paste-from-doc footguns the bare empty-arm gate left open:
5153    ///
5154    ///   - leading / trailing whitespace from a paste-from-doc
5155    ///     (`"2026 "`, `" 2026"`)
5156    ///   - control characters / CRLF from a paste-from-multiline-doc
5157    ///     (`"2026\n"`)
5158    ///   - non-ASCII look-alikes from a fullwidth keyboard
5159    ///     (`"2026"`) which would silently land as a non-ASCII
5160    ///     string in the rendered caixa.lisp
5161    ///   - free-form non-year values (`"x"`, `"latest"`,
5162    ///     `"nightly"`) that have no operational meaning on the
5163    ///     substrate's build-time edition selector
5164    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
5165    ///     `"r2026"`) — common version-tag idioms that don't apply
5166    ///     to the year-shaped edition axis
5167    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
5168    ///     edition is a year, not a fractional version
5169    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
5170    ///     `"00026"`) that don't name a year
5171    ///
5172    /// `None` (the canonical "omit the slot to defer to the
5173    /// substrate's default edition" shape every existing
5174    /// [`caixa-resolver`] integration test fixture carries via
5175    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5176    /// is accepted trivially — the gate is a no-op when the author
5177    /// didn't declare a value. The empty-first cascade discipline
5178    /// mirrors every peer per-axis identity gate:
5179    /// [`ManifestError::EdicaoEmpty`] runs before
5180    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
5181    /// diagnostic surfaces on `Some("")` rather than the broader
5182    /// shape-predicate diagnostic — peer with how
5183    /// [`ManifestError::NomeEmpty`] runs before
5184    /// [`ManifestError::NomeInvalid`],
5185    /// [`ManifestError::VersaoEmpty`] runs before
5186    /// [`ManifestError::VersaoInvalid`],
5187    /// [`ManifestError::RepositorioEmpty`] runs before
5188    /// [`ManifestError::RepositorioInvalid`].
5189    ///
5190    /// A future tightening on this axis can extend the shape
5191    /// predicate into a known-edition allowlist (rejecting
5192    /// year-shaped values that don't name a tatara-lisp edition
5193    /// the substrate actually understands — e.g., `"1999"` is
5194    /// year-shaped but no `1999` edition exists). That allowlist
5195    /// only becomes meaningful past the introduction of a sibling
5196    /// edition to `"2026"`; this gate establishes the structural
5197    /// floor by refusing every non-year-shaped value at validate
5198    /// time.
5199    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
5200        let Some(s) = self.edicao() else {
5201            return Ok(());
5202        };
5203        if s.is_empty() {
5204            return Err(ManifestError::EdicaoEmpty);
5205        }
5206        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
5207            return Err(ManifestError::EdicaoInvalid {
5208                edicao: s.to_string(),
5209                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
5210            });
5211        }
5212        Ok(())
5213    }
5214
5215    /// Compose the supervisor-related flat slots into a single
5216    /// [`SupervisorSpec`] for validation. Returns `None` when the
5217    /// caixa isn't a `:kind Supervisor`.
5218    ///
5219    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
5220    /// simple (one form, no nested `:supervisor (…)` block); this view
5221    /// is the "typed shape" the operator + supervisor reconciler
5222    /// consume.
5223    #[must_use]
5224    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
5225        if !self.kind().is_supervisor() {
5226            return None;
5227        }
5228        // Fold through the shared `supervisor::duration_codec::parse`
5229        // — the same parser the serde-routed `with = "duration_codec"`
5230        // on `SupervisorSpec::restart_window`, the `:politicas
5231        // :timeout` codec, and the `:politicas :circuit-breaker
5232        // :window` codec all consume. The prior inline f64-shaped
5233        // duplicate (`parse_window_inline`) admitted every magnitude
5234        // the integer-magnitude gate (1c55a2a) rejects on the three
5235        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
5236        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
5237        // `None` (i.e. "no reset"), divergent from the shared codec's
5238        // integer-magnitude discipline by construction. The fold
5239        // closes the divergence: every value the typed
5240        // `SupervisorSpec` carries past `supervisor_view` is in the
5241        // shared codec's accepted set. The `.ok()` here preserves the
5242        // existing soft-swallow shape on this view-construction path;
5243        // the new [`Caixa::validate_restart_window`] (sibling of
5244        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
5245        // the offending raw string at build time so authoring tools
5246        // (`feira lint`, the future layout-side wire-up) surface a
5247        // self-locating diagnostic instead of a silently dropped
5248        // window.
5249        let restart_window = self
5250            .restart_window()
5251            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
5252        Some(SupervisorSpec {
5253            // Route the author-omitted `:estrategia` arm through the
5254            // substrate-canonical
5255            // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
5256            // `pub const` rather than the transitively-derived
5257            // [`RestartStrategy::default`] route the prior
5258            // `.unwrap_or_default()` fold reached for — one source of
5259            // truth for the Erlang/OTP `one_for_one` half of Learn You
5260            // Some Erlang's `{one_for_one, intensity, 5, 60}` worker-
5261            // supervisor canonical default that also backs the
5262            // [`crate::supervisor::Default for RestartStrategy`] impl
5263            // and the [`crate::supervisor::Default for SupervisorSpec`]
5264            // impl's struct-literal `estrategia` field, all now routed
5265            // through the same lifted constant. Prior to the lift the
5266            // composition site carried `.unwrap_or_default()` with no
5267            // compile-time link back to the shared OTP-canonical
5268            // default that the peer paired
5269            // `.unwrap_or(SUPERVISOR_MAX_RESTARTS_DEFAULT)` (b698ec0)
5270            // arm on the sibling `:max-restarts` axis routes through —
5271            // so a future rebrand of the OTP-canonical strategy default
5272            // (a widening to `rest_for_one` once the substrate
5273            // discovers startup-order-coupled child cohorts as the more
5274            // common shape, a per-cluster overlay the operator pins
5275            // through the MESH-COMPOSITION §III.2 supervision-canary
5276            // `:estrategia-overrides` roadmap slot) would have had to
5277            // migrate the paired `MaxIntensity` + `Period` halves
5278            // through the lifted constants and the `one_for_one` half
5279            // through a `RestartStrategy::default()` route in lockstep
5280            // or the three halves of the same OTP-canonical default
5281            // would silently drift out of pairing. Byte-parity against
5282            // the lifted constant closes the split. Pinned by
5283            // [`supervisor_view_estrategia_fallback_routes_through_lifted_default`]
5284            // in the tests module.
5285            estrategia: self
5286                .estrategia()
5287                .unwrap_or(crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT),
5288            // Route the author-omitted `:max-restarts` arm through the
5289            // substrate-canonical [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`]
5290            // typed `pub const` rather than the raw `5` literal — one
5291            // source of truth for the Erlang/OTP-canonical
5292            // `{intensity, 5, 60}` `MaxIntensity` default that also
5293            // backs the serde-side wire-format author-omitted arm on
5294            // [`crate::supervisor::SupervisorSpec::max_restarts`] via
5295            // `#[serde(default = "default_max_restarts")]` and the
5296            // [`Default for SupervisorSpec`] impl's struct-literal
5297            // default field. Prior to the lift the composition site
5298            // carried a raw `5` with no compile-time link back to the
5299            // serde-side default, so a future rebrand of the OTP-
5300            // canonical default (a tightening to Elixir's `3`, a
5301            // widening to a per-cluster overlay the operator pins
5302            // through the MESH-COMPOSITION §III.2 supervision-canary
5303            // `:supervisor :max-restarts-overrides` roadmap slot)
5304            // would have had to be threaded through both open-coded
5305            // copies in lockstep or the wire-format author-omitted arm
5306            // and this view-construction author-omitted arm would
5307            // silently disagree on which restart-budget an omitted
5308            // `:max-restarts` resolves to. Pinned by
5309            // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
5310            // in the tests module.
5311            max_restarts: self
5312                .max_restarts()
5313                .unwrap_or(crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT),
5314            restart_window,
5315            children: self.children().to_vec(),
5316        })
5317    }
5318
5319    /// A minimal starter manifest emitted by `feira init`.
5320    #[must_use]
5321    pub fn template(nome: &str) -> String {
5322        format!(
5323            "(defcaixa\n  \
5324               :nome        {nome:?}\n  \
5325               :versao      \"0.1.0\"\n  \
5326               :kind        Biblioteca\n  \
5327               :edicao      \"2026\"\n  \
5328               :descricao   \"FIXME — describe this caixa\"\n  \
5329               :autores     ()\n  \
5330               :etiquetas   ()\n  \
5331               :deps        ()\n  \
5332               :deps-dev    ()\n  \
5333               :bibliotecas (\"lib/{nome}.lisp\"))\n"
5334        )
5335    }
5336
5337    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
5338    /// back after mutation (e.g. `feira add`).
5339    ///
5340    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
5341    /// The derive-macro `compile_from_sexp` path is the inverse, so any
5342    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
5343    #[must_use]
5344    pub fn to_lisp(&self) -> String {
5345        let json = serde_json::to_value(self).expect("Caixa serialize");
5346        let sexp = tatara_lisp::domain::json_to_sexp(&json);
5347        let tatara_lisp::Sexp::List(items) = sexp else {
5348            return format!("(defcaixa {sexp})\n");
5349        };
5350        let mut out = String::from("(defcaixa");
5351        let mut i = 0;
5352        while i + 1 < items.len() {
5353            out.push_str("\n  ");
5354            out.push_str(&items[i].to_string());
5355            out.push(' ');
5356            out.push_str(&items[i + 1].to_string());
5357            i += 2;
5358        }
5359        out.push_str(")\n");
5360        out
5361    }
5362}
5363
5364/// Errors raised by top-level [`Caixa`] validators that don't fit
5365/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
5366/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
5367/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
5368/// through every substrate-side artifact's `metadata.name` /
5369/// version derivation.
5370///
5371/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
5372/// doc-comment anticipates) can hold one of each per-axis error
5373/// family without reshaping individual diagnostics; this enum is
5374/// the first such per-Caixa-identity family.
5375#[derive(Debug, Error, PartialEq, Eq)]
5376pub enum ManifestError {
5377    #[error(
5378        ":nome is empty (every caixa must name itself; the value flows \
5379         into every K8s artifact's `metadata.name` derivation and into \
5380         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
5381    )]
5382    NomeEmpty,
5383    #[error(
5384        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
5385         apiserver enforces this rule on every `metadata.name` the \
5386         caixa's substrate-side renderers derive from `:nome` — the \
5387         `lareira-<nome>` Helm chart name, the programs.yaml entry \
5388         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
5389         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
5390         name; use a lowercase alphanumeric + hyphen identifier like \
5391         `\"checkout\"` or `\"cart-v2\"`)"
5392    )]
5393    NomeInvalid { nome: String, reason: String },
5394    #[error(
5395        ":nome {nome:?} overflows the joint-length budget on the canonical \
5396         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
5397         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
5398         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
5399         `chart:` slot, `caixa-tatara`'s `release_name` + \
5400         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
5401         joint name through the canonical `lareira_chart_name` helper, and \
5402         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
5403         DNS-1123 label cap on every chart-name-derived `metadata.name` \
5404         reject any joint name exceeding 63 bytes; the narrower \
5405         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
5406         arm gates the chart-name budget downstream renderers inherit)"
5407    )]
5408    NomeChartNameBudgetExceeded { nome: String, reason: String },
5409    #[error(
5410        ":versao is empty (every caixa must pin its own version; the value flows \
5411         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
5412         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
5413         `:latest` tags, the lacre closure's `concrete_versao`, and the \
5414         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
5415    )]
5416    VersaoEmpty,
5417    #[error(
5418        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
5419         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
5420         with optional `-prerelease` and `+build` — across every artifact derived \
5421         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
5422         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
5423         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
5424         and the `:upgrade-from :from` peers that match against this exact shape; \
5425         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
5426         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
5427         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
5428    )]
5429    VersaoInvalid { versao: String, reason: String },
5430    #[error(
5431        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
5432         substrate consumes this string through the shared \
5433         `supervisor::duration_codec` — the same parser routed via `with = \
5434         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
5435         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
5436         the canonical authoring form is `<integer><unit>` where the unit is one \
5437         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
5438         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
5439         Without this gate a malformed `:restart-window` silently produced a \
5440         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
5441         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
5442         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
5443         layer with the offending value named verbatim. Omit the slot entirely to \
5444         express \"no reset\"; carry a positive integer duration to express the \
5445         sliding window)"
5446    )]
5447    RestartWindowMalformed {
5448        restart_window: String,
5449        reason: String,
5450    },
5451    #[error(
5452        "{slot} entry is an empty path string — every {slot} entry must name \
5453         a file relative to the caixa root; omit the entry to omit the file \
5454         (the layout checker's `root.join(\"\")` resolves to the caixa root \
5455         itself, so an empty entry silently aliases the project root as a \
5456         declared {slot} file, then fails downstream at parse / existence \
5457         time with a diagnostic that names the root rather than the offending \
5458         entry)"
5459    )]
5460    CodePathEmpty { slot: &'static str },
5461    #[error(
5462        "{slot} entry {} is an absolute path — entries must be relative to \
5463         the caixa root, since `Path::join` replaces the base with an absolute \
5464         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
5465         outside the caixa root sandbox; rewrite the entry as a relative path \
5466         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
5467         `\"servicos/<name>.computeunit.yaml\"`)",
5468        path.display()
5469    )]
5470    CodePathAbsolute { slot: &'static str, path: PathBuf },
5471    #[error(
5472        "{slot} entry {} contains a `..` component — entries must not traverse \
5473         above the caixa root (the layout's `starts_with(<dir>)` fence on \
5474         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
5475         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
5476         has no such fence, so a leading `..` escapes unconditionally if the \
5477         resolved target happens to exist)",
5478        path.display()
5479    )]
5480    CodePathParentEscape { slot: &'static str, path: PathBuf },
5481    #[error(
5482        "{slot} entry {} does not terminate in the `.lisp` extension — every \
5483         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
5484         loop reads through `tatara_lisp::read` at parse time, so any other \
5485         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
5486         structurally a parser error far from the source caixa.lisp, with \
5487         no field naming the offending `:bibliotecas` entry. Pin a relative \
5488         path under the caixa root whose terminating extension is \
5489         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
5490         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
5491         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
5492         (33cc830) axes already carry through the same lifted \
5493         `is_lisp_extension` predicate",
5494        path.display()
5495    )]
5496    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
5497    #[error(
5498        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
5499         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
5500         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
5501         through `serde_yaml::from_str` at chart / FluxCD bundle render \
5502         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
5503         off-by-one-segment `.computeunit-yaml`, the editor-backup \
5504         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
5505         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
5506         source caixa.lisp, with no field naming the offending `:servicos` \
5507         entry. Pin a relative path under the caixa root whose terminating \
5508         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
5509         `\"servicos/<name>.computeunit.yaml\"`, \
5510         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
5511         contract the sibling `:bibliotecas` axis (64772a9) already carries \
5512         on the tatara-lisp-source axis through the peer lifted \
5513         `is_lisp_extension` predicate, here on the compound-suffix axis \
5514         `Path::extension` can't express on its own through the lifted \
5515         `is_computeunit_yaml_extension` predicate",
5516        path.display()
5517    )]
5518    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
5519    #[error(
5520        "{slot} entry {} appears more than once (the code-path list is \
5521         a set, not a multiset; every peer Vec-shaped author-supplied \
5522         list past validate is set-not-multiset — `:membros :caixa`, \
5523         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5524         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
5525         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
5526         code-path lists are the last Vec-shaped author-supplied slots on \
5527         the typed Caixa surface still admitting a duplicate entry. \
5528         `:bibliotecas` duplicates re-parse the same file at \
5529         `feira build` time and silently mask the author's intent to \
5530         declare a *second* biblioteca; `:exe` duplicates collide on the \
5531         flake `packages.<name>` derivation key at the future \
5532         `caixa-flake` materializer; `:servicos` duplicates surface as the \
5533         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
5534         rejection far from the source `caixa.lisp`. Drop the duplicate \
5535         or rename it to the actual second file intended)",
5536        path.display()
5537    )]
5538    CodePathDuplicate { slot: &'static str, path: PathBuf },
5539    #[error(
5540        ":etiquetas entry is empty (every tag must carry a non-empty \
5541         registry-search identifier; the empty entry has no operational \
5542         meaning — it indexes nothing in the future caixa-registry search \
5543         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
5544         with a no-op tag; omit the entry to express \"no tag on this \
5545         position\")"
5546    )]
5547    EtiquetaEmpty,
5548    #[error(
5549        ":etiquetas entry {etiqueta:?} appears more than once (the \
5550         registry-search tag set is a set, not a multiset; duplicate \
5551         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
5552         at chart render — a \"second wins / one silently disappears\" \
5553         shape divergent from every peer typed-graph set gate \
5554         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
5555         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
5556         duplicate or rename it to the actual tag intended)"
5557    )]
5558    EtiquetaDuplicate { etiqueta: String },
5559    #[error(
5560        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
5561         {reason} (the substrate consumes this string through the shared \
5562         `crate::render::is_chart_keyword_shape` predicate — the same \
5563         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
5564         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
5565         continuation. The canonical authoring shapes are short kebab-case \
5566         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
5567         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
5568         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
5569         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
5570         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
5571         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
5572         `\"mesh,http,grpc\"` — the author meant to author three separate \
5573         list entries; path-separator confusion `\"caixa/servico\"`; \
5574         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
5575         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
5576         `\"café\"` — every legitimate search tag is strict ASCII; \
5577         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
5578         passed `from_lisp` + `validate_etiquetas` + \
5579         `StandardLayout::verify` and landed in the rendered \
5580         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
5581         malformed search tag — Artifact Hub's keyword index + the future \
5582         caixa-registry's keyword index would either silently drop the \
5583         tag or fail to index it far from the source caixa.lisp; the gate \
5584         moves the diagnostic to the manifest layer with the offending \
5585         value named verbatim)"
5586    )]
5587    EtiquetaInvalid { etiqueta: String, reason: String },
5588    #[error(
5589        ":autores entry is empty (every maintainer must carry a non-empty \
5590         identifier; the empty entry has no operational meaning — it \
5591         identifies no one in the substrate's authorship index and renders \
5592         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
5593         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
5594         omit the entry to express \"no maintainer on this position\")"
5595    )]
5596    AutorEmpty,
5597    #[error(
5598        ":autores entry {autor:?} appears more than once (the maintainer \
5599         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
5600         `maintainers:` rendering does *no* dedup — duplicate entries \
5601         stack verbatim in `Chart.yaml` as two identical \
5602         `Maintainer {{ name, email: None }}` records, divergent from every \
5603         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
5604         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5605         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
5606         rename it to the actual author intended)"
5607    )]
5608    AutorDuplicate { autor: String },
5609    #[error(
5610        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
5611         {reason} (the substrate consumes this string through the shared \
5612         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
5613         single-line-UTF-8 floor every realistic chart maintainer name carries: \
5614         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
5615         characters anywhere, Unicode bytes accepted. The canonical authoring \
5616         shapes are short single-line identifiers like `\"pleme-io\"`, \
5617         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
5618         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
5619         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
5620         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
5621         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
5622         records into one entry instead of splitting into one entry per author; \
5623         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
5624         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
5625         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5626         `validate_autores` + `StandardLayout::verify` and landed in the \
5627         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
5628         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
5629         round-trip — every chart-aware UI (`helm list`, `helm search`, \
5630         Artifact Hub maintainer index) would render the maintainer name in a \
5631         single-line column far from the source caixa.lisp; the gate moves the \
5632         diagnostic to the manifest layer with the offending value named \
5633         verbatim)"
5634    )]
5635    AutorInvalid { autor: String, reason: String },
5636    #[error(
5637        ":repositorio is the empty string (every published caixa names its \
5638         git source via a non-empty `:repositorio` locator — the value \
5639         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
5640         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
5641         `GitRepository.spec.url` via `caixa-flux`'s \
5642         `ClusterBundleOpts::for_caixa`; both consumers' \
5643         `Option::unwrap_or_else` fallbacks only fire when the slot is \
5644         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
5645         `url: \"\"` in the rendered artifacts and breaks at `helm \
5646         template` / FluxCD source-controller reconcile time far from the \
5647         source caixa.lisp; omit the slot entirely to defer to the \
5648         renderer's `https://github.com/pleme-io/<nome>` / \
5649         `caixa.nome`-derived fallback, or carry a canonical authoring \
5650         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
5651         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
5652         `\"file:///path\"`)"
5653    )]
5654    RepositorioEmpty,
5655    #[error(
5656        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
5657         (the substrate consumes this string through the shared \
5658         `crate::render::is_git_repo_url` predicate — the same parser the \
5659         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
5660         value through via `DepSource::validate`; the canonical authoring \
5661         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
5662         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
5663         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
5664         scp-style SSH form. Without this gate a malformed `:repositorio` \
5665         (whitespace from a paste-from-doc; control characters / CRLF \
5666         from a paste-from-multiline-doc; a leading `-` from a \
5667         CLI-argument-injection footgun; a missing `:` separator from a \
5668         bare `org/repo` shape git treats as a relative filesystem path) \
5669         silently landed in the rendered `Chart.yaml home:` and the \
5670         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
5671         FluxCD reconcile time far from the source caixa.lisp; the gate \
5672         moves the diagnostic to the manifest layer with the offending \
5673         value named verbatim)"
5674    )]
5675    RepositorioInvalid { repositorio: String, reason: String },
5676    #[error(
5677        ":descricao is the empty string (every published caixa names \
5678         its purpose via a non-empty `:descricao` summary — the value \
5679         flows verbatim into the rendered `lareira-<nome>` Helm \
5680         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
5681         `build_chart_yaml` and into the chart `README.md` header via \
5682         `build_readme`; both consumers' `Option::unwrap_or_else` \
5683         `caixa.nome`-derived fallbacks only fire when the slot is \
5684         `None`, so an empty `Some(\"\")` silently lands as \
5685         `description: \"\"` / a blank `README.md` header in the \
5686         rendered artifacts and breaks at `helm lint` time \
5687         (`WARNING [chart.metadata.description]: description is \
5688         required` on `apiVersion: v2` charts) far from the source \
5689         caixa.lisp; omit the slot entirely to defer to the \
5690         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
5691         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
5692         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
5693         Servico.\"`)"
5694    )]
5695    DescricaoEmpty,
5696    #[error(
5697        ":descricao {descricao:?} is not a valid chart-description shape: \
5698         {reason} (the substrate consumes this string through the shared \
5699         `crate::render::is_chart_description_shape` predicate — the same \
5700         single-line-UTF-8 floor every realistic chart description carries: \
5701         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
5702         characters anywhere, Unicode prose bytes accepted. The canonical \
5703         authoring shapes are short single-line summaries like `\"Canonical \
5704         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
5705         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
5706         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
5707         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
5708         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
5709         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
5710         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
5711         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5712         `validate_descricao` + `StandardLayout::verify` and landed in the \
5713         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
5714         field + `README.md` header paragraph as a YAML-illegal multi-line \
5715         scalar or a silently-trimmed whitespace round-trip — every \
5716         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
5717         render the description in a single-line column far from the source \
5718         caixa.lisp; the gate moves the diagnostic to the manifest layer \
5719         with the offending value named verbatim)"
5720    )]
5721    DescricaoInvalid { descricao: String, reason: String },
5722    #[error(
5723        ":licenca is the empty string (every published caixa names \
5724         its license via a non-empty `:licenca` SPDX expression — the \
5725         value flows verbatim into the rendered `lareira-<nome>` Helm \
5726         chart's `README.md` `## License` section via `caixa-helm`'s \
5727         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
5728         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
5729         only fires when the slot is `None`, so an empty `Some(\"\")` \
5730         silently lands as a bare trailing period in the rendered \
5731         chart `README.md` `License` section far from the source \
5732         caixa.lisp; omit the slot entirely to defer to the \
5733         renderer's `MIT` fallback, or carry a canonical SPDX \
5734         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
5735         `\"Apache-2.0 OR MIT\"`)"
5736    )]
5737    LicencaEmpty,
5738    #[error(
5739        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
5740         (the substrate consumes this string through the shared \
5741         `crate::render::is_spdx_expression_shape` predicate — the same \
5742         alphabet-floor parser every peer per-axis value-shape gate routes \
5743         its value through; the canonical authoring shapes are single \
5744         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
5745         compound expressions like `\"Apache-2.0 OR MIT\"`, \
5746         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
5747         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
5748         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
5749         like `\"LicenseRef-MyLicense\"` / \
5750         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
5751         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
5752         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
5753         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
5754         a smart-quote paste; underscore-instead-of-hyphen typo \
5755         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
5756         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
5757         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
5758         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
5759         `README.md` `## License` section + a future SPDX-aware \
5760         `Chart.yaml license:` emitter would refuse the value at \
5761         `helm lint` time far from the source caixa.lisp; the gate moves \
5762         the diagnostic to the manifest layer with the offending value \
5763         named verbatim)"
5764    )]
5765    LicencaInvalid { licenca: String, reason: String },
5766    #[error(
5767        ":edicao is the empty string (every published caixa names \
5768         its language edition via a non-empty `:edicao` value — the \
5769         edition determines the tatara-lisp macro surface + \
5770         compatibility flags the substrate applies when building \
5771         the caixa; the canonical `Caixa::template` scaffold every \
5772         `feira init` emits carries `:edicao \"2026\"` verbatim and \
5773         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
5774         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
5775         construction, so an empty `Some(\"\")` silently lands as a \
5776         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
5777         a future renderer-side consumer that folds it through \
5778         `Option::unwrap_or_else` will skip the fallback and pass the \
5779         empty edition through to the substrate's build-time edition \
5780         selector far from the source caixa.lisp; omit the slot \
5781         entirely to defer to the substrate's default edition, or \
5782         carry a canonical edition like `\"2026\"`)"
5783    )]
5784    EdicaoEmpty,
5785    #[error(
5786        ":edicao {edicao:?} is not a valid edition: {reason} (every \
5787         documented tatara-lisp edition is a 4-digit ASCII decimal \
5788         year — `\"2026\"` is the only edition currently minted; \
5789         future-introduced siblings will follow the same shape, peer \
5790         with Cargo's `[package] edition` grammar which every value \
5791         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
5792         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
5793         paste-from-doc footguns silently passed: a trailing space \
5794         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
5795         from a paste-from-multiline-doc, a fullwidth-keyboard \
5796         look-alike (`\"2026\"`), a free-form non-year value \
5797         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
5798         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
5799         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
5800         wrong-length numeric value (`\"26\"`, `\"202\"`, \
5801         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
5802         rendered caixa.lisp and broke at the substrate's \
5803         build-time edition selector far from the source caixa.lisp; \
5804         omit the slot entirely to defer to the substrate's default \
5805         edition, or carry a canonical 4-digit ASCII decimal year \
5806         like `\"2026\"`)"
5807    )]
5808    EdicaoInvalid { edicao: String, reason: String },
5809}
5810
5811#[cfg(test)]
5812mod tests {
5813    use super::*;
5814
5815    #[test]
5816    fn template_round_trips() {
5817        let src = Caixa::template("demo");
5818        let c = Caixa::from_lisp(&src).expect("template must parse");
5819        assert_eq!(c.nome, "demo");
5820        assert_eq!(c.versao, "0.1.0");
5821        assert_eq!(c.kind, CaixaKind::Biblioteca);
5822        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
5823        assert!(c.deps.is_empty());
5824        assert!(c.deps_dev.is_empty());
5825    }
5826
5827    #[test]
5828    fn register_populates_registry() {
5829        Caixa::register().expect("first register call in this test process must succeed");
5830        let kws = tatara_lisp::domain::registered_keywords();
5831        assert!(kws.contains(&"defcaixa"));
5832    }
5833
5834    #[test]
5835    fn to_lisp_round_trips() {
5836        let src = Caixa::template("demo");
5837        let c1 = Caixa::from_lisp(&src).unwrap();
5838        let emitted = c1.to_lisp();
5839        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
5840        assert_eq!(c1, c2);
5841    }
5842
5843    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
5844    //
5845    // The compounding pin: the variant stores only the typed
5846    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
5847    // (canonical keyword, description, consumer) routes through the enum's
5848    // own accessors at Display time. Prior to that closure the variant
5849    // carried each accessor's return value as a stored `&'static str`
5850    // snapshot alongside `dialeto`; a caller could construct the variant
5851    // with a snapshot that drifted from what `dialeto`'s accessors would
5852    // return, and every downstream user-facing projection would silently
5853    // disagree with the classification. Storing only the axis makes the
5854    // drift structurally impossible.
5855
5856    #[test]
5857    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
5858        // Single-field construction is the whole compounding shape — a
5859        // future re-introduction of a snapshot field (a `palavra_canonica:
5860        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
5861        // would re-open the drift surface and this construction would fail
5862        // to compile with "missing field" until every snapshot was seeded
5863        // at the call site again. The compile-time guarantee is the
5864        // invariant; the assertion below only witnesses that the
5865        // construction is well-formed after the closure.
5866        let err = LeituraError::DialetoEstrangeiro {
5867            dialeto: crate::dialeto::CaixaDialeto::Molde,
5868        };
5869        assert!(matches!(
5870            err,
5871            LeituraError::DialetoEstrangeiro {
5872                dialeto: crate::dialeto::CaixaDialeto::Molde,
5873            }
5874        ));
5875    }
5876
5877    #[test]
5878    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
5879        // For every foreign-dialect classification the variant surfaces —
5880        // [`crate::dialeto::CaixaDialeto::Molde`] and
5881        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
5882        // variants [`Caixa::from_lisp`] raises this error for — the
5883        // rendered [`std::fmt::Display`] byte-string must interpolate each
5884        // typed accessor's return verbatim. A future re-introduction of a
5885        // stored `&'static str` snapshot alongside `dialeto` that Display
5886        // read instead of the accessor would fail this pin as soon as the
5887        // two disagreed; a future accessor rebrand (a per-dialect
5888        // consumer rename, a canonical-keyword shift once the substrate
5889        // migration named in [`crate::dialeto`] completes) reaches every
5890        // consumer through one typed dispatch and this pin verifies the
5891        // display path is one of them.
5892        for d in [
5893            crate::dialeto::CaixaDialeto::Molde,
5894            crate::dialeto::CaixaDialeto::MoldePosicional,
5895        ] {
5896            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
5897            assert!(
5898                rendered.contains(d.palavra_canonica()),
5899                "Display must interpolate `dialeto.palavra_canonica()` \
5900                 verbatim — a stored snapshot would silently drift from \
5901                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
5902            );
5903            assert!(
5904                rendered.contains(d.descricao()),
5905                "Display must interpolate `dialeto.descricao()` verbatim. \
5906                 dialect: {d}, rendered: {rendered:?}"
5907            );
5908            assert!(
5909                rendered.contains(d.consumidor()),
5910                "Display must interpolate `dialeto.consumidor()` verbatim. \
5911                 dialect: {d}, rendered: {rendered:?}"
5912            );
5913        }
5914    }
5915
5916    #[test]
5917    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
5918        // The end-to-end pin the compounding closure defends: a
5919        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
5920        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
5921        // rendered Display byte-string names the Molde accessors'
5922        // returns verbatim. Any future path that constructed the variant
5923        // with a mismatched snapshot (a stored `palavra_canonica:
5924        // "defcaixa"` on a `Molde` classification) would land Display
5925        // pointing at `defcaixa` while the typed axis said `Molde` — the
5926        // exact drift the closure removes.
5927        let src = r#"
5928          (defcaixa
5929            :name "x"
5930            :kind :Biblioteca
5931            :ecosystem :rust-single-crate
5932            :package {:name "x" :version "0.1.0"})
5933        "#;
5934        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
5935        match err {
5936            LeituraError::DialetoEstrangeiro { dialeto } => {
5937                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
5938                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5939                assert!(rendered.contains(dialeto.palavra_canonica()));
5940                assert!(rendered.contains(dialeto.consumidor()));
5941                assert!(rendered.contains(dialeto.descricao()));
5942            }
5943            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
5944        }
5945    }
5946
5947    #[test]
5948    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
5949        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
5950        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
5951        // positional-arity `defmolde` form written under a `(defcaixa …)`
5952        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
5953        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
5954        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
5955        // so no test exercised the positional-arity path through
5956        // `Caixa::from_lisp` specifically; the sibling
5957        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
5958        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
5959        // two arms route through the lifted
5960        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
5961        // typed predicate — the same predicate the pre-lift `foreign =>`
5962        // wildcard resolved to today — and this pin makes the
5963        // positional-arity arm's byte-shape at the gate explicit rather
5964        // than implied by wildcard-absorption. A future regression that
5965        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
5966        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
5967        // from the two-arity closure) would fail this pin at caixa-core
5968        // test time rather than surfacing far from the change as a
5969        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
5970        // …)` silently parsing past the derive.
5971        let src = r#"
5972          (defcaixa todoku-go
5973            :kind :Biblioteca
5974            :ecosystem :go
5975            :package {:name "todoku-go" :version "0.3.0"})
5976        "#;
5977        let err =
5978            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
5979        match err {
5980            LeituraError::DialetoEstrangeiro { dialeto } => {
5981                assert_eq!(
5982                    dialeto,
5983                    crate::dialeto::CaixaDialeto::MoldePosicional,
5984                    "DialetoEstrangeiro must carry the MoldePosicional \
5985                     variant verbatim — the positional-arity `defmolde` \
5986                     form under a `(defcaixa …)` head is the \
5987                     `MoldePosicional` arm's canonical byte-shape"
5988                );
5989                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5990                assert!(
5991                    rendered.contains(dialeto.palavra_canonica()),
5992                    "Display must interpolate `dialeto.palavra_canonica()` \
5993                     verbatim on the MoldePosicional arm; rendered: \
5994                     {rendered:?}"
5995                );
5996                assert!(
5997                    rendered.contains(dialeto.consumidor()),
5998                    "Display must interpolate `dialeto.consumidor()` \
5999                     verbatim on the MoldePosicional arm; rendered: \
6000                     {rendered:?}"
6001                );
6002                assert!(
6003                    rendered.contains(dialeto.descricao()),
6004                    "Display must interpolate `dialeto.descricao()` \
6005                     verbatim on the MoldePosicional arm; rendered: \
6006                     {rendered:?}"
6007                );
6008            }
6009            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
6010        }
6011    }
6012
6013    #[test]
6014    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
6015        // Load-bearing byte-parity pin: for every arm in
6016        // [`crate::dialeto::CaixaDialeto::ALL`], the
6017        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
6018        // partition must agree with the lifted
6019        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
6020        // typed predicate — i.e. from_lisp raises
6021        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
6022        // `d.is_molde_family()` returns `true`, and does NOT raise
6023        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
6024        // predicate returns `false` (the arm's source falls through to
6025        // the derive — parses cleanly on
6026        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
6027        // [`LeituraError::Leitura`] on
6028        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
6029        //
6030        // Pre-lift the gate hand-rolled a three-arm match
6031        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
6032        // whose `foreign =>` wildcard expressed no compile-time link
6033        // back to the substrate primitive's arm-family; a future fifth
6034        // dialect the [`crate::dialeto`] module doc's "third dialect"
6035        // hazard actualises would fall silently onto the wildcard
6036        // regardless of whether it belonged to the `defmolde` family or
6037        // to a distinct `defcaixa`-family. Post-lift the partition
6038        // resolves through
6039        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
6040        // typed dispatch, and this pin refuses any future regression
6041        // that silently split the from_lisp partition from the typed
6042        // predicate — the two paths now migrate as one on any future
6043        // arm addition.
6044        //
6045        // Sibling in shape to the peer
6046        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
6047        // (e9d2315) that pins the same byte-parity between
6048        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
6049        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
6050        // `== "defmolde"` classifier — extends the discipline from the
6051        // two paths within the [`crate::dialeto`] primitive onto the
6052        // third external consumer of the `defmolde`-family partition
6053        // (the [`Caixa::from_lisp`] gate that raises
6054        // [`LeituraError::DialetoEstrangeiro`]).
6055        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
6056            (
6057                crate::dialeto::CaixaDialeto::Pacote,
6058                r#"
6059                  (defcaixa
6060                    :nome   "checkout"
6061                    :versao "0.1.0"
6062                    :kind   Biblioteca
6063                    :edicao "2026"
6064                    :descricao "canonical Pacote source"
6065                    :autores ()
6066                    :etiquetas ()
6067                    :deps ()
6068                    :deps-dev ()
6069                    :bibliotecas ("lib/checkout.lisp"))
6070                "#,
6071            ),
6072            (
6073                crate::dialeto::CaixaDialeto::Molde,
6074                r#"
6075                  (defcaixa
6076                    :name "base64"
6077                    :kind :Biblioteca
6078                    :ecosystem :rust-single-crate
6079                    :package {:name "base64" :version "0.22.1"}
6080                    :workflows [:auto-release])
6081                "#,
6082            ),
6083            (
6084                crate::dialeto::CaixaDialeto::MoldePosicional,
6085                r#"
6086                  (defcaixa todoku-go
6087                    :kind :Biblioteca
6088                    :ecosystem :go
6089                    :package {:name "todoku-go" :version "0.3.0"})
6090                "#,
6091            ),
6092            (
6093                crate::dialeto::CaixaDialeto::Desconhecido,
6094                r#"(defcaixa :licenca "MIT")"#,
6095            ),
6096        ];
6097
6098        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
6099        // must appear in the fixture table so the pin's arm-set stays
6100        // synchronised with the enum's arm-set. Fails at test time if a
6101        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
6102        // (with a corresponding `is_molde_family` return) forgot to
6103        // extend this fixture table with a canonical source for the new
6104        // arm — the pin cannot cover an arm it has no source for.
6105        for &expected in crate::dialeto::CaixaDialeto::ALL {
6106            assert!(
6107                fixtures.iter().any(|(d, _)| *d == expected),
6108                "fixture table must carry a canonical source for every \
6109                 CaixaDialeto arm; missing: {expected:?}"
6110            );
6111        }
6112
6113        for &(expected_dialect, src) in fixtures {
6114            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
6115                panic!(
6116                    "fixture source for {expected_dialect:?} must classify \
6117                     cleanly, got err: {err:?}"
6118                )
6119            });
6120            assert_eq!(
6121                classified, expected_dialect,
6122                "fixture source for {expected_dialect:?} must classify as \
6123                 {expected_dialect:?} (drift here defeats the byte-parity \
6124                 pin below — a source labelled for one arm but classifying \
6125                 as another would silently satisfy or violate the pin for \
6126                 the wrong reason)"
6127            );
6128
6129            let outcome = Caixa::from_lisp(src);
6130            match (expected_dialect.is_molde_family(), &outcome) {
6131                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
6132                    assert_eq!(
6133                        *dialeto, expected_dialect,
6134                        "DialetoEstrangeiro must carry the same typed arm \
6135                         the classifier returned — a drift here would let \
6136                         from_lisp raise the error while pointing at the \
6137                         wrong dialect (e.g. rejecting a \
6138                         MoldePosicional source as Molde). arm: \
6139                         {expected_dialect:?}"
6140                    );
6141                }
6142                (true, other) => panic!(
6143                    "arm {expected_dialect:?} has is_molde_family() = true \
6144                     so from_lisp must raise DialetoEstrangeiro carrying \
6145                     {expected_dialect:?}; got: {other:?}"
6146                ),
6147                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
6148                    "arm {expected_dialect:?} has is_molde_family() = false \
6149                     so from_lisp must NOT raise DialetoEstrangeiro; got \
6150                     one carrying: {dialeto:?}. This means the typed \
6151                     predicate and the from_lisp partition disagree on \
6152                     this arm — exactly the drift this pin refuses."
6153                ),
6154                (false, _) => {
6155                    // A non-molde arm's source falls through to the
6156                    // derive: Pacote sources parse to Ok(_); Desconhecido
6157                    // sources surface as LeituraError::Leitura from the
6158                    // derive's own unknown-keyword rejection. Either
6159                    // shape is acceptable here — the pin's promise is
6160                    // narrower: "no DialetoEstrangeiro on
6161                    // is_molde_family() == false".
6162                }
6163            }
6164        }
6165    }
6166
6167    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
6168
6169    #[test]
6170    fn limits_round_trip_via_json() {
6171        use crate::LimitsSpec;
6172        use std::time::Duration;
6173        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6174        c.limits = Some(LimitsSpec {
6175            memory: Some(64 * 1024 * 1024),
6176            fuel: Some(1_000_000),
6177            wall_clock: Some(Duration::from_secs(30)),
6178            cpu: Some(500),
6179        });
6180        let json = serde_json::to_string(&c).unwrap();
6181        assert!(json.contains("\"limits\""));
6182        assert!(json.contains("\"64MiB\""));
6183        assert!(json.contains("\"30s\""));
6184        assert!(json.contains("\"500m\""));
6185        let back: Caixa = serde_json::from_str(&json).unwrap();
6186        assert_eq!(c.limits, back.limits);
6187    }
6188
6189    #[test]
6190    fn behavior_round_trip_via_json() {
6191        use crate::BehaviorSpec;
6192        use std::path::PathBuf;
6193        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6194        c.behavior = Some(BehaviorSpec {
6195            on_init: Some(PathBuf::from("lib/init.lisp")),
6196            on_call: Some(PathBuf::from("lib/handlers.lisp")),
6197            ..Default::default()
6198        });
6199        let json = serde_json::to_string(&c).unwrap();
6200        let back: Caixa = serde_json::from_str(&json).unwrap();
6201        assert_eq!(c.behavior, back.behavior);
6202    }
6203
6204    #[test]
6205    fn upgrade_from_round_trip_via_json() {
6206        use crate::{UpgradeFromEntry, UpgradeInstruction};
6207        use std::path::PathBuf;
6208        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6209        c.upgrade_from = vec![UpgradeFromEntry {
6210            from: "0.1.0".into(),
6211            instructions: vec![
6212                UpgradeInstruction::LoadModule {
6213                    module: "demo".into(),
6214                },
6215                UpgradeInstruction::StateChange {
6216                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
6217                },
6218                UpgradeInstruction::SoftPurge {
6219                    module: "demo-old".into(),
6220                },
6221            ],
6222        }];
6223        let json = serde_json::to_string(&c).unwrap();
6224        let back: Caixa = serde_json::from_str(&json).unwrap();
6225        assert_eq!(c.upgrade_from, back.upgrade_from);
6226    }
6227
6228    #[test]
6229    fn supervisor_view_returns_typed_shape() {
6230        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
6231        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
6232        c.kind = CaixaKind::Supervisor;
6233        c.bibliotecas.clear();
6234        c.estrategia = Some(RestartStrategy::OneForOne);
6235        c.max_restarts = Some(5);
6236        c.restart_window = Some("60s".into());
6237        c.children = vec![ChildSpec {
6238            caixa: "worker".into(),
6239            versao: "^0.1".into(),
6240            restart: RestartPolicy::Permanent,
6241        }];
6242        let view = c.supervisor_view().expect("Supervisor kind has a view");
6243        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
6244        assert_eq!(view.max_restarts, 5);
6245        assert_eq!(
6246            view.restart_window,
6247            Some(std::time::Duration::from_secs(60))
6248        );
6249        assert_eq!(view.children.len(), 1);
6250        view.validate().unwrap();
6251    }
6252
6253    #[test]
6254    fn supervisor_view_none_for_non_supervisor_kinds() {
6255        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6256        assert!(c.supervisor_view().is_none());
6257    }
6258
6259    #[test]
6260    fn declared_mesh_slots_empty_for_bare_caixa() {
6261        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6262        assert!(c.declared_mesh_slots().is_empty());
6263    }
6264
6265    #[test]
6266    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
6267        use crate::{Entrada, Membro};
6268        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6269        // Set a non-adjacent pair (:membros + :entrada) to pin that the
6270        // canonical declaration order is preserved regardless of which
6271        // subset is populated.
6272        c.membros = vec![Membro {
6273            caixa: "a".into(),
6274            versao: "^0.1".into(),
6275        }];
6276        c.entrada = Some(Entrada {
6277            host: "x.example.com".into(),
6278            para: "a".into(),
6279            paths: vec![],
6280            port: 8080,
6281        });
6282        assert_eq!(
6283            c.declared_mesh_slots(),
6284            vec![
6285                crate::render::M3_AUTHOR_KEY_MEMBROS,
6286                crate::render::M3_AUTHOR_KEY_ENTRADA,
6287            ]
6288        );
6289    }
6290
6291    #[test]
6292    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6293        // Scalar-value pin: the five author-facing kebab-case labels the
6294        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
6295        // mesh slot axis, one arm per typed slot. Mirrors the peer
6296        // scalar-value pin the sibling
6297        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
6298        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
6299        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
6300        // carry (f49c8b0), so both altitudes of the typed-slot algebra
6301        // (per-Servico M2 + per-Aplicacao M3) share the same
6302        // "one canonical byte-string per arm" discipline. A future
6303        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
6304        // `:politicas` → `:policies`, `:placement` → `:distribution`,
6305        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
6306        // and every consumer that reaches for the label picks it up at
6307        // build time rather than at runtime as a downstream mismatch.
6308        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
6309        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
6310        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
6311        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
6312        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
6313    }
6314
6315    #[test]
6316    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
6317        // Production-through-const pin: the five per-arm labels the
6318        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
6319        // `Vec` route through the lifted
6320        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
6321        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
6322        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
6323        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
6324        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
6325        // declaration order. A future re-order or drift at the tagger
6326        // (a rename that reaches the tagger but not the const, or vice
6327        // versa) surfaces here at build time rather than at runtime as
6328        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
6329        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6330        // commit. Mirror of the peer
6331        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6332        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
6333        // axis.
6334        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
6335        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6336        c.membros = vec![Membro {
6337            caixa: "a".into(),
6338            versao: "^0.1".into(),
6339        }];
6340        c.contratos = vec![WitContract {
6341            de: "a".into(),
6342            para: "a".into(),
6343            wit: "wasi:http/proxy".into(),
6344            endpoint: Some("/x".into()),
6345            subject: None,
6346            slot: None,
6347        }];
6348        c.politicas = Some(MeshPolicy::default());
6349        c.placement = Some(Placement {
6350            estrategia: PlacementStrategy::Replicated,
6351            clusters: vec!["rio".into()],
6352            affinity: None,
6353            shard_key: None,
6354        });
6355        c.entrada = Some(Entrada {
6356            host: "x.example.com".into(),
6357            para: "a".into(),
6358            paths: vec![],
6359            port: 8080,
6360        });
6361        assert_eq!(
6362            c.declared_mesh_slots(),
6363            vec![
6364                crate::render::M3_AUTHOR_KEY_MEMBROS,
6365                crate::render::M3_AUTHOR_KEY_CONTRATOS,
6366                crate::render::M3_AUTHOR_KEY_POLITICAS,
6367                crate::render::M3_AUTHOR_KEY_PLACEMENT,
6368                crate::render::M3_AUTHOR_KEY_ENTRADA,
6369            ]
6370        );
6371    }
6372
6373    #[test]
6374    fn declared_supervisor_slots_empty_for_bare_caixa() {
6375        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6376        assert!(c.declared_supervisor_slots().is_empty());
6377    }
6378
6379    #[test]
6380    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
6381        use crate::RestartStrategy;
6382        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6383        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
6384        // that the canonical declaration order is preserved regardless
6385        // of which subset is populated.
6386        c.estrategia = Some(RestartStrategy::OneForOne);
6387        c.restart_window = Some("60s".into());
6388        assert_eq!(
6389            c.declared_supervisor_slots(),
6390            vec![
6391                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6392                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6393            ]
6394        );
6395    }
6396
6397    #[test]
6398    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6399        // Scalar-value pin: the four author-facing kebab-case labels the
6400        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
6401        // supervision-tree slot axis, one arm per typed slot. Mirrors the
6402        // peer scalar-value pins the sibling
6403        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
6404        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
6405        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
6406        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
6407        // top-level M3 slot consts carry, so all three kind-scoped
6408        // typed-slot-family author-facing-label axes route through one
6409        // canonical per-arm declaration. A future rebrand
6410        // (`:estrategia` → `:strategy` for English uniformity,
6411        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
6412        // `MaxIntensity` name, `:restart-window` → `:period` matching
6413        // OTP's `Period` name, `:children` → `:workers` matching Elixir
6414        // idiom) lands as an edit to exactly one const, and every
6415        // consumer that reaches for the label picks it up at build time
6416        // rather than at runtime as a downstream mismatch.
6417        assert_eq!(
6418            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6419            ":estrategia"
6420        );
6421        assert_eq!(
6422            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6423            ":max-restarts"
6424        );
6425        assert_eq!(
6426            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6427            ":restart-window"
6428        );
6429        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
6430    }
6431
6432    #[test]
6433    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
6434        // Production-through-const pin: the four per-arm labels the
6435        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
6436        // return `Vec` route through the lifted
6437        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
6438        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
6439        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
6440        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
6441        // canonical declaration order. A future re-order or drift at the
6442        // tagger (a rename that reaches the tagger but not the const, or
6443        // vice versa) surfaces here at build time rather than at runtime
6444        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
6445        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6446        // commit. Mirror of the peer
6447        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6448        // (f49c8b0) and
6449        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
6450        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
6451        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
6452        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6453        c.estrategia = Some(RestartStrategy::OneForOne);
6454        c.max_restarts = Some(5);
6455        c.restart_window = Some("60s".into());
6456        c.children = vec![ChildSpec {
6457            caixa: "worker".into(),
6458            versao: "^0.1".into(),
6459            restart: RestartPolicy::Permanent,
6460        }];
6461        assert_eq!(
6462            c.declared_supervisor_slots(),
6463            vec![
6464                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6465                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6466                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6467                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
6468            ]
6469        );
6470    }
6471
6472    #[test]
6473    fn declared_servico_slots_empty_for_bare_caixa() {
6474        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6475        assert!(c.declared_servico_slots().is_empty());
6476    }
6477
6478    #[test]
6479    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
6480        use crate::{UpgradeFromEntry, UpgradeInstruction};
6481        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6482        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
6483        // the canonical declaration order is preserved regardless of
6484        // which subset is populated.
6485        c.limits = Some(crate::LimitsSpec {
6486            fuel: Some(1_000_000),
6487            ..Default::default()
6488        });
6489        c.upgrade_from = vec![UpgradeFromEntry {
6490            from: "0.1.0".into(),
6491            instructions: vec![UpgradeInstruction::Restart],
6492        }];
6493        assert_eq!(
6494            c.declared_servico_slots(),
6495            vec![
6496                crate::render::M2_AUTHOR_KEY_LIMITS,
6497                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6498            ]
6499        );
6500    }
6501
6502    #[test]
6503    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6504        // Scalar-value pin: the three author-facing kebab-case labels
6505        // the `(defcaixa … :<slot> (…))` surface admits on the M2
6506        // top-level slot axis, one arm per typed slot. Mirrors the peer
6507        // scalar-value pin the sibling renderer-side
6508        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
6509        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
6510        // consts carry, so both halves of the M2 top-level slot dual
6511        // axis (author-facing kebab-case label + renderer-side
6512        // camelCase overlay-container wire key) route through one
6513        // canonical per-arm declaration. A future rebrand
6514        // (`:limits` → `:sandbox` matching Lunatic per-process
6515        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
6516        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
6517        // matching Erlang's verbatim appup name) lands as an edit to
6518        // exactly one const, and every consumer that reaches for the
6519        // label picks it up at build time rather than at runtime as a
6520        // downstream mismatch.
6521        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
6522        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
6523        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
6524    }
6525
6526    #[test]
6527    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
6528        // Production-through-const pin: the three per-arm labels the
6529        // [`Caixa::declared_servico_slots`] tagger pushes onto its
6530        // return `Vec` route through the lifted
6531        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
6532        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
6533        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
6534        // declaration order. A future re-order or drift at the tagger
6535        // (a rename that reaches the tagger but not the const, or vice
6536        // versa) surfaces here at build time rather than at runtime as
6537        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
6538        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6539        // commit. Mirror of the peer
6540        // [`crate::behavior::BehaviorSpec::declared_slots`] production
6541        // tagger pin (889dc18) on the sibling per-callback axis.
6542        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
6543        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6544        c.limits = Some(crate::LimitsSpec {
6545            fuel: Some(1_000_000),
6546            ..Default::default()
6547        });
6548        c.behavior = Some(BehaviorSpec {
6549            on_init: Some(PathBuf::from("lib/init.lisp")),
6550            ..Default::default()
6551        });
6552        c.upgrade_from = vec![UpgradeFromEntry {
6553            from: "0.1.0".into(),
6554            instructions: vec![UpgradeInstruction::Restart],
6555        }];
6556        assert_eq!(
6557            c.declared_servico_slots(),
6558            vec![
6559                crate::render::M2_AUTHOR_KEY_LIMITS,
6560                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
6561                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6562            ]
6563        );
6564    }
6565
6566    #[test]
6567    fn existing_manifests_unaffected_by_new_optional_slots() {
6568        // Regression test: a caixa.lisp authored before M2 typed slots
6569        // should still parse + serialize cleanly. The bare `defcaixa`
6570        // emitted by `Caixa::template` has none of the new fields.
6571        let src = Caixa::template("legacy");
6572        let c = Caixa::from_lisp(&src).unwrap();
6573        assert!(c.limits.is_none());
6574        assert!(c.behavior.is_none());
6575        assert!(c.upgrade_from.is_empty());
6576        assert!(c.estrategia.is_none());
6577        assert!(c.children.is_empty());
6578
6579        // And to_lisp emits a manifest with the new slots in the
6580        // empty/default state — round-trippable.
6581        let emitted = c.to_lisp();
6582        let back = Caixa::from_lisp(&emitted).unwrap();
6583        assert_eq!(c, back);
6584    }
6585
6586    #[test]
6587    fn validate_deps_accepts_canonical_caixa() {
6588        // Positive control: the bare template — zero deps, zero
6589        // deps_dev — passes the gate trivially. A future axis added to
6590        // `Dep::validate` mustn't regress an empty-deps caixa to a
6591        // build error.
6592        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6593        c.validate_deps().unwrap();
6594    }
6595
6596    #[test]
6597    fn validate_deps_rejects_invalid_versao_in_deps() {
6598        // Fail-before-pass-after pin: a malformed `:deps :versao`
6599        // surfaces at validate_deps() time, not at lacre-resolve time.
6600        // Mirrors `rejects_invalid_membro_versao_requirement` and
6601        // `validate_rejects_invalid_child_versao_requirement` on the
6602        // other two `:versao` axes.
6603        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6604        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
6605        let err = c.validate_deps().unwrap_err();
6606        assert!(
6607            matches!(
6608                err,
6609                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6610                    if nome == "caixa-teia" && versao == "^bad-version"
6611            ),
6612            "got {err:?}"
6613        );
6614    }
6615
6616    #[test]
6617    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
6618        // Parity pin: `:deps-dev` must run through the same per-entry
6619        // validator as `:deps` — a typo in either axis surfaces the
6620        // same diagnostic. Without this leg, `:deps-dev` would be a
6621        // second-class citizen of the typed surface and an author
6622        // could land a build that passes validate_deps but fails at
6623        // `feira lock`-time when the dev-dep is resolved for a test
6624        // build.
6625        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6626        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
6627        let err = c.validate_deps().unwrap_err();
6628        assert!(
6629            matches!(
6630                err,
6631                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6632                    if nome == "tatara-check" && versao == "^^0.1"
6633            ),
6634            "got {err:?}"
6635        );
6636    }
6637
6638    #[test]
6639    fn validate_deps_runs_deps_before_deps_dev() {
6640        // Order pin: when both lists carry typos, the `:deps`
6641        // diagnostic surfaces first. The author's mental model is
6642        // "runtime deps are load-bearing; dev deps are scaffolding";
6643        // surfacing the runtime axis first matches that hierarchy.
6644        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6645        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
6646        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
6647        let err = c.validate_deps().unwrap_err();
6648        assert!(
6649            matches!(
6650                err,
6651                crate::dep::DepError::VersaoInvalid { ref nome, .. }
6652                    if nome == "runtime-dep"
6653            ),
6654            "expected `:deps` typo to surface first, got {err:?}"
6655        );
6656    }
6657
6658    #[test]
6659    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
6660        // Positive control sweep across both lists. Pin every
6661        // canonical Cargo-shaped form so a future tightening of the
6662        // accepted set surfaces here as a test failure (parity with
6663        // `accepts_canonical_membro_versao_forms` and
6664        // `validate_accepts_canonical_child_versao_forms`).
6665        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6666        c.deps = vec![
6667            Dep::simple("caret", "^0.1"),
6668            Dep::simple("tilde", "~0.1.2"),
6669            Dep::simple("exact", "0.1.0"),
6670            Dep::simple("wildcard", "*"),
6671            Dep::simple("multi-range", ">=0.1, <2"),
6672        ];
6673        c.deps_dev = vec![
6674            Dep::simple("dev-caret", "^0.1"),
6675            Dep::simple("dev-wildcard", "*"),
6676        ];
6677        c.validate_deps().unwrap();
6678    }
6679
6680    #[test]
6681    fn validate_deps_diagnostic_carries_offending_dep() {
6682        // Diagnostic-shape pin: the error names the offending entry's
6683        // `:nome` + `:versao` verbatim and carries a non-empty
6684        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
6685        // run can render the diagnostic without re-parsing.
6686        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6687        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
6688        let err = c.validate_deps().unwrap_err();
6689        let crate::dep::DepError::VersaoInvalid {
6690            nome,
6691            versao,
6692            reason,
6693        } = err
6694        else {
6695            panic!("expected VersaoInvalid, got other variant");
6696        };
6697        assert_eq!(nome, "caixa-teia");
6698        assert_eq!(versao, "not-a-req");
6699        assert!(
6700            !reason.is_empty(),
6701            "VersaoInvalid `reason` must carry the parser's wording verbatim"
6702        );
6703    }
6704
6705    #[test]
6706    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
6707        // Cross-axis pin: `validate_deps` walks both :deps and
6708        // :deps-dev through `Dep::validate`, and the new fonte gate
6709        // (`:tag` + `:branch` both set — the canonical "pin drift"
6710        // footgun) must surface from the :deps-dev arm with the
6711        // offending entry's :nome named. Pin the :deps-dev arm
6712        // explicitly so a future shortcut that only walks :deps
6713        // surfaces here as a regression.
6714        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6715        c.deps_dev = vec![Dep {
6716            nome: "dev-only".into(),
6717            versao: "^0.1".into(),
6718            fonte: Some(crate::DepSource::Git {
6719                repo: "github:p/x".into(),
6720                tag: Some("v1".into()),
6721                rev: None,
6722                branch: Some("main".into()),
6723            }),
6724            opcional: false,
6725            caracteristicas: vec![],
6726        }];
6727        let err = c.validate_deps().unwrap_err();
6728        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
6729            panic!("expected FontePinAmbiguous from :deps-dev walk");
6730        };
6731        assert_eq!(nome, "dev-only");
6732        assert!(pins.contains(":tag") && pins.contains(":branch"));
6733    }
6734
6735    #[test]
6736    fn validate_deps_rejects_empty_repo_in_deps() {
6737        // Parity pin on the :deps arm: an empty :repo on the runtime
6738        // deps list surfaces the same FonteRepoEmpty diagnostic the
6739        // dep.rs per-entry tests pin, naming the offending entry.
6740        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6741        c.deps = vec![Dep {
6742            nome: "runtime".into(),
6743            versao: "^0.1".into(),
6744            fonte: Some(crate::DepSource::Git {
6745                repo: String::new(),
6746                tag: Some("v1".into()),
6747                rev: None,
6748                branch: None,
6749            }),
6750            opcional: false,
6751            caracteristicas: vec![],
6752        }];
6753        let err = c.validate_deps().unwrap_err();
6754        assert!(
6755            matches!(
6756                err,
6757                crate::dep::DepError::FonteRepoEmpty { ref nome }
6758                    if nome == "runtime"
6759            ),
6760            "got {err:?}"
6761        );
6762    }
6763
6764    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
6765
6766    #[test]
6767    fn validate_deps_rejects_duplicate_nome_in_deps() {
6768        // Fail-before-pass-after pin: two `:deps` entries naming the same
6769        // caixa carry two `:versao` / `:fonte` / feature triples that the
6770        // caixa-resolver's lacre pipeline collapses (the second silently
6771        // overwrites the first at `concrete_versao`-resolve time). The
6772        // gate surfaces the duplicate at validate-time, naming the
6773        // offending caixa + the list, before the resolver-side silent
6774        // drop. Mirrors the peer typed-graph duplicate gates
6775        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
6776        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6777        c.deps = vec![
6778            Dep::simple("caixa-teia", "^0.1"),
6779            Dep::simple("caixa-teia", "^0.2"),
6780        ];
6781        let err = c.validate_deps().unwrap_err();
6782        assert!(
6783            matches!(
6784                err,
6785                crate::dep::DepError::DuplicateNome { ref nome, list }
6786                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6787            ),
6788            "got {err:?}"
6789        );
6790    }
6791
6792    #[test]
6793    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
6794        // Parity pin: `:deps-dev` runs through the same per-list
6795        // duplicate check as `:deps` — neither axis is a second-class
6796        // citizen of the set-not-multiset discipline.
6797        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6798        c.deps_dev = vec![
6799            Dep::simple("tatara-check", "*"),
6800            Dep::simple("tatara-check", "^0.1"),
6801        ];
6802        let err = c.validate_deps().unwrap_err();
6803        assert!(
6804            matches!(
6805                err,
6806                crate::dep::DepError::DuplicateNome { ref nome, list }
6807                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
6808            ),
6809            "got {err:?}"
6810        );
6811    }
6812
6813    #[test]
6814    fn validate_deps_accepts_cross_list_same_nome() {
6815        // The Cargo `[dependencies]` + `[dev-dependencies]` override
6816        // convention is preserved: a name appearing in *both* lists is
6817        // valid (the dev-pin overrides at test/dev time). Only
6818        // within-list duplicates are structurally incoherent — pin the
6819        // permissive cross-list semantics so a future shortcut that
6820        // collapses the two seen-sets into one surfaces here as a test
6821        // failure.
6822        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6823        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
6824        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
6825        c.validate_deps().unwrap();
6826    }
6827
6828    #[test]
6829    fn validate_deps_accepts_distinct_nome_in_both_lists() {
6830        // Positive control: distinct names within each list pass — the
6831        // gate's identity element on the canonical authoring shape.
6832        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6833        c.deps = vec![
6834            Dep::simple("caixa-teia", "^0.1"),
6835            Dep::simple("pleme-mesh", "*"),
6836        ];
6837        c.deps_dev = vec![
6838            Dep::simple("tatara-check", "*"),
6839            Dep::simple("dev-shim", "^0.1"),
6840        ];
6841        c.validate_deps().unwrap();
6842    }
6843
6844    #[test]
6845    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
6846        // Diagnostic-precedence pin: a malformed `:versao` on the
6847        // duplicating entry surfaces its narrower `VersaoInvalid`
6848        // diagnostic first, before the cross-entry duplicate gate fires
6849        // — the canonical "per-entry shape before cross-entry uniqueness"
6850        // precedence every peer set-not-multiset gate establishes
6851        // (`*_invalid_fires_before_duplicate_check` pins on
6852        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
6853        // `validate_upgrade_from`).
6854        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6855        c.deps = vec![
6856            Dep::simple("caixa-teia", "^0.1"),
6857            Dep::simple("caixa-teia", "^bad-version"),
6858        ];
6859        let err = c.validate_deps().unwrap_err();
6860        assert!(
6861            matches!(
6862                err,
6863                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6864                    if nome == "caixa-teia" && versao == "^bad-version"
6865            ),
6866            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
6867        );
6868    }
6869
6870    #[test]
6871    fn validate_deps_duplicate_diagnostic_names_first_collision() {
6872        // First-collision determinism pin: with three entries naming the
6873        // same caixa, the first colliding pair surfaces — not the last.
6874        // Mirrors the peer first-collision posture on every
6875        // duplicate-target gate
6876        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
6877        // — the second entry is the first collision; this gate uses the
6878        // same shape: the second entry's `:nome` lands in the diagnostic
6879        // because `seen.insert(first.nome)` already populated the set).
6880        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6881        c.deps = vec![
6882            Dep::simple("caixa-teia", "^0.1"),
6883            Dep::simple("caixa-teia", "^0.2"),
6884            Dep::simple("caixa-teia", "^0.3"),
6885        ];
6886        let err = c.validate_deps().unwrap_err();
6887        // The diagnostic carries the offending caixa name; the
6888        // implementation surfaces on the *second* entry (the first
6889        // collision), so the test pins the `:nome` value.
6890        assert!(
6891            matches!(
6892                err,
6893                crate::dep::DepError::DuplicateNome { ref nome, list }
6894                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6895            ),
6896            "got {err:?}"
6897        );
6898    }
6899
6900    #[test]
6901    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
6902        // Cross-list precedence pin: when both lists carry duplicates,
6903        // the `:deps` diagnostic surfaces first — same author-mental-
6904        // model ordering the `validate_deps_runs_deps_before_deps_dev`
6905        // pin establishes for malformed `:versao` (runtime axis before
6906        // dev axis).
6907        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6908        c.deps = vec![
6909            Dep::simple("runtime-dep", "^0.1"),
6910            Dep::simple("runtime-dep", "^0.2"),
6911        ];
6912        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
6913        let err = c.validate_deps().unwrap_err();
6914        assert!(
6915            matches!(
6916                err,
6917                crate::dep::DepError::DuplicateNome { ref nome, list }
6918                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6919            ),
6920            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
6921        );
6922    }
6923
6924    #[test]
6925    fn validate_deps_empty_lists_pass_duplicate_gate() {
6926        // Empty-set identity pin: the bare template (zero deps, zero
6927        // deps_dev) passes the duplicate gate as the gate's identity
6928        // element. A future tighten that conflates "empty" with
6929        // "missing" would regress this baseline.
6930        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6931        c.validate_deps().unwrap();
6932    }
6933
6934    #[test]
6935    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
6936        // Diagnostic-shape pin: the `list:` field tags which list the
6937        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
6938        // `feira lint` run can route the author to the right block in
6939        // their caixa.lisp without re-deriving the list from context.
6940        // Same self-locating shape every peer per-axis diagnostic
6941        // already exposes.
6942        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6943        c.deps_dev = vec![
6944            Dep::simple("dev-thing", "*"),
6945            Dep::simple("dev-thing", "^0.1"),
6946        ];
6947        let err = c.validate_deps().unwrap_err();
6948        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
6949            panic!("expected DuplicateNome from :deps-dev walk");
6950        };
6951        assert_eq!(nome, "dev-thing");
6952        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
6953    }
6954
6955    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
6956
6957    #[test]
6958    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
6959        // Thread-through pin on `:deps`: the per-entry
6960        // `Dep::validate_caracteristicas` gate fires inside
6961        // `Caixa::validate_deps`'s linear walk, so a malformed feature
6962        // list on any `:deps` entry surfaces as a `DepError` from
6963        // `validate_deps` — the same reachability shape every per-entry
6964        // `Dep::validate` arm threads through. Without this pin a future
6965        // shortcut that skips the per-entry `Dep::validate` call on the
6966        // cross-entry-uniqueness path would mask the within-entry
6967        // `:caracteristicas` gates.
6968        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6969        c.deps = vec![Dep {
6970            nome: "caixa-teia".into(),
6971            versao: "^0.1".into(),
6972            fonte: None,
6973            opcional: false,
6974            caracteristicas: vec!["http".into(), "http".into()],
6975        }];
6976        let err = c.validate_deps().unwrap_err();
6977        let crate::dep::DepError::CaracteristicaDuplicate {
6978            nome,
6979            caracteristica,
6980        } = err
6981        else {
6982            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
6983        };
6984        assert_eq!(nome, "caixa-teia");
6985        assert_eq!(caracteristica, "http");
6986    }
6987
6988    #[test]
6989    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
6990        // Peer thread-through pin on `:deps-dev`: same reachability as
6991        // the `:deps` arm above, on the dev-only authoring axis. Pins
6992        // that the `validate_deps` walk visits both lists' per-entry
6993        // gates uniformly. The empty-feature arm carries here so both
6994        // new `:caracteristicas` arms are surfaced via at least one
6995        // `validate_deps` thread-through.
6996        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6997        c.deps_dev = vec![Dep {
6998            nome: "caixa-teia".into(),
6999            versao: "^0.1".into(),
7000            fonte: None,
7001            opcional: false,
7002            caracteristicas: vec![String::new()],
7003        }];
7004        let err = c.validate_deps().unwrap_err();
7005        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
7006            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
7007        };
7008        assert_eq!(nome, "caixa-teia");
7009    }
7010
7011    #[test]
7012    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
7013        // Thread-through pin on `:deps`: the per-entry
7014        // `Dep::validate_caracteristicas` value-shape gate (lifted via
7015        // `crate::render::is_cargo_feature_name`) fires inside
7016        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
7017        // a structurally invalid feature name on any `:deps` entry
7018        // surfaces as `DepError::CaracteristicaInvalid` from
7019        // `validate_deps` — the same reachability shape every per-entry
7020        // `Dep::validate` arm threads through. Without this pin a
7021        // future shortcut that skips the per-entry `Dep::validate` call
7022        // on the cross-entry-uniqueness path would mask the within-
7023        // entry `:caracteristicas` value-shape gate.
7024        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7025        c.deps = vec![Dep {
7026            nome: "caixa-teia".into(),
7027            versao: "^0.1".into(),
7028            fonte: None,
7029            opcional: false,
7030            caracteristicas: vec!["+http".into()],
7031        }];
7032        let err = c.validate_deps().unwrap_err();
7033        let crate::dep::DepError::CaracteristicaInvalid {
7034            nome,
7035            caracteristica,
7036            ..
7037        } = err
7038        else {
7039            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
7040        };
7041        assert_eq!(nome, "caixa-teia");
7042        assert_eq!(caracteristica, "+http");
7043    }
7044
7045    #[test]
7046    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
7047        // Peer thread-through pin on `:deps-dev`: same reachability as
7048        // the `:deps` arm above, on the dev-only authoring axis. The
7049        // `http/json` shape carries here so the segment-separator
7050        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
7051        // confusion footgun) is surfaced via the cross-entry walk too —
7052        // pinning that the `:deps-dev` list visits the same per-entry
7053        // value-shape gate as the `:deps` list.
7054        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7055        c.deps_dev = vec![Dep {
7056            nome: "caixa-teia".into(),
7057            versao: "^0.1".into(),
7058            fonte: None,
7059            opcional: false,
7060            caracteristicas: vec!["http/json".into()],
7061        }];
7062        let err = c.validate_deps().unwrap_err();
7063        let crate::dep::DepError::CaracteristicaInvalid {
7064            nome,
7065            caracteristica,
7066            ..
7067        } = err
7068        else {
7069            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
7070        };
7071        assert_eq!(nome, "caixa-teia");
7072        assert_eq!(caracteristica, "http/json");
7073    }
7074
7075    #[test]
7076    fn to_lisp_preserves_deps() {
7077        let src = r#"
7078(defcaixa
7079  :nome "x"
7080  :versao "0.1.0"
7081  :kind Biblioteca
7082  :deps ((:nome "a" :versao "^0.1")
7083         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
7084"#;
7085        let c1 = Caixa::from_lisp(src).unwrap();
7086        let emitted = c1.to_lisp();
7087        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
7088        assert_eq!(c1.deps, c2.deps);
7089    }
7090
7091    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
7092
7093    fn caixa_with_nome(nome: &str) -> Caixa {
7094        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
7095        c.nome = nome.to_string();
7096        c
7097    }
7098
7099    #[test]
7100    fn validate_nome_accepts_canonical_template() {
7101        // Positive control: the bare `feira init`-style template's
7102        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
7103        // not regress this baseline shape. A future tightening of the
7104        // accepted set surfaces here as a test failure first.
7105        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7106        c.validate_nome().unwrap();
7107    }
7108
7109    #[test]
7110    fn validate_nome_accepts_canonical_forms() {
7111        // Positive-set sweep: each realistic caixa-name shape the K8s
7112        // apiserver accepts as a `metadata.name` label must pass —
7113        // single-word, hyphen-joined, version-suffixed, single-char,
7114        // two-char, digit-start (DNS-1123 allows this; the stricter
7115        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
7116        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
7117        // the peer member-name axis.
7118        for nome in [
7119            "checkout",
7120            "cart-v2",
7121            "a",
7122            "db",
7123            "3rd-party-shim",
7124            "payment-retry",
7125            "0",
7126        ] {
7127            caixa_with_nome(nome)
7128                .validate_nome()
7129                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
7130        }
7131    }
7132
7133    #[test]
7134    fn validate_nome_rejects_empty() {
7135        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
7136        // an empty `:nome` (the derive macro stores the raw String);
7137        // the gate's empty arm names the offending axis with a narrower
7138        // diagnostic than the `NomeInvalid` parse arm would emit.
7139        let c = caixa_with_nome("");
7140        let err = c.validate_nome().unwrap_err();
7141        assert_eq!(err, ManifestError::NomeEmpty);
7142    }
7143
7144    #[test]
7145    fn validate_nome_rejects_uppercase() {
7146        // The canonical "I copied the TitleCase display name verbatim"
7147        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
7148        // admission on every derived artifact (Helm chart, ComputeUnit,
7149        // CNP, HTTPRoute, label values); the gate moves the diagnostic
7150        // to the source `caixa.lisp` and the reason suggests the
7151        // lowercased fix verbatim.
7152        let c = caixa_with_nome("MyApp");
7153        let err = c.validate_nome().unwrap_err();
7154        let ManifestError::NomeInvalid { nome, reason } = err else {
7155            panic!("expected NomeInvalid for uppercase :nome");
7156        };
7157        assert_eq!(nome, "MyApp");
7158        assert!(
7159            reason.contains("uppercase") && reason.contains("myapp"),
7160            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
7161        );
7162    }
7163
7164    #[test]
7165    fn validate_nome_rejects_underscore() {
7166        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
7167        // `_`; the apiserver rejects on admission across every derived
7168        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
7169        // and `:children :caixa` (31bfa43).
7170        let c = caixa_with_nome("my_app");
7171        let err = c.validate_nome().unwrap_err();
7172        assert!(
7173            matches!(
7174                err,
7175                ManifestError::NomeInvalid { ref nome, ref reason }
7176                    if nome == "my_app" && reason.contains('_')
7177            ),
7178            "got {err:?}"
7179        );
7180    }
7181
7182    #[test]
7183    fn validate_nome_rejects_dot() {
7184        // A `:nome` is a single DNS-1123 label, not a subdomain. The
7185        // "I want to namespace with `.`" footgun the gate redirects to
7186        // `-` via the shared predicate's reason wording.
7187        let c = caixa_with_nome("team.app");
7188        let err = c.validate_nome().unwrap_err();
7189        assert!(
7190            matches!(
7191                err,
7192                ManifestError::NomeInvalid { ref nome, ref reason }
7193                    if nome == "team.app" && reason.contains('.')
7194            ),
7195            "got {err:?}"
7196        );
7197    }
7198
7199    #[test]
7200    fn validate_nome_rejects_leading_hyphen() {
7201        // DNS-1123 boundary rule: the label must start with an ASCII
7202        // alphanumeric. Pin the leading-`-` arm explicitly.
7203        let c = caixa_with_nome("-app");
7204        let err = c.validate_nome().unwrap_err();
7205        assert!(
7206            matches!(
7207                err,
7208                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
7209            ),
7210            "got {err:?}"
7211        );
7212    }
7213
7214    #[test]
7215    fn validate_nome_rejects_trailing_hyphen() {
7216        // Symmetric arm of the boundary rule, pinned separately so a
7217        // future relaxation that only checks the leading position
7218        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
7219        // and `_with_trailing_hyphen` on the supervisor / aplicacao
7220        // axes.
7221        let c = caixa_with_nome("app-");
7222        let err = c.validate_nome().unwrap_err();
7223        assert!(
7224            matches!(
7225                err,
7226                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
7227            ),
7228            "got {err:?}"
7229        );
7230    }
7231
7232    #[test]
7233    fn validate_nome_rejects_unicode() {
7234        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
7235        // bytes are rejected by the K8s apiserver on every name axis.
7236        let c = caixa_with_nome("café");
7237        let err = c.validate_nome().unwrap_err();
7238        assert!(
7239            matches!(
7240                err,
7241                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
7242            ),
7243            "got {err:?}"
7244        );
7245    }
7246
7247    #[test]
7248    fn validate_nome_rejects_whitespace() {
7249        // The paste-from-sketch / paste-from-spec footgun. Internal
7250        // whitespace is rejected by every K8s name axis.
7251        let c = caixa_with_nome("my app");
7252        let err = c.validate_nome().unwrap_err();
7253        assert!(
7254            matches!(
7255                err,
7256                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
7257            ),
7258            "got {err:?}"
7259        );
7260    }
7261
7262    #[test]
7263    fn validate_nome_rejects_too_long() {
7264        // 64-byte boundary pin: the K8s apiserver rejects any
7265        // `metadata.name` over 63 bytes at admission; the diagnostic
7266        // names both the 63-byte cap and the actual length so the
7267        // author can shorten in one edit. Mirrors `_too_long` on the
7268        // peer member-/cluster-/child-name axes.
7269        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
7270        let c = caixa_with_nome(&over);
7271        let err = c.validate_nome().unwrap_err();
7272        let ManifestError::NomeInvalid { nome, reason } = err else {
7273            panic!("expected NomeInvalid for over-cap :nome");
7274        };
7275        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
7276        assert!(
7277            reason.contains("63") && reason.contains("64"),
7278            "diagnostic must name the cap + actual length, got {reason:?}"
7279        );
7280    }
7281
7282    #[test]
7283    fn nome_max_length_validates() {
7284        // The 63-byte cap exactly — the boundary-accepting case pinned
7285        // alongside `validate_nome_rejects_too_long` so a future cap
7286        // shift surfaces both arms simultaneously. Mirrors
7287        // `membro_caixa_max_length_validates`,
7288        // `placement_cluster_max_length_validates`,
7289        // `child_caixa_max_length_validates`.
7290        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
7291        caixa_with_nome(&at_cap).validate_nome().unwrap();
7292    }
7293
7294    #[test]
7295    fn nome_empty_takes_precedence_over_invalid() {
7296        // Order pin: the empty arm fires before the predicate is
7297        // consulted. Empty < invalid in self-locating-ness — the
7298        // narrower `NomeEmpty` diagnostic doesn't carry a useless
7299        // `nome: ""` reference into the parser-shaped reason. Mirrors
7300        // `membro_caixa_empty_takes_precedence_over_invalid` on the
7301        // peer axis (3f9d7a0).
7302        let c = caixa_with_nome("");
7303        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
7304    }
7305
7306    #[test]
7307    fn nome_invalid_diagnostic_carries_offending_nome() {
7308        // Diagnostic-shape pin: the error names the offending `:nome`
7309        // verbatim with a non-empty parser-shaped reason, so a `feira
7310        // lint` run can render the diagnostic without re-parsing.
7311        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
7312        let c = caixa_with_nome("MyApp");
7313        let err = c.validate_nome().unwrap_err();
7314        let ManifestError::NomeInvalid { nome, reason } = err else {
7315            panic!("expected NomeInvalid variant");
7316        };
7317        assert_eq!(nome, "MyApp");
7318        assert!(
7319            !reason.is_empty(),
7320            "NomeInvalid `reason` must carry the predicate's wording verbatim"
7321        );
7322    }
7323
7324    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
7325    //
7326    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
7327    // via DNS-1123; this second-axis gate caps the joint
7328    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
7329    // canonical [`crate::lareira_chart_name`] helper's doc comment
7330    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
7331    // "the M4 admission webhook will pin the joint-length invariant
7332    // when it lands". These tests pin it at the manifest-validate
7333    // layer instead, fail-before-pass-after on the 56-byte boundary.
7334
7335    #[test]
7336    fn validate_nome_chart_name_budget_accepts_canonical_template() {
7337        // Positive control: the bare `feira init`-style template's
7338        // `:nome` ("demo") sits far below the cap; the gate must not
7339        // regress this baseline. Same shape every peer
7340        // value-shape-gate baseline pin uses.
7341        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7342        c.validate_nome_chart_name_budget().unwrap();
7343    }
7344
7345    #[test]
7346    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
7347        // Positive-set sweep across the canonical author surface every
7348        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
7349        // `worker`, the `checkout-aplicacao` example members, the
7350        // `akeyless-attest` caixa-tatara fixture). Every value sits
7351        // far below the 55-byte per-`:nome` budget. Same shape every
7352        // peer per-axis baseline pin uses.
7353        for nome in [
7354            "hello-rio",
7355            "cart",
7356            "checkout",
7357            "worker",
7358            "akeyless-attest",
7359            "demo",
7360            "a",
7361        ] {
7362            caixa_with_nome(nome)
7363                .validate_nome_chart_name_budget()
7364                .unwrap_or_else(|e| {
7365                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
7366                });
7367        }
7368    }
7369
7370    #[test]
7371    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
7372        // Boundary-accepting case at the 55-byte per-`:nome` budget —
7373        // the joint chart name is exactly 63 bytes, the DNS-1123 label
7374        // cap. Pinned alongside the rejecting-arm test so a future cap
7375        // shift surfaces both arms simultaneously. Mirrors
7376        // `nome_max_length_validates` on the peer bare-`:nome` axis.
7377        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
7378        caixa_with_nome(&at_cap)
7379            .validate_nome_chart_name_budget()
7380            .unwrap();
7381    }
7382
7383    #[test]
7384    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
7385        // Fail-before-pass-after pin on the 56-byte boundary: the
7386        // smallest `:nome` length that overflows the joint chart-name
7387        // cap. The inner [`is_dns_1123_label`] gate
7388        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
7389        // this gate it silently passed the manifest-validate cascade
7390        // and surfaced as a `helm lint` / apiserver rejection on the
7391        // rendered chart name far from the source `caixa.lisp`, with
7392        // no field naming the overflow. With this gate the diagnostic
7393        // names the offending `:nome` verbatim alongside the rendered
7394        // chart name and the budget, so the author can shorten in one
7395        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
7396        // bare-`:nome` axis.
7397        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7398        let c = caixa_with_nome(&over);
7399        let err = c.validate_nome_chart_name_budget().unwrap_err();
7400        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7401            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
7402        };
7403        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7404        assert_eq!(nome, over);
7405        assert!(
7406            reason.contains("63") && reason.contains("64") && reason.contains("55"),
7407            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
7408             and the per-`:nome` budget (55), got {reason:?}"
7409        );
7410    }
7411
7412    #[test]
7413    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
7414        // The 63-byte `:nome` boundary — passes the bare-`:nome`
7415        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
7416        // joint chart name that overflows the DNS-1123 label cap
7417        // structurally. The most stringent fail-before-pass-after
7418        // surface: every `:nome` in the 56..=63-byte range passed the
7419        // prior cascade and broke at admission.
7420        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
7421        let c = caixa_with_nome(&bare_max);
7422        // The bare-`:nome` gate accepts the 63-byte length.
7423        c.validate_nome().unwrap();
7424        // The new joint-length gate rejects it.
7425        let err = c.validate_nome_chart_name_budget().unwrap_err();
7426        assert!(
7427            matches!(
7428                err,
7429                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
7430                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
7431            ),
7432            "got {err:?}"
7433        );
7434    }
7435
7436    #[test]
7437    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
7438        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
7439        // name appears verbatim in the diagnostic so the author sees
7440        // exactly the string the apiserver / `helm lint` would have
7441        // rejected — no re-derivation required to grep the source.
7442        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
7443        // on the bare-`:nome` axis.
7444        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
7445        let c = caixa_with_nome(&over);
7446        let err = c.validate_nome_chart_name_budget().unwrap_err();
7447        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7448            panic!("expected NomeChartNameBudgetExceeded variant");
7449        };
7450        assert_eq!(nome, over);
7451        let expected_chart = crate::lareira_chart_name(&over);
7452        assert!(
7453            reason.contains(&expected_chart),
7454            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
7455             got {reason:?}"
7456        );
7457        assert!(
7458            reason.contains("lareira-"),
7459            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
7460        );
7461    }
7462
7463    #[test]
7464    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
7465        // Order pin on the layout cascade: the narrower
7466        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
7467        // joint-length budget. A structurally-malformed `:nome` (here:
7468        // uppercase) surfaces its specific shape error rather than
7469        // the chart-name-budget error, even when the joint length
7470        // would also overflow — the narrower diagnostic is more
7471        // self-locating. Mirrors the cascade-precedence pins peer
7472        // gates already use (e.g. `EntradaParaEmpty` before
7473        // `EntradaParaInvalid`).
7474        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7475        let c = caixa_with_nome(&over);
7476        // The bare-shape gate fires first.
7477        let err = c.validate_nome().unwrap_err();
7478        assert!(
7479            matches!(err, ManifestError::NomeInvalid { .. }),
7480            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
7481        );
7482        // And the layout verify cascade surfaces that diagnostic, not
7483        // the budget arm. Inject a path-exists oracle so the cascade
7484        // gets past the manifest-presence check and into the
7485        // value-shape gates.
7486        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7487        let err = crate::LayoutInvariants::verify(
7488            &layout,
7489            &c,
7490            std::path::Path::new("/tmp/caixa-test-fake-root"),
7491        )
7492        .unwrap_err();
7493        let issue = err.to_string();
7494        assert!(
7495            issue.contains("DNS-1123") || issue.contains("uppercase"),
7496            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
7497             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
7498        );
7499    }
7500
7501    #[test]
7502    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
7503        // Cross-axis envelope pin: the layout cascade wraps both
7504        // bare-`:nome` and joint-length-`:nome` failures through the
7505        // same [`LayoutError::NomeViolation`] envelope, since both
7506        // arms are on the `:nome` axis. The user's diagnostic stays
7507        // self-locating ("which axis"), and a future consumer that
7508        // dispatches on the layout-error variant (e.g. a `feira lint`
7509        // exit-code mapping) sees a single per-axis envelope. The
7510        // wrapped `issue:` carries the full inner diagnostic.
7511        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7512        let c = caixa_with_nome(&over);
7513        // The bare-shape gate accepts.
7514        c.validate_nome().unwrap();
7515        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7516        let err = crate::LayoutInvariants::verify(
7517            &layout,
7518            &c,
7519            std::path::Path::new("/tmp/caixa-test-fake-root"),
7520        )
7521        .unwrap_err();
7522        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
7523            panic!("expected LayoutError::NomeViolation, got {err:?}");
7524        };
7525        assert_eq!(caixa, over);
7526        assert!(
7527            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
7528            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
7529        );
7530    }
7531
7532    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
7533
7534    fn caixa_with_versao(versao: &str) -> Caixa {
7535        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7536        c.versao = versao.to_string();
7537        c
7538    }
7539
7540    #[test]
7541    fn validate_versao_accepts_canonical_template() {
7542        // Positive control: the bare `feira init`-style template's
7543        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
7544        // must not regress this baseline shape. A future tightening of
7545        // the accepted set surfaces here as a test failure first.
7546        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7547        c.validate_versao().unwrap();
7548    }
7549
7550    #[test]
7551    fn validate_versao_accepts_canonical_forms() {
7552        // Positive-set sweep: each realistic SemVer-2 shape the
7553        // substrate's downstream consumers accept must pass — bare
7554        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
7555        // build metadata (`+build.42`), the combined form, and the
7556        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
7557        // the peer `:nome` axis (6c992f8).
7558        for versao in [
7559            "0.1.0",
7560            "0.0.0",
7561            "1.0.0",
7562            "0.2.0-rc.1",
7563            "1.0.0-alpha.0",
7564            "1.0.0+build.42",
7565            "1.0.0-rc.1+build.42",
7566            "10.20.30",
7567        ] {
7568            caixa_with_versao(versao)
7569                .validate_versao()
7570                .unwrap_or_else(|e| {
7571                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
7572                });
7573        }
7574    }
7575
7576    #[test]
7577    fn validate_versao_rejects_empty() {
7578        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
7579        // an empty `:versao` (the derive macro stores the raw String);
7580        // the gate's empty arm names the offending axis with a narrower
7581        // diagnostic than the `VersaoInvalid` parse arm would emit.
7582        // Mirrors `validate_nome_rejects_empty` (6c992f8).
7583        let c = caixa_with_versao("");
7584        let err = c.validate_versao().unwrap_err();
7585        assert_eq!(err, ManifestError::VersaoEmpty);
7586    }
7587
7588    #[test]
7589    fn validate_versao_rejects_git_tag_shape() {
7590        // The canonical "I copied the git tag verbatim" footgun —
7591        // `feira publish` *emits* `v<versao>` git tags, so a leaked
7592        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
7593        // shift every downstream consumer's version axis. `semver`
7594        // rejects the leading `v` at parse time; the gate moves the
7595        // diagnostic to the source `caixa.lisp`.
7596        let c = caixa_with_versao("v0.1.0");
7597        let err = c.validate_versao().unwrap_err();
7598        let ManifestError::VersaoInvalid { versao, reason } = err else {
7599            panic!("expected VersaoInvalid for git-tag-shape :versao");
7600        };
7601        assert_eq!(versao, "v0.1.0");
7602        assert!(
7603            !reason.is_empty(),
7604            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
7605        );
7606    }
7607
7608    #[test]
7609    fn validate_versao_rejects_missing_patch() {
7610        // The canonical "I shortened it" footgun — SemVer-2 requires
7611        // three parts. Cargo's `version =` field accepts the shortened
7612        // form as a requirement, conflating the two leaks across the
7613        // typed `:deps :versao` vs top-level `:versao` axes; the gate
7614        // pins the top-level axis to the strict three-part shape.
7615        let c = caixa_with_versao("0.1");
7616        let err = c.validate_versao().unwrap_err();
7617        assert!(
7618            matches!(
7619                err,
7620                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
7621            ),
7622            "got {err:?}"
7623        );
7624    }
7625
7626    #[test]
7627    fn validate_versao_rejects_requirement_shape() {
7628        // The canonical "I leaked a requirement into a version" footgun —
7629        // the typed `:deps :versao` / `:membros :versao` axes accept
7630        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
7631        // concrete `Version`. Without this gate the two typed surfaces
7632        // would silently overlap, and a top-level `^0.1` would surface
7633        // at `helm install` time as a Chart.yaml version rejection far
7634        // from the source `caixa.lisp`.
7635        let c = caixa_with_versao("^0.1");
7636        let err = c.validate_versao().unwrap_err();
7637        assert!(
7638            matches!(
7639                err,
7640                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
7641            ),
7642            "got {err:?}"
7643        );
7644    }
7645
7646    #[test]
7647    fn validate_versao_rejects_docker_tag_shape() {
7648        // The "I confused it with a docker tag" footgun — `latest`,
7649        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
7650        // SemVer rejects at parse time; the gate moves the diagnostic
7651        // to the source `caixa.lisp`.
7652        for bad in ["latest", "main", "stable"] {
7653            let c = caixa_with_versao(bad);
7654            let err = c.validate_versao().unwrap_err();
7655            assert!(
7656                matches!(
7657                    err,
7658                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
7659                ),
7660                "got {err:?} for {bad:?}"
7661            );
7662        }
7663    }
7664
7665    #[test]
7666    fn validate_versao_rejects_four_part_form() {
7667        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
7668        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
7669        // semver crate rejects the extra `.0` at parse time.
7670        let c = caixa_with_versao("0.1.0.0");
7671        let err = c.validate_versao().unwrap_err();
7672        assert!(
7673            matches!(
7674                err,
7675                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
7676            ),
7677            "got {err:?}"
7678        );
7679    }
7680
7681    #[test]
7682    fn versao_empty_takes_precedence_over_invalid() {
7683        // Order pin: the empty arm fires before the parser is consulted.
7684        // Empty < invalid in self-locating-ness — the narrower
7685        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
7686        // reference into the parser-shaped reason. Mirrors
7687        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
7688        // peer axis.
7689        let c = caixa_with_versao("");
7690        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
7691    }
7692
7693    #[test]
7694    fn versao_invalid_diagnostic_carries_offending_versao() {
7695        // Diagnostic-shape pin: the error names the offending `:versao`
7696        // verbatim with a non-empty parser-shaped reason, so a `feira
7697        // lint` run can render the diagnostic without re-parsing.
7698        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
7699        let c = caixa_with_versao("v0.1.0");
7700        let err = c.validate_versao().unwrap_err();
7701        let ManifestError::VersaoInvalid { versao, reason } = err else {
7702            panic!("expected VersaoInvalid variant");
7703        };
7704        assert_eq!(versao, "v0.1.0");
7705        assert!(
7706            !reason.is_empty(),
7707            "VersaoInvalid `reason` must carry the parser's wording verbatim"
7708        );
7709    }
7710
7711    #[test]
7712    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
7713        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
7714        // for `:upgrade-from :from` must also pass `validate_versao` —
7715        // the two `:versao`-typed surfaces (top-level `:versao`,
7716        // `:upgrade-from :from`) consume the *same* `semver::Version`
7717        // parser, so they must agree on the accepted set. Without this
7718        // pin, a future tightening of one axis could silently diverge
7719        // from the other. Mirrors the `:versao` requirement-axis
7720        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
7721        // commits established.
7722        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
7723            // From the canonical UpgradeFromEntry round-trip fixture
7724            // (`upgrade::tests::round_trip_load_module` peers).
7725            let entry = crate::UpgradeFromEntry {
7726                from: versao.to_string(),
7727                instructions: Vec::new(),
7728            };
7729            entry
7730                .validate()
7731                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
7732            caixa_with_versao(versao)
7733                .validate_versao()
7734                .unwrap_or_else(|e| {
7735                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
7736                });
7737        }
7738    }
7739
7740    // ── Caixa::validate_restart_window — supervisor restart-window
7741    //    folds through the shared `supervisor::duration_codec` ────────
7742
7743    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
7744        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
7745        c.kind = CaixaKind::Supervisor;
7746        c.restart_window = window.map(str::to_string);
7747        c
7748    }
7749
7750    #[test]
7751    fn validate_restart_window_accepts_none() {
7752        // The canonical "omit the slot to express no reset" shape — a
7753        // `None` raw string is the absence of the typed
7754        // `:restart-window` slot, which is exactly the SupervisorSpec
7755        // "never reset" semantics. The gate must be a no-op here; a
7756        // future tightening that rejected `None` would force every
7757        // supervisor caixa to authoring-time pin a window even when
7758        // the OTP semantics call for none.
7759        caixa_with_restart_window(None)
7760            .validate_restart_window()
7761            .unwrap();
7762    }
7763
7764    #[test]
7765    fn validate_restart_window_accepts_canonical_forms() {
7766        // Positive-set sweep across the canonical authoring units the
7767        // shared `supervisor::duration_codec::parse` accepts —
7768        // matches the codec-side `parse_accepts_integer_canonical_units`
7769        // pin in supervisor::tests so a future codec-side tightening
7770        // surfaces simultaneously on both axes.
7771        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
7772            caixa_with_restart_window(Some(window))
7773                .validate_restart_window()
7774                .unwrap_or_else(|e| {
7775                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
7776                });
7777        }
7778    }
7779
7780    #[test]
7781    fn validate_restart_window_rejects_fractional_seconds() {
7782        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
7783        // as f64 to 1.5 → renders back as `"1500ms"` on first
7784        // serialize). Prior to the fold + this gate, the inline
7785        // `parse_window_inline` accepted f64 magnitudes and silently
7786        // produced a `Duration::from_secs_f64(1.5)`, divergent from
7787        // the shared codec's integer-magnitude discipline on the
7788        // serde-routed siblings. The gate now surfaces a self-locating
7789        // diagnostic at the manifest layer.
7790        let err = caixa_with_restart_window(Some("1.5s"))
7791            .validate_restart_window()
7792            .unwrap_err();
7793        let ManifestError::RestartWindowMalformed {
7794            restart_window,
7795            reason,
7796        } = err
7797        else {
7798            panic!("expected RestartWindowMalformed for fractional seconds");
7799        };
7800        assert_eq!(restart_window, "1.5s");
7801        assert!(
7802            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
7803            "diagnostic must carry shared-codec wording, got {reason:?}"
7804        );
7805    }
7806
7807    #[test]
7808    fn validate_restart_window_rejects_decimal_shaped_integer() {
7809        // The `"1.0s"` class — numerically `1s` exactly, but the
7810        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
7811        // gets the same canonical-form diagnostic.
7812        let err = caixa_with_restart_window(Some("1.0s"))
7813            .validate_restart_window()
7814            .unwrap_err();
7815        assert!(
7816            matches!(
7817                err,
7818                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7819                    if restart_window == "1.0s"
7820            ),
7821            "got {err:?}"
7822        );
7823    }
7824
7825    #[test]
7826    fn validate_restart_window_rejects_half_unit_minute() {
7827        // `"0.5m"` is the unit-fraction footgun — author writes a
7828        // human-readable half-minute, the prior inline parser silently
7829        // produced `Duration::from_secs_f64(30.0)` and serde
7830        // re-emitted as `"30s"`, rewriting author intent. The gate
7831        // closes the loop at the manifest layer.
7832        let err = caixa_with_restart_window(Some("0.5m"))
7833            .validate_restart_window()
7834            .unwrap_err();
7835        let ManifestError::RestartWindowMalformed {
7836            restart_window,
7837            reason,
7838        } = err
7839        else {
7840            panic!("expected RestartWindowMalformed");
7841        };
7842        assert_eq!(restart_window, "0.5m");
7843        assert!(
7844            reason.contains("\"30s\""),
7845            "diagnostic must point at the canonical-form remediation, got {reason:?}"
7846        );
7847    }
7848
7849    #[test]
7850    fn validate_restart_window_rejects_leading_sign() {
7851        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
7852        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
7853        // and was caught by the `num < 0.0` arm which silently
7854        // returned `None`, dropping the author-supplied window). The
7855        // shared codec's digit-only gate rejects both with a unified
7856        // canonical-form diagnostic; the manifest-layer wrapper names
7857        // the offending value.
7858        for bad in ["+30s", "-30s"] {
7859            let err = caixa_with_restart_window(Some(bad))
7860                .validate_restart_window()
7861                .unwrap_err();
7862            assert!(
7863                matches!(
7864                    err,
7865                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
7866                        if restart_window == bad
7867                ),
7868                "got {err:?} for {bad:?}"
7869            );
7870        }
7871    }
7872
7873    #[test]
7874    fn validate_restart_window_rejects_unknown_unit() {
7875        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
7876        // unit dispatch surfaces an `unknown duration unit` reason;
7877        // the manifest-layer wrapper names the offending value.
7878        let err = caixa_with_restart_window(Some("30x"))
7879            .validate_restart_window()
7880            .unwrap_err();
7881        let ManifestError::RestartWindowMalformed {
7882            restart_window,
7883            reason,
7884        } = err
7885        else {
7886            panic!("expected RestartWindowMalformed for unknown unit");
7887        };
7888        assert_eq!(restart_window, "30x");
7889        assert!(
7890            reason.contains("unknown duration unit"),
7891            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
7892        );
7893    }
7894
7895    #[test]
7896    fn validate_restart_window_rejects_garbage() {
7897        // Pure non-numeric magnitude (`"abc"`) falls through to the
7898        // shared codec's narrower `"bad duration magnitude"` arm. Same
7899        // diagnostic shape as the codec-side
7900        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
7901        let err = caixa_with_restart_window(Some("abc"))
7902            .validate_restart_window()
7903            .unwrap_err();
7904        let ManifestError::RestartWindowMalformed {
7905            restart_window,
7906            reason,
7907        } = err
7908        else {
7909            panic!("expected RestartWindowMalformed for garbage");
7910        };
7911        assert_eq!(restart_window, "abc");
7912        assert!(
7913            reason.contains("bad duration magnitude"),
7914            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
7915        );
7916    }
7917
7918    #[test]
7919    fn validate_restart_window_rejects_empty_string() {
7920        // The empty-after-trim edge case — distinct from the `None`
7921        // canonical "omit the slot" shape. The shared codec's
7922        // digit-only gate refuses an empty magnitude; the manifest
7923        // layer names the offending `""` so the author can grep for
7924        // the literal empty value in their `caixa.lisp` and either
7925        // remove the slot (the canonical "no reset" shape) or pin a
7926        // positive duration.
7927        let err = caixa_with_restart_window(Some(""))
7928            .validate_restart_window()
7929            .unwrap_err();
7930        assert!(
7931            matches!(
7932                err,
7933                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7934                    if restart_window.is_empty()
7935            ),
7936            "got {err:?}"
7937        );
7938    }
7939
7940    #[test]
7941    fn validate_restart_window_diagnostic_carries_offending_value() {
7942        // Diagnostic-shape pin (peer with
7943        // `nome_invalid_diagnostic_carries_offending_nome` /
7944        // `versao_invalid_diagnostic_carries_offending_versao`): the
7945        // error names the offending raw `:restart-window` verbatim
7946        // with a non-empty shared-codec-shaped reason, so a `feira
7947        // lint` run can render the diagnostic without re-parsing.
7948        let err = caixa_with_restart_window(Some("1.5s"))
7949            .validate_restart_window()
7950            .unwrap_err();
7951        let ManifestError::RestartWindowMalformed {
7952            restart_window,
7953            reason,
7954        } = err
7955        else {
7956            panic!("expected RestartWindowMalformed variant");
7957        };
7958        assert_eq!(restart_window, "1.5s");
7959        assert!(
7960            !reason.is_empty(),
7961            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
7962        );
7963    }
7964
7965    #[test]
7966    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
7967        // Behavioral parity pin after the fold (`parse_window_inline`
7968        // deletion): the canonical `"60s"` still produces
7969        // `Duration::from_secs(60)` on the typed view — the fold is
7970        // semantically equivalent to the prior inline parser on the
7971        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
7972        // pin, narrowed to the parser-side contract.
7973        let c = caixa_with_restart_window(Some("60s"));
7974        let view = c.supervisor_view().expect("Supervisor kind has a view");
7975        assert_eq!(
7976            view.restart_window,
7977            Some(std::time::Duration::from_secs(60))
7978        );
7979    }
7980
7981    #[test]
7982    fn supervisor_view_soft_swallows_what_validate_rejects() {
7983        // Parity pin between the view-construction path and the
7984        // manifest-level validator: the same `"1.5s"` that surfaces
7985        // `RestartWindowMalformed` at `validate_restart_window` time
7986        // becomes `restart_window: None` on the typed view (the fold
7987        // preserves the existing best-effort shape of `supervisor_view`).
7988        // The contract is: a layout-verifier / `feira lint` flow that
7989        // cares about the malformed-window axis MUST consult
7990        // `validate_restart_window` — relying solely on the view's
7991        // `None` swallows the diagnostic silently. This pin makes the
7992        // expectation a typed invariant.
7993        let c = caixa_with_restart_window(Some("1.5s"));
7994        let view = c.supervisor_view().expect("Supervisor kind has a view");
7995        assert_eq!(
7996            view.restart_window, None,
7997            "view-construction path soft-swallows the parse error to None"
7998        );
7999        // And the manifest-level validator does NOT soft-swallow:
8000        assert!(
8001            matches!(
8002                c.validate_restart_window().unwrap_err(),
8003                ManifestError::RestartWindowMalformed { ref restart_window, .. }
8004                    if restart_window == "1.5s"
8005            ),
8006            "validator must surface the offending value",
8007        );
8008    }
8009
8010    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
8011
8012    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
8013        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8014        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
8015        c.exe = exe.into_iter().map(String::from).collect();
8016        c.servicos = servicos.into_iter().map(String::from).collect();
8017        c
8018    }
8019
8020    #[test]
8021    fn validate_code_paths_accepts_canonical_template() {
8022        // The bare `Caixa::template` shape is the gate's identity element
8023        // on the canonical authoring shape — `:bibliotecas
8024        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
8025        // that the gate is non-disruptive against every existing caixa.
8026        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8027        c.validate_code_paths().unwrap();
8028    }
8029
8030    #[test]
8031    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
8032        // Positive control sweep: a canonical-shaped path on every slot
8033        // passes. Mirrors the peer
8034        // `behavior::validate_every_slot_relative_is_ok` pin.
8035        let c = caixa_with_code_paths(
8036            vec!["lib/demo.lisp", "lib/helpers.lisp"],
8037            vec!["exe/demo", "exe/tool"],
8038            vec!["servicos/demo.computeunit.yaml"],
8039        );
8040        c.validate_code_paths().unwrap();
8041    }
8042
8043    #[test]
8044    fn validate_code_paths_accepts_all_empty_lists() {
8045        // The empty-list identity element: every Caixa with no declared
8046        // code paths trivially passes (Supervisor / Aplicacao kinds rely
8047        // on this — the OwnCode gate already rejected them before the
8048        // path-shape gate runs in the layout, but the validator itself
8049        // must accept the empty shape).
8050        let c = caixa_with_code_paths(vec![], vec![], vec![]);
8051        c.validate_code_paths().unwrap();
8052    }
8053
8054    #[test]
8055    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
8056        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
8057        let err = c.validate_code_paths().unwrap_err();
8058        assert!(
8059            matches!(
8060                err,
8061                ManifestError::CodePathEmpty {
8062                    slot: ":bibliotecas"
8063                }
8064            ),
8065            "got {err:?}",
8066        );
8067    }
8068
8069    #[test]
8070    fn validate_code_paths_rejects_empty_exe_entry() {
8071        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
8072        let err = c.validate_code_paths().unwrap_err();
8073        assert!(
8074            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
8075            "got {err:?}",
8076        );
8077    }
8078
8079    #[test]
8080    fn validate_code_paths_rejects_empty_servicos_entry() {
8081        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
8082        let err = c.validate_code_paths().unwrap_err();
8083        assert!(
8084            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
8085            "got {err:?}",
8086        );
8087    }
8088
8089    #[test]
8090    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
8091        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
8092        // so an absolute path that resolves on disk silently passes the
8093        // layout's existence check — the canonical sandbox-escape on
8094        // the biblioteca axis.
8095        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8096        let err = c.validate_code_paths().unwrap_err();
8097        let ManifestError::CodePathAbsolute { slot, path } = err else {
8098            panic!("expected CodePathAbsolute, got {err:?}");
8099        };
8100        assert_eq!(slot, ":bibliotecas");
8101        assert_eq!(path, PathBuf::from("/etc/passwd"));
8102    }
8103
8104    #[test]
8105    fn validate_code_paths_rejects_absolute_exe_entry() {
8106        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
8107        let err = c.validate_code_paths().unwrap_err();
8108        let ManifestError::CodePathAbsolute { slot, path } = err else {
8109            panic!("expected CodePathAbsolute, got {err:?}");
8110        };
8111        assert_eq!(slot, ":exe");
8112        assert_eq!(path, PathBuf::from("/usr/bin/env"));
8113    }
8114
8115    #[test]
8116    fn validate_code_paths_rejects_absolute_servicos_entry() {
8117        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
8118        let err = c.validate_code_paths().unwrap_err();
8119        let ManifestError::CodePathAbsolute { slot, path } = err else {
8120            panic!("expected CodePathAbsolute, got {err:?}");
8121        };
8122        assert_eq!(slot, ":servicos");
8123        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
8124    }
8125
8126    #[test]
8127    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
8128        // Canonical "I want a lib from a sibling caixa" footgun on the
8129        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
8130        // downstream, so a leading `..` traverses to the parent of the
8131        // caixa root with no diagnostic at layout time if the resolved
8132        // target exists.
8133        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
8134        let err = c.validate_code_paths().unwrap_err();
8135        let ManifestError::CodePathParentEscape { slot, path } = err else {
8136            panic!("expected CodePathParentEscape, got {err:?}");
8137        };
8138        assert_eq!(slot, ":bibliotecas");
8139        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
8140    }
8141
8142    #[test]
8143    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
8144        // Mid-path `..` defeats the layout's component-aware
8145        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
8146        // `starts_with(<root>/exe)` is true, but the canonical resolution
8147        // lives outside the caixa root. Caught regardless of where the
8148        // `..` sits — mirrors the peer
8149        // `behavior::validate_rejects_parent_escape_mid_path` pin.
8150        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
8151        let err = c.validate_code_paths().unwrap_err();
8152        let ManifestError::CodePathParentEscape { slot, path } = err else {
8153            panic!("expected CodePathParentEscape, got {err:?}");
8154        };
8155        assert_eq!(slot, ":exe");
8156        assert_eq!(path, PathBuf::from("exe/../../escape"));
8157    }
8158
8159    #[test]
8160    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
8161        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
8162        let err = c.validate_code_paths().unwrap_err();
8163        let ManifestError::CodePathParentEscape { slot, path } = err else {
8164            panic!("expected CodePathParentEscape, got {err:?}");
8165        };
8166        assert_eq!(slot, ":servicos");
8167        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
8168    }
8169
8170    #[test]
8171    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
8172        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
8173        // `:servicos`. A manifest with malformed entries on all three
8174        // surfaces surfaces the `:bibliotecas` defect first, mirroring
8175        // the canonical declaration order
8176        // `Caixa::declared_foreign_code_slots` already establishes for
8177        // the foreign-code-slot diagnostic.
8178        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
8179        let err = c.validate_code_paths().unwrap_err();
8180        assert!(
8181            matches!(
8182                err,
8183                ManifestError::CodePathEmpty {
8184                    slot: ":bibliotecas"
8185                }
8186            ),
8187            "got {err:?}",
8188        );
8189    }
8190
8191    #[test]
8192    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
8193        // Within-slot precedence pin: empty → absolute → parent-escape,
8194        // matching the [`PathShapeViolation`] arm-ordering every peer
8195        // `is_sandboxed_relative_path` caller follows (b0c8389
8196        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
8197        // `:bibliotecas` list whose first entry is empty *and* whose
8198        // later entries are absolute/parent-escape surfaces the empty
8199        // arm first, on the lexicographically-earliest offending entry.
8200        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
8201        let err = c.validate_code_paths().unwrap_err();
8202        assert!(
8203            matches!(
8204                err,
8205                ManifestError::CodePathEmpty {
8206                    slot: ":bibliotecas"
8207                }
8208            ),
8209            "got {err:?}",
8210        );
8211    }
8212
8213    #[test]
8214    fn validate_code_paths_first_offender_per_slot_wins() {
8215        // Within a single slot, the first declaration-order offender
8216        // surfaces — pins that the gate is left-to-right deterministic
8217        // (peer of every `*_first_collision_*` pin on duplicate gates).
8218        let c = caixa_with_code_paths(
8219            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
8220            vec![],
8221            vec![],
8222        );
8223        let err = c.validate_code_paths().unwrap_err();
8224        let ManifestError::CodePathAbsolute { slot, path } = err else {
8225            panic!("expected CodePathAbsolute, got {err:?}");
8226        };
8227        assert_eq!(slot, ":bibliotecas");
8228        assert_eq!(path, PathBuf::from("/etc/escape"));
8229    }
8230
8231    #[test]
8232    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
8233        // Diagnostic-shape pin (peer with
8234        // `nome_invalid_diagnostic_carries_offending_nome` /
8235        // `versao_invalid_diagnostic_carries_offending_versao`): the
8236        // error's Display surfaces both the offending `:slot` tag and
8237        // the offending path verbatim, so a `feira lint` run can render
8238        // the diagnostic without re-parsing.
8239        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8240        let rendered = c.validate_code_paths().unwrap_err().to_string();
8241        assert!(
8242            rendered.contains(":bibliotecas"),
8243            "diagnostic must name the offending slot: {rendered}",
8244        );
8245        assert!(
8246            rendered.contains("/etc/passwd"),
8247            "diagnostic must quote the offending path: {rendered}",
8248        );
8249    }
8250
8251    #[test]
8252    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
8253        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
8254        // axis. Without the gate `feira build` re-parses the same lib
8255        // twice, wasting work and silently masking the author's intent
8256        // to declare a *second* biblioteca.
8257        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
8258        let err = c.validate_code_paths().unwrap_err();
8259        let ManifestError::CodePathDuplicate { slot, path } = err else {
8260            panic!("expected CodePathDuplicate, got {err:?}");
8261        };
8262        assert_eq!(slot, ":bibliotecas");
8263        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
8264    }
8265
8266    #[test]
8267    fn validate_code_paths_rejects_duplicate_exe_entry() {
8268        // Same footgun on the Binario surface. The future `caixa-flake`
8269        // emitter that materializes each `:exe` entry as a flake
8270        // `packages.<name>` derivation would collide on the duplicate
8271        // package key — surfaced here at the typed-validate layer with a
8272        // self-locating diagnostic instead.
8273        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
8274        let err = c.validate_code_paths().unwrap_err();
8275        let ManifestError::CodePathDuplicate { slot, path } = err else {
8276            panic!("expected CodePathDuplicate, got {err:?}");
8277        };
8278        assert_eq!(slot, ":exe");
8279        assert_eq!(path, PathBuf::from("exe/cli"));
8280    }
8281
8282    #[test]
8283    fn validate_code_paths_rejects_duplicate_servicos_entry() {
8284        // Same footgun on the Servico surface. The peer caixa-helm /
8285        // caixa-flux renderers refuse `:servicos.len() != 1` with the
8286        // narrower `UnsupportedServicoCount` diagnostic, but that
8287        // diagnostic surfaces "too many servicos" without naming
8288        // "duplicate entry" — the typed self-locating framing only lands
8289        // at this gate.
8290        let c = caixa_with_code_paths(
8291            vec![],
8292            vec![],
8293            vec![
8294                "servicos/demo.computeunit.yaml",
8295                "servicos/demo.computeunit.yaml",
8296            ],
8297        );
8298        let err = c.validate_code_paths().unwrap_err();
8299        let ManifestError::CodePathDuplicate { slot, path } = err else {
8300            panic!("expected CodePathDuplicate, got {err:?}");
8301        };
8302        assert_eq!(slot, ":servicos");
8303        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
8304    }
8305
8306    #[test]
8307    fn validate_code_paths_accepts_same_path_across_slots() {
8308        // Per-list scope pin: a `:bibliotecas` entry that happens to
8309        // collide with an `:exe` or `:servicos` entry as a *string* is
8310        // not a duplicate by this gate (each list gets its own HashSet),
8311        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
8312        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
8313        // shape on the dep axis). The structural `starts_with(<exe |
8314        // servicos>_dir)` fence at layout time prevents the realistic
8315        // cross-slot collision case from existing on disk, but the gate's
8316        // per-list scope is correct independent of that downstream fence.
8317        let c = caixa_with_code_paths(
8318            vec!["lib/x.lisp"],
8319            vec!["exe/x"],
8320            vec!["servicos/x.computeunit.yaml"],
8321        );
8322        c.validate_code_paths().unwrap();
8323    }
8324
8325    #[test]
8326    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
8327        // Within-slot ordering pin: structural defects (empty / absolute
8328        // / parent-escape) fire before the duplicate gate on the same
8329        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
8330        // surfaces the narrower `CodePathEmpty` for the empty entry
8331        // first, not the duplicate on the later pair — same arm-ordering
8332        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
8333        // `:autores` 86c769b, `:deps` 359fba5).
8334        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
8335        let err = c.validate_code_paths().unwrap_err();
8336        assert!(
8337            matches!(
8338                err,
8339                ManifestError::CodePathEmpty {
8340                    slot: ":bibliotecas"
8341                }
8342            ),
8343            "got {err:?}",
8344        );
8345    }
8346
8347    #[test]
8348    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
8349        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
8350        // duplicates surface before `:exe` duplicates, matching the
8351        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
8352        // order every peer per-slot diagnostic on this surface follows.
8353        let c = caixa_with_code_paths(
8354            vec!["lib/x.lisp", "lib/x.lisp"],
8355            vec!["exe/y", "exe/y"],
8356            vec![],
8357        );
8358        let err = c.validate_code_paths().unwrap_err();
8359        let ManifestError::CodePathDuplicate { slot, path } = err else {
8360            panic!("expected CodePathDuplicate, got {err:?}");
8361        };
8362        assert_eq!(slot, ":bibliotecas");
8363        assert_eq!(path, PathBuf::from("lib/x.lisp"));
8364    }
8365
8366    #[test]
8367    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
8368        // Diagnostic-shape pin (peer with
8369        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8370        // on the structural arm): the duplicate-arm Display surfaces both
8371        // the offending `:slot` tag and the offending path verbatim, so a
8372        // `feira lint` run can render the diagnostic without re-parsing.
8373        let c = caixa_with_code_paths(
8374            vec![],
8375            vec![],
8376            vec![
8377                "servicos/demo.computeunit.yaml",
8378                "servicos/demo.computeunit.yaml",
8379            ],
8380        );
8381        let rendered = c.validate_code_paths().unwrap_err().to_string();
8382        assert!(
8383            rendered.contains(":servicos"),
8384            "diagnostic must name the offending slot: {rendered}",
8385        );
8386        assert!(
8387            rendered.contains("servicos/demo.computeunit.yaml"),
8388            "diagnostic must quote the offending path: {rendered}",
8389        );
8390    }
8391
8392    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
8393    //
8394    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
8395    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
8396    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
8397    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
8398    // at parse time — the same downstream consumer the peer `:behavior
8399    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
8400    // `:upgrade-from :state-change :script` (33cc830,
8401    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
8402    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
8403    // nix-built executable surface (`"exe/<name>"` shape per the canonical
8404    // [`crate::LayoutError::ExeOutsideDir`] error message and every
8405    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
8406    // is the `.computeunit.yaml` ComputeUnit-CR axis.
8407
8408    #[test]
8409    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
8410        // Canonical "I dragged the wrong file from the workspace tree"
8411        // footgun on the biblioteca axis. Without the gate `feira build`
8412        // hands the extensionless path to `tatara_lisp::read` and fails
8413        // with a parser-shaped diagnostic far from the source caixa.lisp,
8414        // with no field naming the offending `:bibliotecas` entry.
8415        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
8416            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8417            let err = c.validate_code_paths().unwrap_err();
8418            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8419                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8420            };
8421            assert_eq!(slot, ":bibliotecas");
8422            assert_eq!(path, PathBuf::from(relpath));
8423        }
8424    }
8425
8426    #[test]
8427    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
8428        // Wrong-extension sweep across common authoring footguns. Same
8429        // sweep posture as the peer
8430        // `behavior::validate_rejects_wrong_extension` (c97815a) and
8431        // `upgrade::tests::state_change_rejects_wrong_extension_script`
8432        // (33cc830) cases.
8433        for relpath in [
8434            "lib/demo.rs",
8435            "lib/demo.txt",
8436            "lib/demo.md",
8437            "lib/demo.json",
8438            "lib/demo.yaml",
8439            "lib/demo.toml",
8440            "lib/demo.lisp.bak",
8441            "lib/demo.lispx",
8442            "lib/demo.lis",
8443        ] {
8444            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8445            let err = c.validate_code_paths().unwrap_err();
8446            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8447                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8448            };
8449            assert_eq!(slot, ":bibliotecas");
8450            assert_eq!(path, PathBuf::from(relpath));
8451        }
8452    }
8453
8454    #[test]
8455    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
8456        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
8457        // contract. An uppercase `.LISP` shape that the layout's existence
8458        // check would (case-insensitively, on case-insensitive volumes)
8459        // match the on-disk file still mismatches the canonical form the
8460        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
8461        // contract. Mirrors the peer
8462        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
8463        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
8464        // (33cc830) sweeps.
8465        for relpath in [
8466            "lib/demo.LISP",
8467            "lib/demo.Lisp",
8468            "lib/demo.LiSp",
8469            "lib/demo.lISP",
8470        ] {
8471            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8472            let err = c.validate_code_paths().unwrap_err();
8473            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8474                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8475            };
8476            assert_eq!(slot, ":bibliotecas");
8477            assert_eq!(path, PathBuf::from(relpath));
8478        }
8479    }
8480
8481    #[test]
8482    fn validate_code_paths_accepts_canonical_lisp_shapes() {
8483        // Positive-control sweep through every canonical authoring shape
8484        // every in-tree fixture and the `Caixa::template` scaffold use.
8485        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
8486        // (c97815a) and the lifted predicate's own
8487        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
8488        // (33cc830).
8489        for relpath in [
8490            "lib/demo.lisp",
8491            "lib/handlers.lisp",
8492            "lib/migrations/v01-to-v02.lisp",
8493            "demo.lisp",
8494            "a.lisp",
8495            "./lib/demo.lisp",
8496            "lib/./handlers.lisp",
8497            "lib/migrations/v.0.1.lisp",
8498        ] {
8499            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8500            c.validate_code_paths()
8501                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8502        }
8503    }
8504
8505    #[test]
8506    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
8507        // The file-type gate is per-slot — only `:bibliotecas` carries the
8508        // tatara-lisp-source contract. An extensionless `:exe` entry
8509        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
8510        // canonical shapes every in-tree fixture uses, and must continue
8511        // to pass validate. Pins that a future tightening that broadens
8512        // the `.lisp` gate to either axis surfaces as a test failure
8513        // rather than as a silent breaking change to existing valid
8514        // manifests.
8515        let c = caixa_with_code_paths(
8516            vec![],
8517            vec!["exe/demo", "exe/tool"],
8518            vec!["servicos/demo.computeunit.yaml"],
8519        );
8520        c.validate_code_paths().unwrap();
8521    }
8522
8523    #[test]
8524    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
8525        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
8526        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
8527        // sandbox-shape diagnostic first (the `.lisp` remediation would
8528        // be misleading when the offending path can never resolve under
8529        // the caixa root anyway). Mirrors the peer
8530        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
8531        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
8532        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
8533        // on `:upgrade-from :state-change :script` (33cc830).
8534        //
8535        // Empty wins (the strictly-smaller-scope structural arm).
8536        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
8537        assert!(
8538            matches!(
8539                c.validate_code_paths().unwrap_err(),
8540                ManifestError::CodePathEmpty {
8541                    slot: ":bibliotecas"
8542                }
8543            ),
8544            "empty must win over non-lisp-extension",
8545        );
8546        // Absolute wins (the path can't resolve under the caixa root).
8547        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8548        let err = c.validate_code_paths().unwrap_err();
8549        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8550            panic!("absolute must win over non-lisp-extension, got {err:?}");
8551        };
8552        assert_eq!(slot, ":bibliotecas");
8553        // ParentEscape wins (the path escapes the caixa root).
8554        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
8555        let err = c.validate_code_paths().unwrap_err();
8556        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8557            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
8558        };
8559        assert_eq!(slot, ":bibliotecas");
8560    }
8561
8562    #[test]
8563    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
8564        // Within-slot precedence pin: the per-entry file-type shape gate
8565        // fires before the cross-entry duplicate gate, so the narrower
8566        // structural defect dominates the uniqueness diagnostic. A
8567        // `("lib/x.txt" "lib/x.txt")` shape surfaces
8568        // `CodePathNonLispExtension` on the first entry rather than
8569        // `CodePathDuplicate` on the pair — same posture every per-entry
8570        // shape-gate-precedes-duplicate cascade follows on this surface
8571        // (the empty / absolute / parent-escape arms already precede the
8572        // duplicate arm; the lifted file-type arm joins that set).
8573        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
8574        let err = c.validate_code_paths().unwrap_err();
8575        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8576            panic!("expected CodePathNonLispExtension, got {err:?}");
8577        };
8578        assert_eq!(slot, ":bibliotecas");
8579        assert_eq!(path, PathBuf::from("lib/x.txt"));
8580    }
8581
8582    #[test]
8583    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
8584        // Diagnostic-shape pin (peer with
8585        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8586        // on the sandbox-shape arms and
8587        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
8588        // on the duplicate arm): the file-type-arm Display surfaces both
8589        // the offending `:slot` tag, the offending path verbatim, and the
8590        // expected `.lisp` extension named in the remediation text, so a
8591        // `feira lint` run can render the diagnostic without re-parsing.
8592        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
8593        let rendered = c.validate_code_paths().unwrap_err().to_string();
8594        assert!(
8595            rendered.contains(":bibliotecas"),
8596            "diagnostic must name the offending slot: {rendered}",
8597        );
8598        assert!(
8599            rendered.contains("lib/demo.rs"),
8600            "diagnostic must quote the offending path: {rendered}",
8601        );
8602        assert!(
8603            rendered.contains(".lisp"),
8604            "diagnostic must name the expected extension: {rendered}",
8605        );
8606    }
8607
8608    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
8609    //
8610    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
8611    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
8612    // contract. The peer caixa-helm / caixa-flux renderers consume each
8613    // `:servicos` entry through `serde_yaml::from_str` as a typed
8614    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
8615    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
8616    // axis `Path::extension` can't express on its own.
8617
8618    #[test]
8619    fn validate_code_paths_rejects_no_extension_servicos_entry() {
8620        // Canonical "I dragged the wrong file from the workspace tree"
8621        // footgun on the Servico axis. Without the gate the peer
8622        // caixa-helm / caixa-flux renderers hand the extensionless path
8623        // to `serde_yaml::from_str` and fail with a parser-shaped
8624        // diagnostic far from the source caixa.lisp, with no field
8625        // naming the offending `:servicos` entry.
8626        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
8627            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8628            let err = c.validate_code_paths().unwrap_err();
8629            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8630                panic!(
8631                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8632                     got {err:?}"
8633                );
8634            };
8635            assert_eq!(slot, ":servicos");
8636            assert_eq!(path, PathBuf::from(relpath));
8637        }
8638    }
8639
8640    #[test]
8641    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
8642        // Wrong-extension sweep across common authoring footguns on the
8643        // Servico axis. Bare `.yaml` is the canonical "I forgot the
8644        // `.computeunit` segment" typo; the off-by-one-segment shapes
8645        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
8646        // bare `Path::extension` view but mismatch the typed compound
8647        // suffix the renderers' `serde_yaml::from_str` consumer demands.
8648        // Same sweep-posture as the peer
8649        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
8650        // (64772a9) on the sibling tatara-lisp-source axis.
8651        for relpath in [
8652            "servicos/demo.yaml",
8653            "servicos/demo.yml",
8654            "servicos/demo.json",
8655            "servicos/demo.toml",
8656            "servicos/demo.txt",
8657            "servicos/demo.computeunit.yaml.bak",
8658            "servicos/demo.computeunit.yam",
8659            "servicos/demo.computeunit",
8660            "servicos/demo-computeunit.yaml",
8661            "servicos/demo_computeunit.yaml",
8662        ] {
8663            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8664            let err = c.validate_code_paths().unwrap_err();
8665            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8666                panic!(
8667                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8668                     got {err:?}"
8669                );
8670            };
8671            assert_eq!(slot, ":servicos");
8672            assert_eq!(path, PathBuf::from(relpath));
8673        }
8674    }
8675
8676    #[test]
8677    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
8678        // Case-sensitivity sweep — pins the strict lowercase
8679        // `.computeunit.yaml` contract. A case-folded shape that the
8680        // layout's existence check would (case-insensitively, on
8681        // case-insensitive volumes) match the on-disk file still
8682        // mismatches the canonical form the codec emits, breaking the
8683        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
8684        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
8685        // (64772a9) sweep on the sibling tatara-lisp-source axis.
8686        for relpath in [
8687            "servicos/demo.ComputeUnit.yaml",
8688            "servicos/demo.COMPUTEUNIT.yaml",
8689            "servicos/demo.computeunit.YAML",
8690            "servicos/demo.computeunit.Yaml",
8691            "servicos/demo.COMPUTEUNIT.YAML",
8692        ] {
8693            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8694            let err = c.validate_code_paths().unwrap_err();
8695            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8696                panic!(
8697                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8698                     got {err:?}"
8699                );
8700            };
8701            assert_eq!(slot, ":servicos");
8702            assert_eq!(path, PathBuf::from(relpath));
8703        }
8704    }
8705
8706    #[test]
8707    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
8708        // Degenerate hidden-file shape: a file name exactly equal to the
8709        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
8710        // the structural "Servico declared with no identity" footgun.
8711        // The substrate identifies each ComputeUnit by the file-stem
8712        // segment that precedes `.computeunit.yaml` (the rendered
8713        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
8714        // the M3 `:contratos` membership lookup), so an empty stem
8715        // leaves the Servico unidentifiable. Pinned at the typed-axis
8716        // level so a future regression that drops the `name.len() >
8717        // SUFFIX.len()` bound at the predicate surfaces here, not
8718        // piecemeal as a `lareira-` chart-name collision at render time.
8719        for relpath in ["servicos/.computeunit.yaml"] {
8720            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8721            let err = c.validate_code_paths().unwrap_err();
8722            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8723                panic!(
8724                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8725                     got {err:?}"
8726                );
8727            };
8728            assert_eq!(slot, ":servicos");
8729            assert_eq!(path, PathBuf::from(relpath));
8730        }
8731    }
8732
8733    #[test]
8734    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
8735        // Positive-control sweep through every canonical authoring shape
8736        // every in-tree fixture and the `Caixa::template` scaffold use.
8737        // Mirrors the peer
8738        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
8739        // and the lifted predicate's own
8740        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
8741        // render.rs.
8742        for relpath in [
8743            "servicos/demo.computeunit.yaml",
8744            "servicos/hello-rio.computeunit.yaml",
8745            "servicos/my-service.computeunit.yaml",
8746            "servicos/a.computeunit.yaml",
8747            "./servicos/demo.computeunit.yaml",
8748            "servicos/./demo.computeunit.yaml",
8749            "servicos/sub/nested.computeunit.yaml",
8750            "servicos/v0.1.computeunit.yaml",
8751        ] {
8752            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8753            c.validate_code_paths()
8754                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8755        }
8756    }
8757
8758    #[test]
8759    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
8760        // The file-type gate is per-slot — only `:servicos` carries the
8761        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
8762        // entry and an extensionless `:exe` entry are the canonical
8763        // shapes every in-tree fixture uses, and must continue to pass
8764        // validate. Peer of
8765        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
8766        // (64772a9) — together pin that the typed
8767        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
8768        // cross-axis leakage in either direction.
8769        let c = caixa_with_code_paths(
8770            vec!["lib/demo.lisp"],
8771            vec!["exe/demo", "exe/tool"],
8772            vec!["servicos/demo.computeunit.yaml"],
8773        );
8774        c.validate_code_paths().unwrap();
8775    }
8776
8777    #[test]
8778    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
8779        // Cross-arm precedence pin: a `:servicos` entry that is *both*
8780        // sandbox-escaping and wrong-extension surfaces the more
8781        // fundamental sandbox-shape diagnostic first (the
8782        // `.computeunit.yaml` remediation would be misleading when the
8783        // offending path can never resolve under the caixa root
8784        // anyway). Mirrors the peer
8785        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
8786        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
8787        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
8788        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
8789        // table establishes.
8790        //
8791        // Empty wins (the strictly-smaller-scope structural arm).
8792        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
8793        assert!(
8794            matches!(
8795                c.validate_code_paths().unwrap_err(),
8796                ManifestError::CodePathEmpty { slot: ":servicos" }
8797            ),
8798            "empty must win over non-computeunit-yaml-extension",
8799        );
8800        // Absolute wins (the path can't resolve under the caixa root).
8801        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
8802        let err = c.validate_code_paths().unwrap_err();
8803        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8804            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
8805        };
8806        assert_eq!(slot, ":servicos");
8807        // ParentEscape wins (the path escapes the caixa root).
8808        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
8809        let err = c.validate_code_paths().unwrap_err();
8810        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8811            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
8812        };
8813        assert_eq!(slot, ":servicos");
8814    }
8815
8816    #[test]
8817    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
8818        // Within-slot precedence pin: the per-entry file-type shape gate
8819        // fires before the cross-entry duplicate gate, so the narrower
8820        // structural defect dominates the uniqueness diagnostic. A
8821        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
8822        // `CodePathNonComputeUnitYamlExtension` on the first entry
8823        // rather than `CodePathDuplicate` on the pair — same posture
8824        // every per-entry shape-gate-precedes-duplicate cascade follows
8825        // on this surface, peer of the 64772a9 `:bibliotecas`
8826        // `("lib/x.txt" "lib/x.txt")` ordering.
8827        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
8828        let err = c.validate_code_paths().unwrap_err();
8829        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8830            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
8831        };
8832        assert_eq!(slot, ":servicos");
8833        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
8834    }
8835
8836    #[test]
8837    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
8838     {
8839        // Diagnostic-shape pin (peer with
8840        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
8841        // on the sibling tatara-lisp-source axis): the file-type-arm
8842        // Display surfaces both the offending `:slot` tag, the
8843        // offending path verbatim, and the expected
8844        // `.computeunit.yaml` compound suffix named in the remediation
8845        // text, so a `feira lint` run can render the diagnostic without
8846        // re-parsing.
8847        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
8848        let rendered = c.validate_code_paths().unwrap_err().to_string();
8849        assert!(
8850            rendered.contains(":servicos"),
8851            "diagnostic must name the offending slot: {rendered}",
8852        );
8853        assert!(
8854            rendered.contains("servicos/demo.yaml"),
8855            "diagnostic must quote the offending path: {rendered}",
8856        );
8857        assert!(
8858            rendered.contains(".computeunit.yaml"),
8859            "diagnostic must name the expected compound suffix: {rendered}",
8860        );
8861    }
8862
8863    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
8864
8865    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
8866        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8867        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
8868        c
8869    }
8870
8871    #[test]
8872    fn validate_etiquetas_accepts_empty_list() {
8873        // The empty-list identity: every caixa with no declared tags
8874        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
8875        // so the gate is non-disruptive against every existing manifest.
8876        let c = caixa_with_etiquetas(vec![]);
8877        c.validate_etiquetas().unwrap();
8878    }
8879
8880    #[test]
8881    fn validate_etiquetas_accepts_canonical_forms() {
8882        // Positive control sweep: a canonical-shaped non-empty distinct
8883        // tag list passes, mirroring the example checkout-aplicacao
8884        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
8885        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
8886        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
8887        c.validate_etiquetas().unwrap();
8888    }
8889
8890    #[test]
8891    fn validate_etiquetas_rejects_empty_entry() {
8892        // Canonical paste-from-blank-doc footgun. Without the gate the
8893        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
8894        // no-op tag indexing nothing in the future caixa-registry.
8895        let c = caixa_with_etiquetas(vec![""]);
8896        let err = c.validate_etiquetas().unwrap_err();
8897        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8898    }
8899
8900    #[test]
8901    fn validate_etiquetas_rejects_duplicate_entry() {
8902        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
8903        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
8904        // collect at chart render — a "second wins / one silently
8905        // disappears" shape divergent from every peer typed-graph set
8906        // gate. The duplicate-arm names the offending tag verbatim.
8907        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8908        let err = c.validate_etiquetas().unwrap_err();
8909        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8910            panic!("expected EtiquetaDuplicate, got {err:?}");
8911        };
8912        assert_eq!(etiqueta, "demo");
8913    }
8914
8915    #[test]
8916    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
8917        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
8918        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
8919        // structural "this entry has no value" defect dominates the
8920        // cross-entry uniqueness diagnostic. Mirrors the peer
8921        // empty-before-duplicate cascades on `:caracteristicas`
8922        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8923        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
8924        // `MembroDuplicate`).
8925        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
8926        let err = c.validate_etiquetas().unwrap_err();
8927        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8928    }
8929
8930    #[test]
8931    fn validate_etiquetas_duplicate_reports_first_collision() {
8932        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8933        // duplicate (the lexicographically-earliest offending position
8934        // — the second `"a"` at index 2 collides with the first `"a"`
8935        // at index 0), not the later `"b"` collision at index 3,
8936        // peer with every other first-collision diagnostic posture on
8937        // this surface (`validate_load_singularity_reports_first_collision`,
8938        // `validate_cleanup_singularity_reports_first_collision`).
8939        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
8940        let err = c.validate_etiquetas().unwrap_err();
8941        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8942            panic!("expected EtiquetaDuplicate, got {err:?}");
8943        };
8944        assert_eq!(etiqueta, "a");
8945    }
8946
8947    #[test]
8948    fn validate_etiquetas_case_sensitive() {
8949        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
8950        // mirroring the peer `:membros :caixa` / `:children :caixa`
8951        // exact-string-match discipline. The shape gate this routine
8952        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
8953        // grammar) accepts mixed case — crates.io's keyword rule is
8954        // "case-insensitive" at the index layer but admits mixed case
8955        // at the entry layer (the canonical Helm chart `keywords:`
8956        // shape is lowercase by convention, but the grammar admits
8957        // uppercase). Case-sensitivity at the duplicate-set layer
8958        // remains structural — two distinct strings are two distinct
8959        // entries.
8960        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
8961        c.validate_etiquetas().unwrap();
8962    }
8963
8964    #[test]
8965    fn validate_etiquetas_diagnostic_carries_offending_tag() {
8966        // Diagnostic-shape pin (peer with
8967        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
8968        // the error's Display surfaces the offending tag verbatim, so a
8969        // `feira lint` run can render the diagnostic without re-parsing
8970        // and the author can grep their caixa.lisp for the offending
8971        // value.
8972        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8973        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8974        assert!(
8975            rendered.contains(":etiquetas"),
8976            "diagnostic must name the offending slot: {rendered}",
8977        );
8978        assert!(
8979            rendered.contains("demo"),
8980            "diagnostic must quote the offending tag: {rendered}",
8981        );
8982    }
8983
8984    #[test]
8985    fn validate_etiquetas_rejects_leading_whitespace_entry() {
8986        // Canonical paste-from-aligned-doc footgun. Without the shape
8987        // gate `" mesh"` silently passed validate and landed as a
8988        // YAML plain-style scalar with leading whitespace in the
8989        // rendered Chart.yaml `keywords:` array — every YAML 1.2
8990        // dumper trims leading whitespace from plain-style scalars,
8991        // so the authored space round-tripped inconsistently back
8992        // through `caixa.lisp`. Mirrors the peer
8993        // `validate_autores_rejects_leading_whitespace_entry`.
8994        let c = caixa_with_etiquetas(vec![" mesh"]);
8995        let err = c.validate_etiquetas().unwrap_err();
8996        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8997            panic!("expected EtiquetaInvalid, got {err:?}");
8998        };
8999        assert_eq!(etiqueta, " mesh");
9000        assert!(reason.contains("whitespace"), "got: {reason}");
9001    }
9002
9003    #[test]
9004    fn validate_etiquetas_rejects_embedded_newline_entry() {
9005        // Canonical paste-from-multiline-doc footgun — the author
9006        // pasted a multi-tag block into one `:etiquetas` entry
9007        // instead of splitting into one entry per tag. Without the
9008        // shape gate `"mesh\nhttp"` silently passed validate and
9009        // landed as a YAML-illegal multi-line scalar in the rendered
9010        // Chart.yaml `keywords:` array.
9011        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
9012        let err = c.validate_etiquetas().unwrap_err();
9013        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9014            panic!("expected EtiquetaInvalid, got {err:?}");
9015        };
9016        assert_eq!(etiqueta, "mesh\nhttp");
9017        assert!(reason.contains("newline"), "got: {reason}");
9018    }
9019
9020    #[test]
9021    fn validate_etiquetas_rejects_embedded_comma_entry() {
9022        // Canonical CSV-list-separator-confusion footgun: the author
9023        // confused the CSV-style separator convention with the
9024        // `:etiquetas` list grammar. Without the shape gate
9025        // `"mesh,http,grpc"` silently passed validate and landed as a
9026        // single malformed search tag in the rendered Chart.yaml
9027        // `keywords:` array — Artifact Hub's keyword index would
9028        // either silently drop the tag or index it as
9029        // `mesh,http,grpc` instead of three separate tags.
9030        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
9031        let err = c.validate_etiquetas().unwrap_err();
9032        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9033            panic!("expected EtiquetaInvalid, got {err:?}");
9034        };
9035        assert_eq!(etiqueta, "mesh,http,grpc");
9036        assert!(reason.contains('`'), "got: {reason}");
9037        assert!(reason.contains(','), "got: {reason}");
9038    }
9039
9040    #[test]
9041    fn validate_etiquetas_rejects_embedded_slash_entry() {
9042        // Canonical path-separator-confusion footgun: the author
9043        // confused namespace-path notation with the keyword grammar.
9044        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
9045        let err = c.validate_etiquetas().unwrap_err();
9046        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9047            panic!("expected EtiquetaInvalid, got {err:?}");
9048        };
9049        assert_eq!(etiqueta, "caixa/servico");
9050        assert!(reason.contains('/'), "got: {reason}");
9051    }
9052
9053    #[test]
9054    fn validate_etiquetas_rejects_leading_digit_entry() {
9055        // Canonical paste-from-numbered-list footgun: the author
9056        // copied `1. mesh` from a numbered doc and the `1` leaked
9057        // into the tag.
9058        let c = caixa_with_etiquetas(vec!["1mesh"]);
9059        let err = c.validate_etiquetas().unwrap_err();
9060        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9061            panic!("expected EtiquetaInvalid, got {err:?}");
9062        };
9063        assert_eq!(etiqueta, "1mesh");
9064        assert!(reason.contains("digit"), "got: {reason}");
9065    }
9066
9067    #[test]
9068    fn validate_etiquetas_rejects_leading_hyphen_entry() {
9069        // Canonical kebab-leak footgun.
9070        let c = caixa_with_etiquetas(vec!["-foo"]);
9071        let err = c.validate_etiquetas().unwrap_err();
9072        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9073            panic!("expected EtiquetaInvalid, got {err:?}");
9074        };
9075        assert_eq!(etiqueta, "-foo");
9076        assert!(reason.contains('-'), "got: {reason}");
9077    }
9078
9079    #[test]
9080    fn validate_etiquetas_rejects_non_ascii_entry() {
9081        // Canonical paste-from-Unicode-doc footgun. Every legitimate
9082        // search tag is strict ASCII; raw non-ASCII silently
9083        // round-trips inconsistently across NFC/NFD normalization on
9084        // APFS / case-folding filesystems and breaks the Artifact Hub
9085        // keyword search index lookup.
9086        let c = caixa_with_etiquetas(vec!["café"]);
9087        let err = c.validate_etiquetas().unwrap_err();
9088        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9089            panic!("expected EtiquetaInvalid, got {err:?}");
9090        };
9091        assert_eq!(etiqueta, "café");
9092        assert!(reason.contains("non-ASCII"), "got: {reason}");
9093    }
9094
9095    #[test]
9096    fn validate_etiquetas_rejects_period_entry() {
9097        // Canonical namespace-confusion / version-suffix footgun
9098        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
9099        // excludes `.` from the continuation set even though the
9100        // sibling `:caracteristicas` axis (Cargo's feature-name
9101        // grammar) admits it. Tighter than the sibling axis, peer
9102        // with Cargo's own crates.io keyword shape.
9103        let c = caixa_with_etiquetas(vec!["http.1"]);
9104        let err = c.validate_etiquetas().unwrap_err();
9105        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9106            panic!("expected EtiquetaInvalid, got {err:?}");
9107        };
9108        assert_eq!(etiqueta, "http.1");
9109        assert!(reason.contains('.'), "got: {reason}");
9110    }
9111
9112    #[test]
9113    fn validate_etiquetas_empty_takes_precedence_over_shape() {
9114        // Per-entry empty-first cascade pin: an entry that is both
9115        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
9116        // narrower "this entry has no value" structural defect
9117        // dominates the broader shape-predicate diagnostic). The
9118        // empty arm fires before the shape predicate is consulted,
9119        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
9120        // cascade established on the sibling universal-axis Vec<String>
9121        // surface.
9122        let c = caixa_with_etiquetas(vec![""]);
9123        let err = c.validate_etiquetas().unwrap_err();
9124        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
9125    }
9126
9127    #[test]
9128    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
9129        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
9130        // entry that is malformed surfaces `EtiquetaInvalid` even when
9131        // a later entry would have collided on duplicate. The
9132        // per-entry shape arm fires inside the same loop iteration as
9133        // the empty arm, before the seen-set insert at end-of-iteration
9134        // — structural per-entry defects dominate the cross-entry
9135        // uniqueness diagnostic. Mirrors the peer
9136        // `validate_autores_shape_takes_precedence_over_duplicate`.
9137        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
9138        let err = c.validate_etiquetas().unwrap_err();
9139        assert!(
9140            matches!(err, ManifestError::EtiquetaInvalid { .. }),
9141            "got {err:?}",
9142        );
9143    }
9144
9145    #[test]
9146    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
9147        // Diagnostic-shape pin on the new shape arm (peer with
9148        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
9149        // the rendered Display surfaces both the offending slot name
9150        // and the offending value verbatim, so a `feira lint` run
9151        // points the author at the exact `:etiquetas` entry to fix.
9152        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
9153        let rendered = c.validate_etiquetas().unwrap_err().to_string();
9154        assert!(
9155            rendered.contains(":etiquetas"),
9156            "diagnostic must name the offending slot: {rendered}",
9157        );
9158        assert!(
9159            rendered.contains("mesh\\nhttp"),
9160            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9161        );
9162    }
9163
9164    #[test]
9165    fn validate_etiquetas_rejects_at_21_byte_boundary() {
9166        // The 20-byte cap pin — boundary-exceeding case rejected,
9167        // boundary-accepting case passes. Mirrors the peer
9168        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
9169        // side pin, surfaced at the per-axis caller so the cap
9170        // propagates through validate end-to-end. Constructed as a
9171        // single all-`a` token so only the cap arm fires.
9172        let max_ok = "a".repeat(20);
9173        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
9174        c.validate_etiquetas().unwrap();
9175        let too_long = "a".repeat(21);
9176        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
9177        let err = c.validate_etiquetas().unwrap_err();
9178        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
9179            panic!("expected EtiquetaInvalid, got {err:?}");
9180        };
9181        assert!(reason.contains("20"), "got: {reason}");
9182        assert!(reason.contains("21"), "got: {reason}");
9183    }
9184
9185    #[test]
9186    fn validate_etiquetas_accepts_canonical_shaped_forms() {
9187        // Positive control sweep: every canonical-shaped tag from the
9188        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
9189        // example fixtures plus the substrate-fixed tags caixa-helm
9190        // unions in at chart render. Drift between this list and the
9191        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
9192        // sweep surfaces here — one source of truth for the rule.
9193        let c = caixa_with_etiquetas(vec![
9194            "example",
9195            "aplicacao",
9196            "mesh",
9197            "ecommerce",
9198            "demo",
9199            "infrastructure",
9200            "aws",
9201            "akeyless",
9202            "pangea-native",
9203            "hello-world",
9204            "wasm",
9205            "rust",
9206            "tatara-lisp",
9207            "caixa-servico",
9208            "lareira",
9209        ]);
9210        c.validate_etiquetas().unwrap();
9211    }
9212
9213    // ── validate_autores — universal-axis maintainer shape ────────────
9214
9215    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
9216        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9217        c.autores = autores.into_iter().map(String::from).collect();
9218        c
9219    }
9220
9221    #[test]
9222    fn validate_autores_accepts_empty_list() {
9223        // The empty-list identity: `Caixa::template` emits `:autores ()`,
9224        // so the gate is non-disruptive against every existing manifest.
9225        let c = caixa_with_autores(vec![]);
9226        c.validate_autores().unwrap();
9227    }
9228
9229    #[test]
9230    fn validate_autores_accepts_canonical_forms() {
9231        // Positive control sweep: every canonical-shaped non-empty
9232        // distinct maintainer list passes — the hello-rio / checkout-
9233        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
9234        // multi-author shape downstream packaging surfaces emit.
9235        let c = caixa_with_autores(vec!["pleme-io"]);
9236        c.validate_autores().unwrap();
9237        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
9238        c.validate_autores().unwrap();
9239    }
9240
9241    #[test]
9242    fn validate_autores_rejects_empty_entry() {
9243        // Canonical paste-from-blank-doc footgun. Without the gate the
9244        // empty entry rendered as `maintainers: [{name: "", email: null}]`
9245        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
9246        // to.
9247        let c = caixa_with_autores(vec![""]);
9248        let err = c.validate_autores().unwrap_err();
9249        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9250    }
9251
9252    #[test]
9253    fn validate_autores_rejects_duplicate_entry() {
9254        // Canonical copy-paste-the-wrong-author footgun. Unlike the
9255        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
9256        // dedups the rendered `keywords:` array), the `maintainers:`
9257        // rendering has *no* dedup — duplicates stack verbatim. The
9258        // duplicate-arm names the offending author verbatim.
9259        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
9260        let err = c.validate_autores().unwrap_err();
9261        let ManifestError::AutorDuplicate { autor } = err else {
9262            panic!("expected AutorDuplicate, got {err:?}");
9263        };
9264        assert_eq!(autor, "pleme-io");
9265    }
9266
9267    #[test]
9268    fn validate_autores_empty_takes_precedence_over_duplicate() {
9269        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
9270        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
9271        // "this entry has no value" defect dominates the cross-entry
9272        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
9273        // cascades on `:etiquetas` (`EtiquetaEmpty` before
9274        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
9275        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
9276        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
9277        // `MembroDuplicate`).
9278        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
9279        let err = c.validate_autores().unwrap_err();
9280        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9281    }
9282
9283    #[test]
9284    fn validate_autores_duplicate_reports_first_collision() {
9285        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
9286        // duplicate (the lexicographically-earliest offending position
9287        // — the second `"a"` at index 2 collides with the first `"a"`
9288        // at index 0), not the later `"b"` collision at index 3,
9289        // peer with every other first-collision diagnostic posture on
9290        // this surface.
9291        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
9292        let err = c.validate_autores().unwrap_err();
9293        let ManifestError::AutorDuplicate { autor } = err else {
9294            panic!("expected AutorDuplicate, got {err:?}");
9295        };
9296        assert_eq!(autor, "a");
9297    }
9298
9299    #[test]
9300    fn validate_autores_case_sensitive() {
9301        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
9302        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
9303        // / `:children :caixa` exact-string-match discipline.
9304        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
9305        c.validate_autores().unwrap();
9306    }
9307
9308    #[test]
9309    fn validate_autores_diagnostic_carries_offending_author() {
9310        // Diagnostic-shape pin (peer with
9311        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
9312        // error's Display surfaces the offending author verbatim, so a
9313        // `feira lint` run can render the diagnostic without re-parsing
9314        // and the author can grep their caixa.lisp for the offending
9315        // value.
9316        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
9317        let rendered = c.validate_autores().unwrap_err().to_string();
9318        assert!(
9319            rendered.contains(":autores"),
9320            "diagnostic must name the offending slot: {rendered}",
9321        );
9322        assert!(
9323            rendered.contains("pleme-io"),
9324            "diagnostic must quote the offending author: {rendered}",
9325        );
9326    }
9327
9328    #[test]
9329    fn validate_autores_rejects_leading_whitespace_entry() {
9330        // Canonical paste-from-aligned-doc footgun. Without the shape
9331        // gate `" pleme-io"` silently passed validate and landed as a
9332        // YAML plain-style scalar with leading whitespace in the
9333        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
9334        // dumper trims leading whitespace from plain-style scalars, so
9335        // the authored space round-tripped inconsistently back through
9336        // `caixa.lisp`. Mirrors the peer
9337        // `validate_descricao_rejects_leading_whitespace`.
9338        let c = caixa_with_autores(vec![" pleme-io"]);
9339        let err = c.validate_autores().unwrap_err();
9340        let ManifestError::AutorInvalid { autor, reason } = err else {
9341            panic!("expected AutorInvalid, got {err:?}");
9342        };
9343        assert_eq!(autor, " pleme-io");
9344        assert!(reason.contains("whitespace"), "got: {reason}");
9345    }
9346
9347    #[test]
9348    fn validate_autores_rejects_trailing_whitespace_entry() {
9349        // Canonical paste-from-doc footgun.
9350        let c = caixa_with_autores(vec!["pleme-io "]);
9351        let err = c.validate_autores().unwrap_err();
9352        let ManifestError::AutorInvalid { autor, reason } = err else {
9353            panic!("expected AutorInvalid, got {err:?}");
9354        };
9355        assert_eq!(autor, "pleme-io ");
9356        assert!(reason.contains("whitespace"), "got: {reason}");
9357    }
9358
9359    #[test]
9360    fn validate_autores_rejects_embedded_newline_entry() {
9361        // Canonical paste-from-multiline-doc footgun — the author
9362        // pasted a multi-line block of author records into one
9363        // `:autores` entry instead of splitting into one entry per
9364        // author. Without the shape gate `"alice\nbob"` silently
9365        // passed validate and landed as a YAML-illegal multi-line
9366        // scalar in the rendered Chart.yaml `maintainers:` array.
9367        let c = caixa_with_autores(vec!["alice\nbob"]);
9368        let err = c.validate_autores().unwrap_err();
9369        let ManifestError::AutorInvalid { autor, reason } = err else {
9370            panic!("expected AutorInvalid, got {err:?}");
9371        };
9372        assert_eq!(autor, "alice\nbob");
9373        assert!(reason.contains("newline"), "got: {reason}");
9374    }
9375
9376    #[test]
9377    fn validate_autores_rejects_embedded_carriage_return_entry() {
9378        // Canonical paste-from-Windows-CRLF-doc footgun.
9379        let c = caixa_with_autores(vec!["alice\rbob"]);
9380        let err = c.validate_autores().unwrap_err();
9381        let ManifestError::AutorInvalid { autor, reason } = err else {
9382            panic!("expected AutorInvalid, got {err:?}");
9383        };
9384        assert_eq!(autor, "alice\rbob");
9385        assert!(reason.contains("carriage return"), "got: {reason}");
9386    }
9387
9388    #[test]
9389    fn validate_autores_rejects_embedded_tab_entry() {
9390        // Canonical tab-from-aligned-doc footgun.
9391        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
9392        let err = c.validate_autores().unwrap_err();
9393        let ManifestError::AutorInvalid { autor, reason } = err else {
9394            panic!("expected AutorInvalid, got {err:?}");
9395        };
9396        assert_eq!(autor, "Pleme\tContributors");
9397        assert!(reason.contains("tab"), "got: {reason}");
9398    }
9399
9400    #[test]
9401    fn validate_autores_rejects_embedded_control_bytes_entry() {
9402        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
9403        // surface the same control-byte arm.
9404        for entry in [
9405            "alice\x00bob",
9406            "alice\x07bob",
9407            "alice\x1bbob",
9408            "alice\x7fbob",
9409        ] {
9410            let c = caixa_with_autores(vec![entry]);
9411            let err = c.validate_autores().unwrap_err();
9412            let ManifestError::AutorInvalid { autor, reason } = err else {
9413                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
9414            };
9415            assert_eq!(autor, entry);
9416            assert!(
9417                reason.contains("control character"),
9418                "{entry:?} reason: {reason}",
9419            );
9420        }
9421    }
9422
9423    #[test]
9424    fn validate_autores_accepts_unicode_entry() {
9425        // Unicode positive control: realistic maintainer names carry
9426        // Unicode (`François`, `日本語`, `naïve`). The predicate must
9427        // round-trip Unicode losslessly, peer with the
9428        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
9429        // sweep.
9430        let c = caixa_with_autores(vec![
9431            "François Dupont",
9432            "日本語の名前",
9433            "naïve <naive@example.com>",
9434        ]);
9435        c.validate_autores().unwrap();
9436    }
9437
9438    #[test]
9439    fn validate_autores_empty_takes_precedence_over_shape() {
9440        // Per-entry empty-first cascade pin: an entry that is both
9441        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
9442        // "this entry has no value" structural defect dominates the
9443        // broader shape-predicate diagnostic). The empty arm fires
9444        // before the shape predicate is consulted, mirroring the peer
9445        // `validate_repositorio_empty_takes_precedence_over_shape`
9446        // cascade on the universal `Option<String>` siblings — and now
9447        // established on the Vec<String> per-entry surface.
9448        let c = caixa_with_autores(vec![""]);
9449        let err = c.validate_autores().unwrap_err();
9450        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9451    }
9452
9453    #[test]
9454    fn validate_autores_shape_takes_precedence_over_duplicate() {
9455        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
9456        // entry that is malformed surfaces `AutorInvalid` even when a
9457        // later entry would have collided on duplicate. The per-entry
9458        // shape arm fires inside the same loop iteration as the empty
9459        // arm, before the seen-set insert at end-of-iteration —
9460        // structural per-entry defects dominate the cross-entry
9461        // uniqueness diagnostic.
9462        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
9463        let err = c.validate_autores().unwrap_err();
9464        assert!(
9465            matches!(err, ManifestError::AutorInvalid { .. }),
9466            "got {err:?}",
9467        );
9468    }
9469
9470    #[test]
9471    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
9472        // Diagnostic-shape pin on the new shape arm (peer with
9473        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
9474        // the rendered Display surfaces both the offending slot name
9475        // and the offending value verbatim, so a `feira lint` run
9476        // points the author at the exact `:autores` entry to fix.
9477        let c = caixa_with_autores(vec!["alice\nbob"]);
9478        let rendered = c.validate_autores().unwrap_err().to_string();
9479        assert!(
9480            rendered.contains(":autores"),
9481            "diagnostic must name the offending slot: {rendered}",
9482        );
9483        assert!(
9484            rendered.contains("alice\\nbob"),
9485            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9486        );
9487    }
9488
9489    #[test]
9490    fn validate_autores_rejects_at_129_byte_boundary() {
9491        // The 128-byte cap pin — boundary-exceeding case rejected,
9492        // boundary-accepting case passes. Mirrors the peer
9493        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
9494        // substrate-side pin, surfaced at the per-axis caller so the
9495        // cap propagates through validate end-to-end. Constructed as
9496        // a single all-`a` token so only the cap arm fires.
9497        let max_ok = "a".repeat(128);
9498        let c = caixa_with_autores(vec![max_ok.as_str()]);
9499        c.validate_autores().unwrap();
9500        let too_long = "a".repeat(129);
9501        let c = caixa_with_autores(vec![too_long.as_str()]);
9502        let err = c.validate_autores().unwrap_err();
9503        let ManifestError::AutorInvalid { reason, .. } = err else {
9504            panic!("expected AutorInvalid, got {err:?}");
9505        };
9506        assert!(reason.contains("128"), "got: {reason}");
9507        assert!(reason.contains("129"), "got: {reason}");
9508    }
9509
9510    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
9511
9512    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
9513        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9514        c.repositorio = repositorio.map(String::from);
9515        c
9516    }
9517
9518    #[test]
9519    fn validate_repositorio_accepts_none() {
9520        // The omit-the-slot identity: `:repositorio` is optional. The
9521        // gate is a no-op when the author didn't declare a value —
9522        // every caixa without a `:repositorio` line trivially passes,
9523        // and the substrate-side renderers fall back to their
9524        // documented placeholder (`caixa-helm`'s `home: None`,
9525        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
9526        // URL). Mirrors the peer `validate_restart_window_accepts_none`
9527        // posture on the other `Option<String>` Caixa slot.
9528        let c = caixa_with_repositorio(None);
9529        c.validate_repositorio().unwrap();
9530    }
9531
9532    #[test]
9533    fn validate_repositorio_accepts_canonical_forms() {
9534        // Positive control sweep across every documented `:repositorio`
9535        // authoring shape — the same union the shared
9536        // `crate::render::is_git_repo_url` predicate accepts and the
9537        // peer `:deps :fonte :repo` axis already routes through.
9538        // Covers the `github:` shorthand (the canonical pleme-io
9539        // convention used in the `:repositorio` field of every
9540        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
9541        // `examples/`), the `https://…` URL the README quickstart uses,
9542        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
9543        // `file://` URL schemes the shared predicate documents.
9544        for repo in [
9545            "github:pleme-io/hello-rio",
9546            "github:pleme-io/checkout",
9547            "https://github.com/pleme-io/hello-rio",
9548            "ssh://git@github.com/pleme-io/hello-rio.git",
9549            "git://github.com/pleme-io/hello-rio.git",
9550            "git@github.com:pleme-io/hello-rio.git",
9551            "file:///srv/pleme/hello-rio",
9552        ] {
9553            let c = caixa_with_repositorio(Some(repo));
9554            c.validate_repositorio()
9555                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
9556        }
9557    }
9558
9559    #[test]
9560    fn validate_repositorio_rejects_empty_some() {
9561        // Canonical paste-from-blank-doc footgun. The narrower
9562        // [`ManifestError::RepositorioEmpty`] arm fires before the
9563        // shape predicate is consulted, mirroring the empty-first
9564        // cascade every peer per-axis identity gate uses
9565        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9566        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
9567        // the empty `Some("")` silently passed the renderer's
9568        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9569        // on `None`) and landed as `home: ""` in `Chart.yaml` /
9570        // `url: ""` in the FluxCD `GitRepository`.
9571        let c = caixa_with_repositorio(Some(""));
9572        let err = c.validate_repositorio().unwrap_err();
9573        assert!(
9574            matches!(err, ManifestError::RepositorioEmpty),
9575            "got {err:?}",
9576        );
9577    }
9578
9579    #[test]
9580    fn validate_repositorio_rejects_whitespace() {
9581        // Paste-from-doc whitespace footgun. The shared
9582        // `is_git_repo_url` predicate refuses any whitespace byte; a
9583        // trailing space in a `:repositorio` value silently broke
9584        // `git clone '<value> '` at clone time. The diagnostic names
9585        // the offending value verbatim.
9586        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
9587        let err = c.validate_repositorio().unwrap_err();
9588        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
9589            panic!("expected RepositorioInvalid, got {err:?}");
9590        };
9591        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
9592    }
9593
9594    #[test]
9595    fn validate_repositorio_rejects_control_char() {
9596        // Paste-from-multiline-doc CRLF footgun — control characters
9597        // at the URL boundary are a class of subprocess-arg injection
9598        // and break git's URL parser at every porcelain entry point.
9599        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
9600        let err = c.validate_repositorio().unwrap_err();
9601        assert!(
9602            matches!(err, ManifestError::RepositorioInvalid { .. }),
9603            "got {err:?}",
9604        );
9605    }
9606
9607    #[test]
9608    fn validate_repositorio_rejects_leading_dash() {
9609        // Canonical CLI-argument-injection footgun: `git clone <repo>`
9610        // interprets a leading `-` as a CLI flag, so a
9611        // `-upload-pack=…` value escapes the subprocess argument
9612        // boundary. The shared predicate refuses every leading-`-`
9613        // shape at validate time.
9614        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
9615        let err = c.validate_repositorio().unwrap_err();
9616        assert!(
9617            matches!(err, ManifestError::RepositorioInvalid { .. }),
9618            "got {err:?}",
9619        );
9620    }
9621
9622    #[test]
9623    fn validate_repositorio_rejects_missing_colon_separator() {
9624        // The bare `org/repo` ambiguity footgun — `git clone` reads
9625        // a no-`:` form as a relative filesystem path rather than the
9626        // GitHub-shorthand expansion the author probably intended.
9627        // The shared predicate refuses every shape without a `:`
9628        // separator.
9629        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9630        let err = c.validate_repositorio().unwrap_err();
9631        assert!(
9632            matches!(err, ManifestError::RepositorioInvalid { .. }),
9633            "got {err:?}",
9634        );
9635    }
9636
9637    #[test]
9638    fn validate_repositorio_rejects_fragment_anchor() {
9639        // Paste-from-browser-address-bar footgun on the
9640        // `:repositorio` axis — an author copies a GitHub permalink
9641        // to a README section / line-permalink and forgets to trim
9642        // the `#fragment` tail. The shared `is_git_repo_url`
9643        // predicate refuses the byte at the URL-grammar layer
9644        // (libcurl strips the fragment before opening the
9645        // transport, so the byte rides verbatim into the rendered
9646        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
9647        // fields but is silently dropped on the wire — two
9648        // manifest variants whose values differ only in their
9649        // fragment anchor lock to two distinct rendered artifacts
9650        // for the byte-identical clone, defeating the THEORY.md
9651        // §V.2 render-determinism contract on the `:repositorio`
9652        // axis the peer `:fonte :repo` axis already closes).
9653        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
9654        let err = c.validate_repositorio().unwrap_err();
9655        let ManifestError::RepositorioInvalid {
9656            repositorio,
9657            reason,
9658        } = err
9659        else {
9660            panic!("expected RepositorioInvalid, got {err:?}");
9661        };
9662        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
9663        assert!(
9664            reason.contains("must not contain `#`"),
9665            "reason must surface the fragment-`#` arm, got {reason:?}"
9666        );
9667    }
9668
9669    #[test]
9670    fn validate_repositorio_rejects_query_string() {
9671        // Paste-from-browser-address-bar footgun on the
9672        // `:repositorio` axis (peer with the a68f818 fragment-`#`
9673        // arm on the same axis). An author copies a GitHub tab
9674        // deep-link out of the address bar and forgets to trim
9675        // the `?tab=…` query tail. The shared `is_git_repo_url`
9676        // predicate refuses the byte at the URL-grammar layer
9677        // (GitHub / GitLab / Bitbucket silently ignore the
9678        // `?query` tail and serve the same repo regardless, so
9679        // the byte rides verbatim into the rendered `Chart.yaml`
9680        // `home:` and FluxCD `GitRepository` `url:` fields but
9681        // is silently masked at the wire — two manifest variants
9682        // whose values differ only in their query tail lock to
9683        // two distinct rendered artifacts for the byte-identical
9684        // clone, defeating the THEORY.md §V.2 render-determinism
9685        // contract on the `:repositorio` axis the peer `:fonte
9686        // :repo` axis already closes).
9687        let c = caixa_with_repositorio(Some(
9688            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
9689        ));
9690        let err = c.validate_repositorio().unwrap_err();
9691        let ManifestError::RepositorioInvalid {
9692            repositorio,
9693            reason,
9694        } = err
9695        else {
9696            panic!("expected RepositorioInvalid, got {err:?}");
9697        };
9698        assert_eq!(
9699            repositorio,
9700            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
9701        );
9702        assert!(
9703            reason.contains("must not contain `?`"),
9704            "reason must surface the query-`?` arm, got {reason:?}"
9705        );
9706    }
9707
9708    #[test]
9709    fn validate_repositorio_rejects_embedded_backslash() {
9710        // Windows-file-path-confusion footgun on the `:repositorio`
9711        // axis (peer with the prior fragment-`#` / query-`?` arms on
9712        // the same axis, and peer with the new dep-level `:fonte :repo`
9713        // backslash arm on the URL-grammar trajectory). An author
9714        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
9715        // hello-rio` into the `:repositorio` slot, expecting the
9716        // `lareira-<nome>` chart's `home:` field and the FluxCD
9717        // `GitRepository` `url:` field to render the canonical local
9718        // file-URI. The shared `is_git_repo_url` predicate refuses
9719        // the byte at the URL-grammar layer (libcurl silently
9720        // translates `\` → `/` on some platforms and refuses it on
9721        // others, so the byte rides verbatim into the rendered
9722        // artifacts but is silently rewritten or rejected at the wire
9723        // — two manifest variants whose values differ only in
9724        // backslash-vs-forward-slash lock to two distinct rendered
9725        // artifacts for the byte-identical clone, defeating the
9726        // THEORY.md §V.2 render-determinism contract on the
9727        // `:repositorio` axis the peer `:fonte :repo` axis already
9728        // closes).
9729        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
9730        let err = c.validate_repositorio().unwrap_err();
9731        let ManifestError::RepositorioInvalid {
9732            repositorio,
9733            reason,
9734        } = err
9735        else {
9736            panic!("expected RepositorioInvalid, got {err:?}");
9737        };
9738        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
9739        assert!(
9740            reason.contains("must not contain `\\`"),
9741            "reason must surface the backslash-`\\` arm, got {reason:?}"
9742        );
9743    }
9744
9745    #[test]
9746    fn validate_repositorio_rejects_uri_template_placeholder() {
9747        // URI Template (RFC 6570) placeholder footgun on the
9748        // `:repositorio` axis (peer with the prior fragment-`#` /
9749        // query-`?` / backslash-`\` arms on the same axis, and peer
9750        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
9751        // URL-grammar trajectory). An author pastes a quick-start
9752        // README snippet / OpenAPI `servers:` URL / Helm chart
9753        // `home:` template carrying unresolved `{org}` / `{repo}`
9754        // placeholders into the `:repositorio` slot, expecting the
9755        // substrate to resolve the placeholder downstream. The
9756        // shared `is_git_repo_url` predicate refuses the byte at the
9757        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
9758        // `%7B` / `%7D` on the wire, so the byte round-trips
9759        // inconsistently between the rendered `Chart.yaml home:` /
9760        // FluxCD `GitRepository url:` and the resolver's `git clone`
9761        // invocation, defeating the THEORY.md §V.2 render-
9762        // determinism contract on the `:repositorio` axis the peer
9763        // `:fonte :repo` axis already closes; every git porcelain
9764        // entry-point additionally fetches a nonexistent literal-
9765        // `{placeholder}`-named path far from the source caixa.lisp).
9766        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
9767        let err = c.validate_repositorio().unwrap_err();
9768        let ManifestError::RepositorioInvalid {
9769            repositorio,
9770            reason,
9771        } = err
9772        else {
9773            panic!("expected RepositorioInvalid, got {err:?}");
9774        };
9775        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
9776        assert!(
9777            reason.contains("must not contain `{`"),
9778            "reason must surface the open-brace `{{` arm, got {reason:?}"
9779        );
9780        assert!(
9781            reason.contains("URI Template") || reason.contains("RFC 6570"),
9782            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
9783        );
9784    }
9785
9786    #[test]
9787    fn validate_repositorio_empty_takes_precedence_over_shape() {
9788        // Empty-first cascade pin: the empty `Some("")` surfaces the
9789        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
9790        // `RepositorioInvalid`, mirroring the peer
9791        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9792        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
9793        // `is_git_repo_url` predicate also rejects the empty input
9794        // (defensively, with its own `"must not be empty"` reason),
9795        // but the manifest-layer empty arm runs first to surface the
9796        // narrower diagnostic verbatim.
9797        let c = caixa_with_repositorio(Some(""));
9798        let err = c.validate_repositorio().unwrap_err();
9799        assert!(
9800            matches!(err, ManifestError::RepositorioEmpty),
9801            "got {err:?}",
9802        );
9803    }
9804
9805    #[test]
9806    fn validate_repositorio_diagnostic_carries_offending_value() {
9807        // Diagnostic-shape pin (peer with
9808        // `validate_autores_diagnostic_carries_offending_author`): the
9809        // error's Display surfaces the offending value + slot name
9810        // verbatim, so a `feira lint` run can render the diagnostic
9811        // without re-parsing and the author can grep their caixa.lisp
9812        // for the offending `:repositorio` value.
9813        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9814        let rendered = c.validate_repositorio().unwrap_err().to_string();
9815        assert!(
9816            rendered.contains(":repositorio"),
9817            "diagnostic must name the offending slot: {rendered}",
9818        );
9819        assert!(
9820            rendered.contains("pleme-io/hello-rio"),
9821            "diagnostic must quote the offending value: {rendered}",
9822        );
9823    }
9824
9825    // ── validate_descricao — universal-axis Chart.yaml description shape ──
9826
9827    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
9828        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9829        c.descricao = descricao.map(String::from);
9830        c
9831    }
9832
9833    #[test]
9834    fn validate_descricao_accepts_none() {
9835        // The omit-the-slot identity: `:descricao` is optional. The
9836        // gate is a no-op when the author didn't declare a value —
9837        // every caixa without a `:descricao` line trivially passes,
9838        // and the substrate-side renderers fall back to their
9839        // documented `caixa.nome`-derived placeholder. Mirrors the
9840        // peer `validate_repositorio_accepts_none` posture on the
9841        // sibling `Option<String>` Caixa slot.
9842        let c = caixa_with_descricao(None);
9843        c.validate_descricao().unwrap();
9844    }
9845
9846    #[test]
9847    fn validate_descricao_accepts_canonical_summary() {
9848        // Positive control: the canonical pleme-io descricao shape —
9849        // a short free-form prose summary — passes the gate. Covers
9850        // the fixture shapes the `caixa-helm` / `caixa-flux` /
9851        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
9852        // wasip2 caixa Servico."`, `"Checkout flow."`).
9853        for desc in [
9854            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9855            "Checkout flow.",
9856            "AWS provider caixa for tatara-lisp",
9857            "FIXME — describe this caixa",
9858            "x",
9859        ] {
9860            let c = caixa_with_descricao(Some(desc));
9861            c.validate_descricao()
9862                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
9863        }
9864    }
9865
9866    #[test]
9867    fn validate_descricao_rejects_empty_some() {
9868        // Canonical paste-from-blank-doc footgun. Without this gate
9869        // the empty `Some("")` silently passed the renderer's
9870        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9871        // on `None`) and landed as `description: ""` in `Chart.yaml`
9872        // and a blank `README.md` header. Mirrors the peer
9873        // [`ManifestError::RepositorioEmpty`] empty-arm on the
9874        // sibling `Option<String>` Caixa slot.
9875        let c = caixa_with_descricao(Some(""));
9876        let err = c.validate_descricao().unwrap_err();
9877        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9878    }
9879
9880    #[test]
9881    fn validate_descricao_rejects_leading_whitespace() {
9882        // Paste-from-aligned-doc footgun: a leading ASCII space the
9883        // bare empty-arm gate accepted, the shape predicate now
9884        // refuses. The diagnostic carries the offending value
9885        // verbatim (with the leading space preserved) so the author
9886        // can grep their caixa.lisp for the exact `:descricao` line
9887        // and fix the round-trip-inconsistent leading whitespace.
9888        // Mirrors the peer
9889        // `validate_licenca_rejects_leading_whitespace` arm on the
9890        // sibling `:licenca` axis.
9891        let c = caixa_with_descricao(Some(" Checkout flow."));
9892        let err = c.validate_descricao().unwrap_err();
9893        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9894            panic!("expected DescricaoInvalid, got {err:?}");
9895        };
9896        assert_eq!(descricao, " Checkout flow.");
9897        assert!(reason.contains("whitespace"), "got: {reason:?}");
9898    }
9899
9900    #[test]
9901    fn validate_descricao_rejects_trailing_whitespace() {
9902        // Paste-from-doc footgun: a trailing ASCII space the bare
9903        // empty-arm gate accepted, the shape predicate now refuses.
9904        let c = caixa_with_descricao(Some("Checkout flow. "));
9905        let err = c.validate_descricao().unwrap_err();
9906        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9907            panic!("expected DescricaoInvalid, got {err:?}");
9908        };
9909        assert_eq!(descricao, "Checkout flow. ");
9910        assert!(reason.contains("whitespace"), "got: {reason:?}");
9911    }
9912
9913    #[test]
9914    fn validate_descricao_rejects_embedded_newline() {
9915        // Paste-from-multiline-doc footgun: an embedded LF the bare
9916        // empty-arm gate accepted, the shape predicate now refuses.
9917        // Without this gate the embedded newline silently landed in
9918        // the rendered Chart.yaml as a multi-line YAML block scalar,
9919        // and every chart-aware UI (`helm list`, `helm search`,
9920        // Artifact Hub) renders the description in a single-line
9921        // column so the embedded newline is silently dropped at
9922        // every downstream consumer.
9923        let c = caixa_with_descricao(Some("Checkout\nflow."));
9924        let err = c.validate_descricao().unwrap_err();
9925        assert!(
9926            matches!(err, ManifestError::DescricaoInvalid { .. }),
9927            "got {err:?}",
9928        );
9929        assert!(err.to_string().contains("newline"), "got {err}");
9930    }
9931
9932    #[test]
9933    fn validate_descricao_rejects_embedded_carriage_return() {
9934        // Paste-from-Windows-CRLF-doc footgun.
9935        let c = caixa_with_descricao(Some("Checkout\rflow."));
9936        let err = c.validate_descricao().unwrap_err();
9937        assert!(
9938            matches!(err, ManifestError::DescricaoInvalid { .. }),
9939            "got {err:?}",
9940        );
9941        assert!(err.to_string().contains("carriage return"), "got {err}");
9942    }
9943
9944    #[test]
9945    fn validate_descricao_rejects_embedded_tab() {
9946        // Tab-from-aligned-doc footgun.
9947        let c = caixa_with_descricao(Some("Checkout\tflow."));
9948        let err = c.validate_descricao().unwrap_err();
9949        assert!(
9950            matches!(err, ManifestError::DescricaoInvalid { .. }),
9951            "got {err:?}",
9952        );
9953        assert!(err.to_string().contains("tab"), "got {err}");
9954    }
9955
9956    #[test]
9957    fn validate_descricao_rejects_embedded_control_bytes() {
9958        // Paste-from-binary-blob footgun: every other control byte
9959        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
9960        // the peer SPDX-expression control-byte arm.
9961        for s in [
9962            "Checkout\x00flow.",
9963            "Checkout\x07flow.",
9964            "Checkout\x1bflow.",
9965            "Checkout\x7fflow.",
9966        ] {
9967            let c = caixa_with_descricao(Some(s));
9968            let err = c.validate_descricao().unwrap_err();
9969            assert!(
9970                matches!(err, ManifestError::DescricaoInvalid { .. }),
9971                "{s:?} got {err:?}",
9972            );
9973            assert!(
9974                err.to_string().contains("control character"),
9975                "{s:?} got {err}",
9976            );
9977        }
9978    }
9979
9980    #[test]
9981    fn validate_descricao_accepts_unicode_prose() {
9982        // Positive control: Unicode prose is accepted — the
9983        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
9984        // and `Caixa::template`'s `"FIXME — describe this caixa"`
9985        // scaffold every `feira init` emits must continue to pass.
9986        for s in [
9987            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9988            "FIXME — describe this caixa",
9989            "Caixa pour le projet tâche",
9990            "日本語の説明",
9991        ] {
9992            let c = caixa_with_descricao(Some(s));
9993            c.validate_descricao()
9994                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
9995        }
9996    }
9997
9998    #[test]
9999    fn validate_descricao_empty_takes_precedence_over_shape() {
10000        // Cascade pin: a `Some("")` surfaces the narrower
10001        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
10002        // shape-predicate arm. Mirrors the peer
10003        // `validate_licenca_empty_takes_precedence_over_shape` pin
10004        // on the sibling `:licenca` axis.
10005        let c = caixa_with_descricao(Some(""));
10006        let err = c.validate_descricao().unwrap_err();
10007        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
10008    }
10009
10010    #[test]
10011    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
10012        // Diagnostic-shape pin: the error's Display surfaces both
10013        // the `:descricao` slot name and the offending value
10014        // verbatim, so a `feira lint` run can render the diagnostic
10015        // without re-parsing and the author can grep their caixa.lisp
10016        // for the offending `:descricao` line. Mirrors the peer
10017        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
10018        // pin (ee2e888) on the sibling `:licenca` axis.
10019        // The `{descricao:?}` Debug format escapes embedded control
10020        // bytes; the quoted offending value surfaces as
10021        // `"Checkout\nflow."` (literal backslash-n) in the rendered
10022        // diagnostic. The author can grep their caixa.lisp for the
10023        // literal `Checkout` summary prefix.
10024        let c = caixa_with_descricao(Some("Checkout\nflow."));
10025        let rendered = c.validate_descricao().unwrap_err().to_string();
10026        assert!(
10027            rendered.contains(":descricao"),
10028            "diagnostic must name the offending slot: {rendered}",
10029        );
10030        assert!(
10031            rendered.contains("Checkout\\nflow."),
10032            "diagnostic must quote the offending value (debug-escaped): {rendered}",
10033        );
10034    }
10035
10036    #[test]
10037    fn validate_descricao_template_passes() {
10038        // Round-trip pin: the bare `Caixa::template` shape carries
10039        // `:descricao "FIXME — describe this caixa"` (a non-empty
10040        // sentinel), so the template-derived Caixa passes the gate by
10041        // construction. A future template-shape change that omits or
10042        // empties `:descricao` would surface here as a regression.
10043        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10044        c.validate_descricao().unwrap();
10045    }
10046
10047    #[test]
10048    fn validate_descricao_diagnostic_names_offending_slot() {
10049        // Diagnostic-shape pin (peer with
10050        // `validate_repositorio_diagnostic_carries_offending_value`):
10051        // the error's Display surfaces the `:descricao` slot name
10052        // verbatim, so a `feira lint` run can render the diagnostic
10053        // without re-parsing and the author can grep their caixa.lisp
10054        // for the offending `:descricao` line.
10055        let c = caixa_with_descricao(Some(""));
10056        let rendered = c.validate_descricao().unwrap_err().to_string();
10057        assert!(
10058            rendered.contains(":descricao"),
10059            "diagnostic must name the offending slot: {rendered}",
10060        );
10061    }
10062
10063    // ── validate_licenca — universal-axis chart README license shape ──
10064
10065    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
10066        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10067        c.licenca = licenca.map(String::from);
10068        c
10069    }
10070
10071    #[test]
10072    fn validate_licenca_accepts_none() {
10073        // The omit-the-slot identity: `:licenca` is optional. The
10074        // gate is a no-op when the author didn't declare a value —
10075        // every caixa without a `:licenca` line trivially passes,
10076        // and the substrate-side `caixa-helm` renderer falls back to
10077        // the documented `"MIT"` placeholder. Mirrors the peer
10078        // `validate_descricao_accepts_none` posture on the sibling
10079        // `Option<String>` Caixa slot.
10080        let c = caixa_with_licenca(None);
10081        c.validate_licenca().unwrap();
10082    }
10083
10084    #[test]
10085    fn validate_licenca_accepts_canonical_expressions() {
10086        // Positive control: every canonical SPDX expression shape
10087        // pleme-io carries in its existing fixtures + the canonical
10088        // SPDX dual-license / with-exception / `+`-suffix / grouped /
10089        // user-defined-reference shapes all pass the gate. Covers
10090        // the single-license, `OR`-compound, `AND`-compound,
10091        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
10092        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
10093        // production the SPDX 2.1 expression grammar admits that
10094        // sits within the alphabet floor the
10095        // `is_spdx_expression_shape` predicate enforces.
10096        for lic in [
10097            "MIT",
10098            "Apache-2.0",
10099            "Apache-2.0 OR MIT",
10100            "Apache-2.0 AND MIT",
10101            "BSD-3-Clause",
10102            "MPL-2.0",
10103            "GPL-3.0-or-later",
10104            "GPL-2.0+",
10105            "Apache-2.0 WITH LLVM-exception",
10106            "(MIT OR Apache-2.0) AND BSD-3-Clause",
10107            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
10108            "LicenseRef-MyLicense",
10109            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
10110            "x",
10111        ] {
10112            let c = caixa_with_licenca(Some(lic));
10113            c.validate_licenca()
10114                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
10115        }
10116    }
10117
10118    #[test]
10119    fn validate_licenca_rejects_trailing_whitespace() {
10120        // Paste-from-doc whitespace footgun. A trailing space in the
10121        // `:licenca` value would silently break a downstream SPDX
10122        // parser that splits on exact `AND` / `OR` / `WITH` keyword
10123        // boundaries. The shape predicate refuses every trailing
10124        // whitespace byte by construction. Peer with
10125        // `validate_repositorio_rejects_whitespace` and
10126        // `validate_edicao_rejects_trailing_whitespace`.
10127        let c = caixa_with_licenca(Some("MIT "));
10128        let err = c.validate_licenca().unwrap_err();
10129        let ManifestError::LicencaInvalid { licenca, .. } = err else {
10130            panic!("expected LicencaInvalid, got {err:?}");
10131        };
10132        assert_eq!(licenca, "MIT ");
10133    }
10134
10135    #[test]
10136    fn validate_licenca_rejects_leading_whitespace() {
10137        // Symmetric paste-from-doc whitespace footgun on the leading
10138        // boundary — the gate refuses every shape that starts with a
10139        // space byte by construction. Peer with
10140        // `validate_edicao_rejects_leading_whitespace`.
10141        let c = caixa_with_licenca(Some(" MIT"));
10142        let err = c.validate_licenca().unwrap_err();
10143        assert!(
10144            matches!(err, ManifestError::LicencaInvalid { .. }),
10145            "got {err:?}",
10146        );
10147    }
10148
10149    #[test]
10150    fn validate_licenca_rejects_control_char() {
10151        // Paste-from-multiline-doc CRLF footgun — control characters
10152        // at the value boundary land as a malformed line in the
10153        // rendered chart `README.md` `## License` section. Peer with
10154        // `validate_repositorio_rejects_control_char` and
10155        // `validate_edicao_rejects_control_char`.
10156        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
10157            let c = caixa_with_licenca(Some(lic));
10158            let err = c.validate_licenca().unwrap_err();
10159            assert!(
10160                matches!(err, ManifestError::LicencaInvalid { .. }),
10161                "expected LicencaInvalid on {lic:?}, got {err:?}",
10162            );
10163        }
10164    }
10165
10166    #[test]
10167    fn validate_licenca_rejects_tab() {
10168        // Tab-from-aligned-doc footgun — SPDX expressions use a
10169        // single ASCII space between tokens; a tab breaks every
10170        // downstream SPDX parser that splits on exact `" "`
10171        // boundaries.
10172        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
10173        let err = c.validate_licenca().unwrap_err();
10174        assert!(
10175            matches!(err, ManifestError::LicencaInvalid { .. }),
10176            "got {err:?}",
10177        );
10178    }
10179
10180    #[test]
10181    fn validate_licenca_rejects_non_ascii() {
10182        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
10183        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
10184        // ".")` production. The shape predicate refuses every
10185        // non-ASCII byte by construction; peer with
10186        // `validate_edicao_rejects_non_ascii_lookalike`.
10187        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
10188            let c = caixa_with_licenca(Some(lic));
10189            let err = c.validate_licenca().unwrap_err();
10190            assert!(
10191                matches!(err, ManifestError::LicencaInvalid { .. }),
10192                "expected LicencaInvalid on {lic:?}, got {err:?}",
10193            );
10194        }
10195    }
10196
10197    #[test]
10198    fn validate_licenca_rejects_underscore() {
10199        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
10200        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
10201        // snake-case identifier conventions that don't apply to the
10202        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
10203        // "-" / "."`). The shape predicate refuses every underscore
10204        // byte by construction.
10205        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
10206            let c = caixa_with_licenca(Some(lic));
10207            let err = c.validate_licenca().unwrap_err();
10208            assert!(
10209                matches!(err, ManifestError::LicencaInvalid { .. }),
10210                "expected LicencaInvalid on {lic:?}, got {err:?}",
10211            );
10212        }
10213    }
10214
10215    #[test]
10216    fn validate_licenca_rejects_comma_separator() {
10217        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
10218        // SPDX expressions compose multiple licenses via `AND` / `OR`
10219        // keywords, not the comma separator. The shape predicate
10220        // refuses every comma byte by construction.
10221        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
10222            let c = caixa_with_licenca(Some(lic));
10223            let err = c.validate_licenca().unwrap_err();
10224            assert!(
10225                matches!(err, ManifestError::LicencaInvalid { .. }),
10226                "expected LicencaInvalid on {lic:?}, got {err:?}",
10227            );
10228        }
10229    }
10230
10231    #[test]
10232    fn validate_licenca_rejects_slash_dual_license() {
10233        // Slash-dual-license colloquial idiom footgun — the
10234        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
10235        // `package.license` field but non-SPDX; the SPDX equivalent
10236        // is `MIT OR Apache-2.0`. The shape predicate refuses every
10237        // forward-slash byte by construction.
10238        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
10239            let c = caixa_with_licenca(Some(lic));
10240            let err = c.validate_licenca().unwrap_err();
10241            assert!(
10242                matches!(err, ManifestError::LicencaInvalid { .. }),
10243                "expected LicencaInvalid on {lic:?}, got {err:?}",
10244            );
10245        }
10246    }
10247
10248    #[test]
10249    fn validate_licenca_rejects_semicolon_separator() {
10250        // Semicolon-list-separator confusion footgun — adjacent to
10251        // the comma-separator idiom, every list-separator-belongs-
10252        // to-list-grammar confusion lands here.
10253        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
10254        let err = c.validate_licenca().unwrap_err();
10255        assert!(
10256            matches!(err, ManifestError::LicencaInvalid { .. }),
10257            "got {err:?}",
10258        );
10259    }
10260
10261    #[test]
10262    fn validate_licenca_empty_takes_precedence_over_shape() {
10263        // Empty-first cascade pin: the empty `Some("")` surfaces the
10264        // narrower `LicencaEmpty` not the shape-predicate-wrapped
10265        // `LicencaInvalid`, mirroring the peer
10266        // `validate_edicao_empty_takes_precedence_over_shape` and
10267        // `validate_repositorio_empty_takes_precedence_over_shape`
10268        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
10269        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
10270        // The shape predicate also refuses the empty input
10271        // (defensively — `"must not be empty"`), but the manifest-
10272        // layer empty arm runs first to surface the narrower
10273        // diagnostic verbatim.
10274        let c = caixa_with_licenca(Some(""));
10275        let err = c.validate_licenca().unwrap_err();
10276        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
10277    }
10278
10279    #[test]
10280    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
10281        // Diagnostic-shape pin on the shape-predicate arm (peer with
10282        // `validate_edicao_invalid_diagnostic_carries_offending_value`
10283        // and `validate_repositorio_diagnostic_carries_offending_value`):
10284        // the error's Display surfaces the offending value + slot
10285        // name verbatim, so a `feira lint` run can render the
10286        // diagnostic without re-parsing and the author can grep
10287        // their caixa.lisp for the offending `:licenca` value.
10288        let c = caixa_with_licenca(Some("Apache_2.0"));
10289        let rendered = c.validate_licenca().unwrap_err().to_string();
10290        assert!(
10291            rendered.contains(":licenca"),
10292            "diagnostic must name the offending slot: {rendered}",
10293        );
10294        assert!(
10295            rendered.contains("Apache_2.0"),
10296            "diagnostic must quote the offending value: {rendered}",
10297        );
10298    }
10299
10300    #[test]
10301    fn validate_licenca_rejects_empty_some() {
10302        // Canonical paste-from-blank-doc footgun. Without this gate
10303        // the empty `Some("")` silently passed the renderer's
10304        // `Option::unwrap_or_else(|| "MIT".into())` (which only
10305        // fires on `None`) and landed as a bare trailing period in
10306        // the rendered chart `README.md` `## License` section.
10307        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
10308        // arm on the sibling `Option<String>` Caixa slot.
10309        let c = caixa_with_licenca(Some(""));
10310        let err = c.validate_licenca().unwrap_err();
10311        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
10312    }
10313
10314    #[test]
10315    fn validate_licenca_template_passes() {
10316        // Round-trip pin: the bare `Caixa::template` shape (whether
10317        // it carries `:licenca` or omits it) passes the gate by
10318        // construction. A future template-shape change that
10319        // introduced `(:licenca "")` would surface here as a
10320        // regression. Mirrors the peer
10321        // `validate_descricao_template_passes` pin.
10322        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10323        c.validate_licenca().unwrap();
10324    }
10325
10326    #[test]
10327    fn validate_licenca_diagnostic_names_offending_slot() {
10328        // Diagnostic-shape pin (peer with
10329        // `validate_descricao_diagnostic_names_offending_slot`):
10330        // the error's Display surfaces the `:licenca` slot name
10331        // verbatim, so a `feira lint` run can render the diagnostic
10332        // without re-parsing and the author can grep their caixa.lisp
10333        // for the offending `:licenca` line.
10334        let c = caixa_with_licenca(Some(""));
10335        let rendered = c.validate_licenca().unwrap_err().to_string();
10336        assert!(
10337            rendered.contains(":licenca"),
10338            "diagnostic must name the offending slot: {rendered}",
10339        );
10340    }
10341
10342    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
10343
10344    #[test]
10345    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
10346        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
10347        // pin: [`Caixa::licenca`] must return the `:licenca` typed
10348        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10349        // raw `self.licenca.as_deref()` access across every
10350        // representative value in the accept-set — `None` (the "omit
10351        // the slot to defer to the caixa-helm renderer's `MIT`
10352        // fallback" arm every existing fixture without a `:licenca`
10353        // line carries), `Some("")` (a past-the-guard sentinel that
10354        // pins the accessor doesn't perform a silent
10355        // `Some("") → None` collapse on the empty arm — validate
10356        // rejects `Some("")` through `LicencaEmpty` but the accessor
10357        // must ship the raw slot verbatim so a validate-time gate
10358        // regression surfaces at the caixa-helm emit boundary rather
10359        // than being silently absorbed into the fallback), `Some("MIT")`
10360        // (the canonical single-license shape every `feira init`
10361        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
10362        // canonical `OR`-compound shape the peer
10363        // `validate_licenca_accepts_canonical_expressions` positive
10364        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
10365        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
10366        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
10367        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
10368        // guard sentinels — validate rejects each through
10369        // `LicencaInvalid` but the accessor must ship the raw slot
10370        // verbatim).
10371        //
10372        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
10373        // accessor pin on the substrate primitive — opens the "outer
10374        // [`Caixa`] `Option<&str>` scalar" projection pattern the
10375        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
10376        // future lifts fold on. Sibling in shape to the peer per-`:placement`
10377        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10378        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10379        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10380        // axes, extended onto the outer top-level [`Caixa`] universal-
10381        // axis surface. Pins against a future silent detour that
10382        // returned an owned `Option<String>` (which would type-check
10383        // but silently allocate on every accessor call, breaking the
10384        // zero-cost projection every peer sibling accessor carries), a
10385        // `Some("") → None` collapse (which would silently absorb the
10386        // `LicencaEmpty` refusal case at the accessor boundary and the
10387        // caixa-helm emit path would silently fall back to `"MIT"` on
10388        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
10389        // `None → Some("MIT")` collapse (which would silently reify
10390        // the caixa-helm renderer's `"MIT"` fallback at the accessor
10391        // boundary and every downstream consumer keying off the
10392        // `Option::is_none()` discriminator would lose the "author
10393        // omitted the slot" signal).
10394        for licenca in [
10395            None,
10396            Some(""),
10397            Some("MIT"),
10398            Some("Apache-2.0 OR MIT"),
10399            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
10400            Some("MIT "),
10401            Some(" MIT"),
10402            Some("MIT\n"),
10403            Some("Apache_2.0"),
10404            Some("MIT,Apache-2.0"),
10405        ] {
10406            let c = caixa_with_licenca(licenca);
10407            assert_eq!(
10408                c.licenca(),
10409                licenca,
10410                "Caixa::licenca must return :licenca verbatim (got {:?}, \
10411                 expected {licenca:?})",
10412                c.licenca(),
10413            );
10414            assert_eq!(
10415                c.licenca(),
10416                c.licenca.as_deref(),
10417                "Caixa::licenca must byte-equal the raw \
10418                 `self.licenca.as_deref()` field access across every \
10419                 value in the Option<&str> accept-set",
10420            );
10421        }
10422    }
10423
10424    #[test]
10425    fn validate_licenca_empty_arm_routes_through_accessor() {
10426        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
10427        // must key off [`Caixa::licenca`], not the raw
10428        // `self.licenca.as_deref()` field access. Structurally: a
10429        // `Caixa { licenca: Some(""), .. }` must surface the
10430        // `LicencaEmpty` refusal exactly, and a
10431        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
10432        // single-license form) must pass validate. The pair jointly
10433        // pins the accessor + validate-gate composition: any future
10434        // silent detour that had the accessor return `None` on the
10435        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10436        // silently absorb the `LicencaEmpty` refusal at the accessor
10437        // boundary and the validate gate would accept a struct-literal
10438        // `Caixa { licenca: Some(""), .. }` — the composition pin
10439        // catches that at caixa-core build time.
10440        //
10441        // Peer of the per-`:politicas :circuit-breaker`
10442        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
10443        // accessor-composition pin
10444        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
10445        // on the sibling per-M3-mesh-slot required-`u32` axis — same
10446        // "the validate / shape-gate predicate must route through the
10447        // substrate-primitive typed dispatch" discipline extended onto
10448        // the outer top-level [`Caixa`] universal-axis
10449        // `Option<&str>`-composition surface.
10450        let c = caixa_with_licenca(Some(""));
10451        assert!(
10452            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
10453            "validate_licenca must reject licenca == Some(\"\") with \
10454             LicencaEmpty — the accessor and the validate gate must \
10455             route through the same substrate-primitive typed dispatch \
10456             on the :licenca empty arm",
10457        );
10458        let c = caixa_with_licenca(Some("MIT"));
10459        assert!(
10460            c.validate_licenca().is_ok(),
10461            "validate_licenca must accept licenca == Some(\"MIT\") \
10462             (the canonical single-license SPDX shape)",
10463        );
10464    }
10465
10466    #[test]
10467    fn licenca_projects_option_str_by_borrow() {
10468        // The by-borrow pin: [`Caixa::licenca`] returns
10469        // `Option<&str>` by borrow — the `&str` borrows the underlying
10470        // `String` storage of the `Option<String>` slot and the
10471        // accessor must not allocate a fresh `String` on every call.
10472        // Peer of the per-`:placement`
10473        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10474        // borrow pin on the peer per-M3-mesh-slot
10475        // `Option<&str>`-return axis, extended onto the outer top-
10476        // level [`Caixa`] universal-axis `Option<&str>` shape — the
10477        // accessor's returned `&str` must borrow from `&self` (the
10478        // returned reference's lifetime is tied to `&self`), and
10479        // calling the accessor twice on the same [`Caixa`] must yield
10480        // the same `Option<&str>` verbatim (idempotent, no side
10481        // effects on `&self`).
10482        //
10483        // Pins against a future silent detour that returned an owned
10484        // `Option<String>` (which would type-check but silently
10485        // allocate on every call, breaking the zero-cost projection
10486        // every peer sibling accessor carries), or a one-arm-only
10487        // accessor that returned a saturating value on some sentinel
10488        // input (breaking the pass-through invariant the sibling
10489        // required-scalar accessors carry).
10490        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
10491            let c = caixa_with_licenca(licenca);
10492            let first = c.licenca();
10493            let second = c.licenca();
10494            assert_eq!(
10495                first, second,
10496                "Caixa::licenca must be idempotent — two successive \
10497                 calls on the same &self must return the same \
10498                 Option<&str>",
10499            );
10500            assert_eq!(
10501                first, licenca,
10502                "Caixa::licenca must return :licenca verbatim by \
10503                 borrow — got {first:?}, expected {licenca:?}",
10504            );
10505        }
10506    }
10507
10508    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
10509
10510    #[test]
10511    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
10512        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
10513        // pin: [`Caixa::repositorio`] must return the `:repositorio`
10514        // typed byte-string verbatim as an `Option<&str>`, byte-equal
10515        // to the raw `self.repositorio.as_deref()` access across every
10516        // representative value in the accept-set — `None` (the "omit
10517        // the slot to defer to the per-renderer placeholder" arm every
10518        // existing fixture without a `:repositorio` line carries),
10519        // `Some("")` (a past-the-guard sentinel that pins the accessor
10520        // doesn't perform a silent `Some("") → None` collapse on the
10521        // empty arm — validate rejects `Some("")` through
10522        // `RepositorioEmpty` but the accessor must ship the raw slot
10523        // verbatim so a validate-time gate regression surfaces at the
10524        // caixa-helm / caixa-flux emit boundary rather than being
10525        // silently absorbed into the per-renderer fallback),
10526        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
10527        // shorthand every existing manifest fixture across
10528        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
10529        // `Some("https://github.com/pleme-io/checkout")` (the canonical
10530        // `https://` URL the README quickstart uses),
10531        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
10532        // `Some("git://github.com/pleme-io/checkout.git")` /
10533        // `Some("git@github.com:pleme-io/checkout.git")` /
10534        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
10535        // github scheme the shared `is_git_repo_url` predicate
10536        // documents), and five past-the-guard sentinels for the
10537        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
10538        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
10539        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
10540        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
10541        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
10542        // sentinels pin the accessor doesn't silently absorb the
10543        // refusal cases into a fallback).
10544        //
10545        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
10546        // accessor pin on the substrate primitive — sibling of the peer
10547        // [`Caixa::licenca`] (6d5bc28) pin
10548        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
10549        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
10550        // projection pin pattern this pin folds on. Sibling in shape to
10551        // the peer per-`:placement`
10552        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10553        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10554        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10555        // axes, extended onto the outer top-level [`Caixa`] universal-
10556        // axis surface. Pins against a future silent detour that
10557        // returned an owned `Option<String>` (which would type-check
10558        // but silently allocate on every accessor call, breaking the
10559        // zero-cost projection every peer sibling accessor carries), a
10560        // `Some("") → None` collapse (which would silently absorb the
10561        // `RepositorioEmpty` refusal case at the accessor boundary and
10562        // the caixa-helm `Chart.yaml` `home:` fold would silently
10563        // render a `home: null` / omitted field on a struct-literal
10564        // `Caixa { repositorio: Some(""), .. }`), or a
10565        // `None → Some(<default>)` collapse (which would silently reify
10566        // the per-renderer fallback at the accessor boundary and every
10567        // downstream consumer keying off the `Option::is_none()`
10568        // discriminator would lose the "author omitted the slot"
10569        // signal).
10570        for repositorio in [
10571            None,
10572            Some(""),
10573            Some("github:pleme-io/hello-rio"),
10574            Some("https://github.com/pleme-io/checkout"),
10575            Some("ssh://git@github.com/pleme-io/checkout.git"),
10576            Some("git://github.com/pleme-io/checkout.git"),
10577            Some("git@github.com:pleme-io/checkout.git"),
10578            Some("file:///opt/mirrors/pleme-io/checkout"),
10579            Some("pleme-io/checkout"),
10580            Some("-upload-pack=evil"),
10581            Some("github:pleme-io/checkout?ref=main"),
10582            Some("github:pleme-io/checkout#main"),
10583            Some("github:pleme-io/{tpl}"),
10584        ] {
10585            let c = caixa_with_repositorio(repositorio);
10586            assert_eq!(
10587                c.repositorio(),
10588                repositorio,
10589                "Caixa::repositorio must return :repositorio verbatim \
10590                 (got {:?}, expected {repositorio:?})",
10591                c.repositorio(),
10592            );
10593            assert_eq!(
10594                c.repositorio(),
10595                c.repositorio.as_deref(),
10596                "Caixa::repositorio must byte-equal the raw \
10597                 `self.repositorio.as_deref()` field access across every \
10598                 value in the Option<&str> accept-set",
10599            );
10600        }
10601    }
10602
10603    #[test]
10604    fn validate_repositorio_empty_arm_routes_through_accessor() {
10605        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
10606        // gate must key off [`Caixa::repositorio`], not the raw
10607        // `self.repositorio.as_deref()` field access. Structurally: a
10608        // `Caixa { repositorio: Some(""), .. }` must surface the
10609        // `RepositorioEmpty` refusal exactly, and a
10610        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
10611        // (the canonical `github:` shorthand form) must pass validate.
10612        // The pair jointly pins the accessor + validate-gate
10613        // composition: any future silent detour that had the accessor
10614        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
10615        // collapse) would silently absorb the `RepositorioEmpty` refusal
10616        // at the accessor boundary and the validate gate would accept a
10617        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
10618        // composition pin catches that at caixa-core build time.
10619        //
10620        // Peer of the [`Caixa::licenca`] (6d5bc28)
10621        // `validate_licenca_empty_arm_routes_through_accessor`
10622        // composition pin on the sibling outer top-level [`Caixa`]
10623        // `Option<&str>` universal-axis surface — same "the validate /
10624        // shape-gate predicate must route through the substrate-
10625        // primitive typed dispatch" discipline extended onto the second
10626        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10627        // composition surface.
10628        let c = caixa_with_repositorio(Some(""));
10629        assert!(
10630            matches!(
10631                c.validate_repositorio(),
10632                Err(ManifestError::RepositorioEmpty),
10633            ),
10634            "validate_repositorio must reject repositorio == Some(\"\") \
10635             with RepositorioEmpty — the accessor and the validate gate \
10636             must route through the same substrate-primitive typed \
10637             dispatch on the :repositorio empty arm",
10638        );
10639        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
10640        assert!(
10641            c.validate_repositorio().is_ok(),
10642            "validate_repositorio must accept repositorio == \
10643             Some(\"github:pleme-io/hello-rio\") (the canonical \
10644             `github:` shorthand git-repo-URL shape)",
10645        );
10646    }
10647
10648    #[test]
10649    fn repositorio_projects_option_str_by_borrow() {
10650        // The by-borrow pin: [`Caixa::repositorio`] returns
10651        // `Option<&str>` by borrow — the `&str` borrows the underlying
10652        // `String` storage of the `Option<String>` slot and the
10653        // accessor must not allocate a fresh `String` on every call.
10654        // Peer of the per-`:placement`
10655        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
10656        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
10657        // `Option<&str>`-return axes, extended onto the second outer
10658        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
10659        // the accessor's returned `&str` must borrow from `&self` (the
10660        // returned reference's lifetime is tied to `&self`), and
10661        // calling the accessor twice on the same [`Caixa`] must yield
10662        // the same `Option<&str>` verbatim (idempotent, no side effects
10663        // on `&self`).
10664        //
10665        // Pins against a future silent detour that returned an owned
10666        // `Option<String>` (which would type-check but silently
10667        // allocate on every call, breaking the zero-cost projection
10668        // every peer sibling accessor carries), or a one-arm-only
10669        // accessor that returned a saturating value on some sentinel
10670        // input (breaking the pass-through invariant the sibling
10671        // required-scalar accessors carry).
10672        for repositorio in [
10673            None,
10674            Some(""),
10675            Some("github:pleme-io/hello-rio"),
10676            Some("https://github.com/pleme-io/checkout"),
10677        ] {
10678            let c = caixa_with_repositorio(repositorio);
10679            let first = c.repositorio();
10680            let second = c.repositorio();
10681            assert_eq!(
10682                first, second,
10683                "Caixa::repositorio must be idempotent — two successive \
10684                 calls on the same &self must return the same \
10685                 Option<&str>",
10686            );
10687            assert_eq!(
10688                first, repositorio,
10689                "Caixa::repositorio must return :repositorio verbatim by \
10690                 borrow — got {first:?}, expected {repositorio:?}",
10691            );
10692        }
10693    }
10694
10695    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
10696
10697    #[test]
10698    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
10699        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
10700        // return the author-declared `:repositorio` byte-string verbatim
10701        // on the `Some` arm — no scheme rewrite, no trailing-slash
10702        // canonicalization, no `github:` → `https://github.com/`
10703        // desugaring. The resolved-URL composer is the projection of
10704        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
10705        // the `String`-return arity every substrate-side field-fill
10706        // consumer keys off; on the `Some` arm the projection is
10707        // `str::to_owned` verbatim, so every accept-set value the
10708        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
10709        // across_permutations` pin covers (`https://…`, `github:…`,
10710        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
10711        // guard sentinel `pleme-io/…`) must survive the accessor
10712        // byte-equal. Pins against a future silent detour that rewrote
10713        // the `github:` shorthand to the `https://github.com/` full URL
10714        // at the accessor boundary (which would silently split the
10715        // resolved-URL surface from the raw [`Caixa::repositorio`]
10716        // accessor's documented pass-through invariant), or a trailing-
10717        // slash normalization (which would silently break the
10718        // FluxCD `GitRepository` `spec.url` byte-exact match every
10719        // downstream consumer keys the source-controller reconcile off).
10720        for repositorio in [
10721            "github:pleme-io/hello-rio",
10722            "https://github.com/pleme-io/checkout",
10723            "ssh://git@github.com/pleme-io/checkout.git",
10724            "git://github.com/pleme-io/checkout.git",
10725            "git@github.com:pleme-io/checkout.git",
10726            "file:///opt/mirrors/pleme-io/checkout",
10727        ] {
10728            let c = caixa_with_repositorio(Some(repositorio));
10729            assert_eq!(
10730                c.canonical_git_url(),
10731                repositorio,
10732                "Caixa::canonical_git_url on the Some arm must return \
10733                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
10734                c.canonical_git_url(),
10735            );
10736        }
10737    }
10738
10739    #[test]
10740    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
10741        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
10742        // `None` arm must emit the substrate's canonical pleme-org github
10743        // URL derived from `caixa.nome()` — `https://github.com/<org>/
10744        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
10745        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
10746        // is the exact byte-image of the prior inline
10747        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
10748        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
10749        // re-derived open-coded. Pins against a future silent detour
10750        // that migrated the `<org>` segment to a different constant (a
10751        // fork rebranding that split off a new
10752        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
10753        // to migrate onto), a scheme change (`https://` → `git://` or
10754        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
10755        // override (which would break the substrate-wide single-source-
10756        // of-truth guarantee this method encodes).
10757        let c = caixa_with_repositorio(None);
10758        let expected = format!(
10759            "https://github.com/{org}/{nome}",
10760            org = crate::DEFAULT_PLEME_GIT_ORG,
10761            nome = c.nome(),
10762        );
10763        assert_eq!(
10764            c.canonical_git_url(),
10765            expected,
10766            "Caixa::canonical_git_url on the None arm must fold through \
10767             the substrate's canonical pleme-org github URL fallback \
10768             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
10769             {:?}, expected {expected:?}",
10770            c.canonical_git_url(),
10771        );
10772    }
10773
10774    #[test]
10775    fn canonical_git_url_byte_matches_manual_composition() {
10776        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
10777        // byte-identically to the manual open-coded
10778        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
10779        //  format!("https://github.com/{org}/{nome}", ...))` composition
10780        // every prior substrate-side caller re-derived. Guards the
10781        // paired-site convergence just applied at caixa-flux's
10782        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
10783        // now routes through this accessor): a future implementation of
10784        // this method that reordered the format arguments, swapped the
10785        // `<org>` constant for a different one, or interposed a
10786        // canonicalization pass on the `Some` arm surfaces here as a
10787        // caixa-core build-time test failure rather than as a downstream
10788        // FluxCD `GitRepository` reconcile mismatch far from this
10789        // method's source.
10790        for repositorio in [
10791            None,
10792            Some("github:pleme-io/hello-rio"),
10793            Some("https://github.com/pleme-io/checkout"),
10794            Some("ssh://git@github.com/pleme-io/checkout.git"),
10795        ] {
10796            let c = caixa_with_repositorio(repositorio);
10797            let manual = c.repositorio().map_or_else(
10798                || {
10799                    format!(
10800                        "https://github.com/{org}/{nome}",
10801                        org = crate::DEFAULT_PLEME_GIT_ORG,
10802                        nome = c.nome(),
10803                    )
10804                },
10805                str::to_owned,
10806            );
10807            assert_eq!(
10808                c.canonical_git_url(),
10809                manual,
10810                "Caixa::canonical_git_url must byte-equal the manual \
10811                 open-coded `repositorio().map(str::to_owned)\
10812                 .unwrap_or_else(|| format!(...))` composition across \
10813                 every representative :repositorio input — got {:?}, \
10814                 expected {manual:?}",
10815                c.canonical_git_url(),
10816            );
10817        }
10818    }
10819
10820    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
10821
10822    #[test]
10823    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
10824        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
10825        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
10826        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
10827        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
10828        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
10829        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
10830        // the `0.0.0` boundary case. Every accept-set value the peer
10831        // validate gate lets through must survive the resolved-tag
10832        // projection byte-equal.
10833        for versao in [
10834            "0.1.0",
10835            "0.0.0",
10836            "1.0.0",
10837            "1.2.3-rc.1",
10838            "1.2.3+build.42",
10839            "1.2.3-rc.1+build.42",
10840        ] {
10841            let c = caixa_with_versao(versao);
10842            let expected = format!(
10843                "{prefix}{versao}",
10844                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
10845            );
10846            assert_eq!(
10847                c.publish_tag(),
10848                expected,
10849                "Caixa::publish_tag must compose \
10850                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
10851                 :versao ({versao:?}) verbatim — got {got:?}, \
10852                 expected {expected:?}",
10853                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
10854                got = c.publish_tag(),
10855            );
10856        }
10857    }
10858
10859    #[test]
10860    fn publish_tag_starts_with_default_publish_tag_prefix() {
10861        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
10862        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
10863        // byte-string on every input, guarding a hypothetical future
10864        // implementation that migrated the prefix segment to an inline
10865        // literal (`"v"`) that would silently drift from any rebrand of
10866        // the lifted constant. Peer to the sibling caixa-flux
10867        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
10868        // test which pins the same prefix invariant at the reader-side
10869        // `GitRefSpec::Tag` emit site.
10870        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
10871            let c = caixa_with_versao(versao);
10872            let tag = c.publish_tag();
10873            assert!(
10874                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
10875                "Caixa::publish_tag emission {tag:?} must start with \
10876                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
10877                 ({prefix:?})",
10878                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
10879            );
10880        }
10881    }
10882
10883    #[test]
10884    fn publish_tag_byte_matches_manual_composition() {
10885        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
10886        // identically to the manual open-coded
10887        // `format!("{prefix}{versao}", prefix =
10888        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
10889        //  caixa.versao())` composition every prior substrate-side
10890        // caller re-derived. Guards the paired-site convergence just
10891        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
10892        // `git_ref` composer (which now routes through this accessor):
10893        // a future implementation of this method that reordered the
10894        // format arguments, swapped the `<prefix>` constant for a
10895        // different one, or interposed a canonicalization pass on the
10896        // `:versao` axis surfaces here as a caixa-core build-time test
10897        // failure rather than as a downstream FluxCD `GitRepository`
10898        // reconcile mismatch far from this method's source.
10899        for versao in [
10900            "0.1.0",
10901            "0.0.0",
10902            "1.2.3-rc.1",
10903            "1.2.3+build.42",
10904            "1.2.3-rc.1+build.42",
10905        ] {
10906            let c = caixa_with_versao(versao);
10907            let manual = format!(
10908                "{prefix}{versao}",
10909                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
10910                versao = c.versao(),
10911            );
10912            assert_eq!(
10913                c.publish_tag(),
10914                manual,
10915                "Caixa::publish_tag must byte-equal the manual \
10916                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
10917                 composition across every representative :versao input \
10918                 — got {got:?}, expected {manual:?}",
10919                got = c.publish_tag(),
10920            );
10921        }
10922    }
10923
10924    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
10925
10926    #[test]
10927    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
10928        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
10929        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
10930        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
10931        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
10932        // set sweep documents — single-word, hyphen-joined, version-
10933        // suffixed, single-char, two-char, digit-start, retry-suffixed.
10934        // Every accept-set value the peer validate gate lets through must
10935        // survive the resolved-chart-name projection byte-equal.
10936        for nome in [
10937            "checkout",
10938            "cart-v2",
10939            "a",
10940            "db",
10941            "3rd-party-shim",
10942            "payment-retry",
10943            "0",
10944        ] {
10945            let c = caixa_with_nome(nome);
10946            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
10947            assert_eq!(
10948                c.lareira_chart_name(),
10949                expected,
10950                "Caixa::lareira_chart_name must compose \
10951                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
10952                 :nome ({nome:?}) verbatim — got {got:?}, \
10953                 expected {expected:?}",
10954                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
10955                got = c.lareira_chart_name(),
10956            );
10957        }
10958    }
10959
10960    #[test]
10961    fn lareira_chart_name_starts_with_lifted_prefix() {
10962        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
10963        // must begin with the canonical
10964        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
10965        // input, guarding a hypothetical future implementation that
10966        // migrated the prefix segment to an inline literal (`"lareira-"`)
10967        // that would silently drift from any rebrand of the lifted
10968        // constant. Peer to the sibling
10969        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
10970        // the co-resident resolved-publish-tag composer's prefix axis.
10971        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
10972            let c = caixa_with_nome(nome);
10973            let chart = c.lareira_chart_name();
10974            assert!(
10975                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
10976                "Caixa::lareira_chart_name emission {chart:?} must start \
10977                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
10978                 ({prefix:?})",
10979                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
10980            );
10981        }
10982    }
10983
10984    #[test]
10985    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
10986        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
10987        // byte-identically to the manual open-coded
10988        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
10989        // composition every prior substrate-side caller re-derived.
10990        // Guards the paired-site convergence just applied at caixa-helm's
10991        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
10992        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
10993        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
10994        // composer (all of which now route through this accessor): a
10995        // future implementation of this method that reordered the
10996        // composition arguments, swapped the `<prefix>` constant for a
10997        // different one, or interposed a canonicalization pass on the
10998        // `:nome` axis surfaces here as a caixa-core build-time test
10999        // failure rather than as a downstream Helm chart-render / FluxCD
11000        // reconcile / tatara Process-CR mismatch far from this method's
11001        // source.
11002        for nome in [
11003            "checkout",
11004            "cart-v2",
11005            "a",
11006            "db",
11007            "3rd-party-shim",
11008            "payment-retry",
11009        ] {
11010            let c = caixa_with_nome(nome);
11011            let manual = crate::lareira_chart_name(c.nome());
11012            assert_eq!(
11013                c.lareira_chart_name(),
11014                manual,
11015                "Caixa::lareira_chart_name must byte-equal the manual \
11016                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
11017                 composition across every representative :nome input — \
11018                 got {got:?}, expected {manual:?}",
11019                got = c.lareira_chart_name(),
11020            );
11021        }
11022    }
11023
11024    // ── Caixa::oci_chart_ref — resolved-OCI-chart-ref composer ────────
11025
11026    #[test]
11027    fn oci_chart_ref_composes_scheme_and_lareira_chart_name_on_all_shapes() {
11028        // Fail-before-pass-after pin: [`Caixa::oci_chart_ref`] must
11029        // compose [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied
11030        // `registry` + [`crate::lareira_chart_name`]-of-[`Caixa::nome`]
11031        // across the full paired `(registry, :nome)` accept-set — every
11032        // representative registry the substrate-side emitters carry
11033        // (`ghcr.io/pleme-io/charts`, the canonical CAIXA-SDLC §II
11034        // ArtifactHub-tier registry; `ghcr.io/pleme-io`, the bare-org
11035        // arm the sibling `oci_chart_ref_pins_byte_shape_against_prior_
11036        // inline_format` render-side pin exercises; `registry.example.
11037        // com`, an off-org shape; `localhost:5000`, the local-dev shape
11038        // every `feira chart` iteration path lands under) × every DNS-
11039        // 1123 `:nome` shape the peer `validate_nome_accepts_canonical_
11040        // forms` positive-set sweep documents (single-word, hyphen-
11041        // joined, single-char, two-char, digit-start, retry-suffixed).
11042        // Every accept-set pair the peer validate gates let through must
11043        // survive the resolved-OCI-ref projection byte-equal.
11044        for registry in [
11045            "ghcr.io/pleme-io/charts",
11046            "ghcr.io/pleme-io",
11047            "registry.example.com",
11048            "localhost:5000",
11049        ] {
11050            for nome in [
11051                "checkout",
11052                "cart-v2",
11053                "a",
11054                "db",
11055                "3rd-party-shim",
11056                "payment-retry",
11057                "0",
11058            ] {
11059                let c = caixa_with_nome(nome);
11060                let expected = format!(
11061                    "{scheme}{registry}/{chart}",
11062                    scheme = crate::OCI_SCHEME_PREFIX,
11063                    chart = crate::lareira_chart_name(nome),
11064                );
11065                assert_eq!(
11066                    c.oci_chart_ref(registry),
11067                    expected,
11068                    "Caixa::oci_chart_ref must compose \
11069                     OCI_SCHEME_PREFIX ({scheme:?}) + registry ({registry:?}) + \
11070                     lareira_chart_name(:nome ({nome:?})) verbatim — got {got:?}, \
11071                     expected {expected:?}",
11072                    scheme = crate::OCI_SCHEME_PREFIX,
11073                    got = c.oci_chart_ref(registry),
11074                );
11075            }
11076        }
11077    }
11078
11079    #[test]
11080    fn oci_chart_ref_starts_with_lifted_scheme_prefix() {
11081        // Scheme-prefix-shape pin: every [`Caixa::oci_chart_ref`]
11082        // emission must begin with the canonical
11083        // [`crate::OCI_SCHEME_PREFIX`] byte-string on every input, guarding
11084        // a hypothetical future implementation that migrated the scheme
11085        // segment to an inline literal (`"oci://"`) that would silently
11086        // drift from any rebrand of the lifted constant. Peer to the
11087        // sibling [`publish_tag_starts_with_default_publish_tag_prefix`]
11088        // + [`lareira_chart_name_starts_with_lifted_prefix`] pins on the
11089        // co-resident resolved-publish-tag / resolved-chart-name
11090        // composers' prefix axes.
11091        for registry in [
11092            "ghcr.io/pleme-io/charts",
11093            "ghcr.io/pleme-io",
11094            "localhost:5000",
11095        ] {
11096            for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
11097                let c = caixa_with_nome(nome);
11098                let ref_ = c.oci_chart_ref(registry);
11099                assert!(
11100                    ref_.starts_with(crate::OCI_SCHEME_PREFIX),
11101                    "Caixa::oci_chart_ref emission {ref_:?} must start \
11102                     with the lifted crate::OCI_SCHEME_PREFIX ({scheme:?}) \
11103                     — registry ({registry:?}), :nome ({nome:?})",
11104                    scheme = crate::OCI_SCHEME_PREFIX,
11105                );
11106            }
11107        }
11108    }
11109
11110    #[test]
11111    fn oci_chart_ref_byte_matches_canonical_helper_composition() {
11112        // Byte-parity pin: [`Caixa::oci_chart_ref`] must render byte-
11113        // identically to the manual open-coded
11114        // `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step
11115        // composition every prior substrate-side caller re-derived.
11116        // Guards the paired-site convergence just applied at caixa-
11117        // tatara's [`derive_chart_ref`] helper (which now routes through
11118        // this accessor): a future implementation of this method that
11119        // reordered the composition arguments, swapped the `<scheme>`
11120        // constant for a different one, migrated the `<chart>` segment
11121        // off the paired [`crate::lareira_chart_name`] composer, or
11122        // interposed a canonicalization pass on either input axis
11123        // surfaces here as a caixa-core build-time test failure rather
11124        // than as a downstream `helm install` / FluxCD OCI-source
11125        // reconcile / tatara `Process`-CR mismatch far from this
11126        // method's source. Sibling to the peer
11127        // [`lareira_chart_name_byte_matches_canonical_helper_composition`]
11128        // / [`publish_tag_byte_matches_manual_composition`] /
11129        // [`canonical_git_url_byte_matches_manual_composition`] byte-
11130        // parity pins that carry the same discipline on the co-resident
11131        // resolved-chart-name / resolved-publish-tag / resolved-git-URL
11132        // composers.
11133        for registry in [
11134            "ghcr.io/pleme-io/charts",
11135            "ghcr.io/pleme-io",
11136            "registry.example.com",
11137            "localhost:5000",
11138        ] {
11139            for nome in [
11140                "checkout",
11141                "cart-v2",
11142                "a",
11143                "db",
11144                "3rd-party-shim",
11145                "payment-retry",
11146            ] {
11147                let c = caixa_with_nome(nome);
11148                let manual = crate::oci_chart_ref(registry, c.nome());
11149                assert_eq!(
11150                    c.oci_chart_ref(registry),
11151                    manual,
11152                    "Caixa::oci_chart_ref must byte-equal the manual \
11153                     open-coded `caixa_core::oci_chart_ref(registry, \
11154                     caixa.nome())` composition across every representative \
11155                     (registry, :nome) pair — registry ({registry:?}), \
11156                     :nome ({nome:?}), got {got:?}, expected {manual:?}",
11157                    got = c.oci_chart_ref(registry),
11158                );
11159            }
11160        }
11161    }
11162
11163    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
11164
11165    #[test]
11166    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
11167        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
11168        // pin: [`Caixa::descricao`] must return the `:descricao` typed
11169        // byte-string verbatim as an `Option<&str>`, byte-equal to the
11170        // raw `self.descricao.as_deref()` access across every
11171        // representative value in the accept-set — `None` (the "omit
11172        // the slot to defer to the per-renderer `caixa.nome`-derived
11173        // fallback" arm every existing fixture without a `:descricao`
11174        // line carries), `Some("")` (a past-the-guard sentinel that
11175        // pins the accessor doesn't perform a silent `Some("") → None`
11176        // collapse on the empty arm — validate rejects `Some("")`
11177        // through `DescricaoEmpty` but the accessor must ship the raw
11178        // slot verbatim so a validate-time gate regression surfaces at
11179        // the caixa-helm / caixa-feira emit boundary rather than being
11180        // silently absorbed into the per-renderer `caixa.nome`-derived
11181        // fallback), `Some("Checkout flow.")` (the canonical one-line
11182        // prose descriptor the peer
11183        // `validate_descricao_accepts_canonical_value` positive sweep
11184        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
11185        // Servico.")` (the multi-byte Unicode continuation-byte shape
11186        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
11187        // multi-glyph Unicode shape the peer
11188        // `is_chart_description_shape` predicate accepts), and five
11189        // past-the-guard sentinels for the `DescricaoInvalid` refusal
11190        // cases (`Some(" Checkout flow.")` leading-whitespace,
11191        // `Some("Checkout flow. ")` trailing-whitespace,
11192        // `Some("Checkout\nflow.")` embedded-LF,
11193        // `Some("Checkout\tflow.")` embedded-TAB, and
11194        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
11195        // the accessor doesn't silently absorb the refusal cases into
11196        // a fallback).
11197        //
11198        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
11199        // accessor pin on the substrate primitive — sibling of the peer
11200        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
11201        // (cc7332d) pins that opened the "outer [`Caixa`]
11202        // `Option<&str>` scalar" projection pin pattern this pin folds
11203        // on. Sibling in shape to the peer per-`:placement`
11204        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11205        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11206        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11207        // axes, extended onto the outer top-level [`Caixa`] universal-
11208        // axis surface. Pins against a future silent detour that
11209        // returned an owned `Option<String>` (which would type-check
11210        // but silently allocate on every accessor call, breaking the
11211        // zero-cost projection every peer sibling accessor carries), a
11212        // `Some("") → None` collapse (which would silently absorb the
11213        // `DescricaoEmpty` refusal case at the accessor boundary and
11214        // the caixa-helm `Chart.yaml` `description:` fold would
11215        // silently render a `caixa.nome`-derived fallback on a
11216        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
11217        // `None → Some(<default>)` collapse (which would silently
11218        // reify the per-renderer `caixa.nome`-derived fallback at the
11219        // accessor boundary and every downstream consumer keying off
11220        // the `Option::is_none()` discriminator would lose the "author
11221        // omitted the slot" signal).
11222        for descricao in [
11223            None,
11224            Some(""),
11225            Some("Checkout flow."),
11226            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
11227            Some("→ — · ✓"),
11228            Some(" Checkout flow."),
11229            Some("Checkout flow. "),
11230            Some("Checkout\nflow."),
11231            Some("Checkout\tflow."),
11232            Some("Checkout\x00flow."),
11233        ] {
11234            let c = caixa_with_descricao(descricao);
11235            assert_eq!(
11236                c.descricao(),
11237                descricao,
11238                "Caixa::descricao must return :descricao verbatim (got \
11239                 {:?}, expected {descricao:?})",
11240                c.descricao(),
11241            );
11242            assert_eq!(
11243                c.descricao(),
11244                c.descricao.as_deref(),
11245                "Caixa::descricao must byte-equal the raw \
11246                 `self.descricao.as_deref()` field access across every \
11247                 value in the Option<&str> accept-set",
11248            );
11249        }
11250    }
11251
11252    #[test]
11253    fn validate_descricao_empty_arm_routes_through_accessor() {
11254        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
11255        // gate must key off [`Caixa::descricao`], not the raw
11256        // `self.descricao.as_deref()` field access. Structurally: a
11257        // `Caixa { descricao: Some(""), .. }` must surface the
11258        // `DescricaoEmpty` refusal exactly, and a
11259        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
11260        // canonical one-line-prose form) must pass validate. The pair
11261        // jointly pins the accessor + validate-gate composition: any
11262        // future silent detour that had the accessor return `None` on
11263        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
11264        // silently absorb the `DescricaoEmpty` refusal at the accessor
11265        // boundary and the validate gate would accept a struct-literal
11266        // `Caixa { descricao: Some(""), .. }` — the composition pin
11267        // catches that at caixa-core build time.
11268        //
11269        // Peer of the [`Caixa::licenca`] (6d5bc28)
11270        // `validate_licenca_empty_arm_routes_through_accessor` and
11271        // [`Caixa::repositorio`] (cc7332d)
11272        // `validate_repositorio_empty_arm_routes_through_accessor`
11273        // composition pins on the sibling outer top-level [`Caixa`]
11274        // `Option<&str>` universal-axis surface — same "the validate /
11275        // shape-gate predicate must route through the substrate-
11276        // primitive typed dispatch" discipline extended onto the third
11277        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
11278        // composition surface.
11279        let c = caixa_with_descricao(Some(""));
11280        assert!(
11281            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
11282            "validate_descricao must reject descricao == Some(\"\") \
11283             with DescricaoEmpty — the accessor and the validate gate \
11284             must route through the same substrate-primitive typed \
11285             dispatch on the :descricao empty arm",
11286        );
11287        let c = caixa_with_descricao(Some("Checkout flow."));
11288        assert!(
11289            c.validate_descricao().is_ok(),
11290            "validate_descricao must accept descricao == \
11291             Some(\"Checkout flow.\") (the canonical one-line-prose \
11292             chart-description shape)",
11293        );
11294    }
11295
11296    #[test]
11297    fn descricao_projects_option_str_by_borrow() {
11298        // The by-borrow pin: [`Caixa::descricao`] returns
11299        // `Option<&str>` by borrow — the `&str` borrows the underlying
11300        // `String` storage of the `Option<String>` slot and the
11301        // accessor must not allocate a fresh `String` on every call.
11302        // Peer of the [`Caixa::licenca`] (6d5bc28) and
11303        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
11304        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
11305        // the per-`:placement`
11306        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11307        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
11308        // return axis, extended onto the third outer top-level
11309        // [`Caixa`] universal-axis `Option<&str>` shape — the
11310        // accessor's returned `&str` must borrow from `&self` (the
11311        // returned reference's lifetime is tied to `&self`), and
11312        // calling the accessor twice on the same [`Caixa`] must yield
11313        // the same `Option<&str>` verbatim (idempotent, no side
11314        // effects on `&self`).
11315        //
11316        // Pins against a future silent detour that returned an owned
11317        // `Option<String>` (which would type-check but silently
11318        // allocate on every call, breaking the zero-cost projection
11319        // every peer sibling accessor carries), or a one-arm-only
11320        // accessor that returned a saturating value on some sentinel
11321        // input (breaking the pass-through invariant the sibling
11322        // required-scalar accessors carry).
11323        for descricao in [
11324            None,
11325            Some(""),
11326            Some("Checkout flow."),
11327            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
11328        ] {
11329            let c = caixa_with_descricao(descricao);
11330            let first = c.descricao();
11331            let second = c.descricao();
11332            assert_eq!(
11333                first, second,
11334                "Caixa::descricao must be idempotent — two successive \
11335                 calls on the same &self must return the same \
11336                 Option<&str>",
11337            );
11338            assert_eq!(
11339                first, descricao,
11340                "Caixa::descricao must return :descricao verbatim by \
11341                 borrow — got {first:?}, expected {descricao:?}",
11342            );
11343        }
11344    }
11345
11346    // ── validate_edicao — universal-axis language-edition shape ──
11347
11348    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
11349        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11350        c.edicao = edicao.map(String::from);
11351        c
11352    }
11353
11354    #[test]
11355    fn validate_edicao_accepts_none() {
11356        // The omit-the-slot identity: `:edicao` is optional. The
11357        // gate is a no-op when the author didn't declare a value —
11358        // every caixa without an `:edicao` line trivially passes,
11359        // and the substrate-side build pipeline falls back to the
11360        // documented default edition. Mirrors the peer
11361        // `validate_licenca_accepts_none` posture on the sibling
11362        // `Option<String>` Caixa slot.
11363        let c = caixa_with_edicao(None);
11364        c.validate_edicao().unwrap();
11365    }
11366
11367    #[test]
11368    fn validate_edicao_accepts_canonical_value() {
11369        // Positive control: the canonical `"2026"` edition every
11370        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
11371        // `caixa-mesh`) carries by construction passes the gate.
11372        // Future-introduced sibling editions (`"2027"`, `"2030"`,
11373        // `"2049"`) that match the same 4-digit ASCII decimal year
11374        // shape must also trivially pass — the structural shape
11375        // predicate accepts every well-formed year regardless of
11376        // whether the substrate yet understands the specific value
11377        // (a future known-edition allowlist tightens that).
11378        for ed in ["2026", "2027", "2030", "2049"] {
11379            let c = caixa_with_edicao(Some(ed));
11380            c.validate_edicao()
11381                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
11382        }
11383    }
11384
11385    #[test]
11386    fn validate_edicao_rejects_empty_some() {
11387        // Canonical paste-from-blank-doc footgun. Without this gate
11388        // the empty `Some("")` silently lands as `(:edicao "")` in
11389        // the rendered caixa.lisp and a future renderer-side
11390        // consumer's `Option::unwrap_or_else` (which only fires on
11391        // `None`) skips its fallback. Mirrors the peer
11392        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
11393        // `Option<String>` Caixa slot.
11394        let c = caixa_with_edicao(Some(""));
11395        let err = c.validate_edicao().unwrap_err();
11396        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
11397    }
11398
11399    #[test]
11400    fn validate_edicao_rejects_free_form_non_year() {
11401        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
11402        // `"nightly"` shapes carry no operational meaning on the
11403        // substrate's build-time edition selector. Until this gate
11404        // landed the bare empty-arm check let every such value
11405        // through and broke far from the source caixa.lisp. Peer
11406        // with the shape-predicate cascade
11407        // `validate_repositorio_rejects_missing_colon_separator`
11408        // establishes past its own empty arm.
11409        for ed in ["x", "latest", "nightly", "stable"] {
11410            let c = caixa_with_edicao(Some(ed));
11411            let err = c.validate_edicao().unwrap_err();
11412            assert!(
11413                matches!(err, ManifestError::EdicaoInvalid { .. }),
11414                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11415            );
11416        }
11417    }
11418
11419    #[test]
11420    fn validate_edicao_rejects_trailing_whitespace() {
11421        // Paste-from-doc whitespace footgun. A trailing space in
11422        // the `:edicao` value would silently break the substrate's
11423        // build-time edition match-table lookup at the rendered
11424        // artifact's edition-selector consumer. The shape predicate
11425        // refuses every whitespace byte by construction (any byte
11426        // outside `0-9` fails `is_ascii_digit`). Peer with
11427        // `validate_repositorio_rejects_whitespace`.
11428        let c = caixa_with_edicao(Some("2026 "));
11429        let err = c.validate_edicao().unwrap_err();
11430        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
11431            panic!("expected EdicaoInvalid, got {err:?}");
11432        };
11433        assert_eq!(edicao, "2026 ");
11434    }
11435
11436    #[test]
11437    fn validate_edicao_rejects_leading_whitespace() {
11438        // Symmetric paste-from-doc whitespace footgun on the leading
11439        // boundary — the gate refuses every shape with a non-digit
11440        // byte by construction.
11441        let c = caixa_with_edicao(Some(" 2026"));
11442        let err = c.validate_edicao().unwrap_err();
11443        assert!(
11444            matches!(err, ManifestError::EdicaoInvalid { .. }),
11445            "got {err:?}",
11446        );
11447    }
11448
11449    #[test]
11450    fn validate_edicao_rejects_control_char() {
11451        // Paste-from-multiline-doc CRLF footgun — control characters
11452        // at the value boundary break the substrate's build-time
11453        // edition-selector parser. Peer with
11454        // `validate_repositorio_rejects_control_char`.
11455        let c = caixa_with_edicao(Some("2026\n"));
11456        let err = c.validate_edicao().unwrap_err();
11457        assert!(
11458            matches!(err, ManifestError::EdicaoInvalid { .. }),
11459            "got {err:?}",
11460        );
11461    }
11462
11463    #[test]
11464    fn validate_edicao_rejects_non_ascii_lookalike() {
11465        // Fullwidth-keyboard look-alike footgun — `"2026"` is
11466        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
11467        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
11468        // edition selector wants an ASCII year, and the gate
11469        // refuses every non-ASCII shape by construction (length in
11470        // bytes is 12 ≠ 4, *and* every byte falls outside
11471        // `is_ascii_digit`'s `0-9` range).
11472        let c = caixa_with_edicao(Some("2026"));
11473        let err = c.validate_edicao().unwrap_err();
11474        assert!(
11475            matches!(err, ManifestError::EdicaoInvalid { .. }),
11476            "got {err:?}",
11477        );
11478    }
11479
11480    #[test]
11481    fn validate_edicao_rejects_version_tag_prefix() {
11482        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
11483        // / `"r2026"` are familiar shapes from git-tag / Rust
11484        // edition / release-tag conventions that don't apply to
11485        // the year-shaped edition axis. The shape predicate refuses
11486        // every leading non-digit prefix.
11487        for ed in ["v2026", "e2026", "r2026"] {
11488            let c = caixa_with_edicao(Some(ed));
11489            let err = c.validate_edicao().unwrap_err();
11490            assert!(
11491                matches!(err, ManifestError::EdicaoInvalid { .. }),
11492                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11493            );
11494        }
11495    }
11496
11497    #[test]
11498    fn validate_edicao_rejects_decimal_shape() {
11499        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
11500        // `"2026.0"` are familiar shapes from semver / float
11501        // conventions that don't apply to the year-shaped edition
11502        // axis. The shape predicate refuses every non-digit byte
11503        // (`.` falls outside `is_ascii_digit`).
11504        for ed in ["2026.1", "2026.0", "2026.0.1"] {
11505            let c = caixa_with_edicao(Some(ed));
11506            let err = c.validate_edicao().unwrap_err();
11507            assert!(
11508                matches!(err, ManifestError::EdicaoInvalid { .. }),
11509                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11510            );
11511        }
11512    }
11513
11514    #[test]
11515    fn validate_edicao_rejects_wrong_length_numeric() {
11516        // Wrong-length numeric footgun — `"26"` (truncated) /
11517        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
11518        // (zero-padded too wide) all parse as integers but don't
11519        // name a 4-digit year. The shape predicate refuses every
11520        // value whose length isn't exactly 4 bytes.
11521        for ed in ["26", "202", "20260", "00026", "9"] {
11522            let c = caixa_with_edicao(Some(ed));
11523            let err = c.validate_edicao().unwrap_err();
11524            assert!(
11525                matches!(err, ManifestError::EdicaoInvalid { .. }),
11526                "expected EdicaoInvalid on {ed:?}, got {err:?}",
11527            );
11528        }
11529    }
11530
11531    #[test]
11532    fn validate_edicao_empty_takes_precedence_over_shape() {
11533        // Empty-first cascade pin: the empty `Some("")` surfaces
11534        // the narrower `EdicaoEmpty` not the shape-predicate-
11535        // wrapped `EdicaoInvalid`, mirroring the peer
11536        // `validate_repositorio_empty_takes_precedence_over_shape`
11537        // (`RepositorioEmpty` → `RepositorioInvalid`),
11538        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
11539        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
11540        // cascades. The shape predicate also refuses the empty
11541        // input (defensively — `s.len() != 4`), but the
11542        // manifest-layer empty arm runs first to surface the
11543        // narrower diagnostic verbatim.
11544        let c = caixa_with_edicao(Some(""));
11545        let err = c.validate_edicao().unwrap_err();
11546        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
11547    }
11548
11549    #[test]
11550    fn validate_edicao_template_passes() {
11551        // Round-trip pin: the bare `Caixa::template` shape (which
11552        // carries `:edicao "2026"` verbatim) passes the gate by
11553        // construction. A future template-shape change that
11554        // introduced `(:edicao "")` or a non-year value would
11555        // surface here as a regression. Mirrors the peer
11556        // `validate_licenca_template_passes` pin.
11557        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11558        c.validate_edicao().unwrap();
11559    }
11560
11561    #[test]
11562    fn validate_edicao_diagnostic_names_offending_slot() {
11563        // Diagnostic-shape pin (peer with
11564        // `validate_licenca_diagnostic_names_offending_slot`): the
11565        // error's Display surfaces the `:edicao` slot name verbatim,
11566        // so a `feira lint` run can render the diagnostic without
11567        // re-parsing and the author can grep their caixa.lisp for
11568        // the offending `:edicao` line.
11569        let c = caixa_with_edicao(Some(""));
11570        let rendered = c.validate_edicao().unwrap_err().to_string();
11571        assert!(
11572            rendered.contains(":edicao"),
11573            "diagnostic must name the offending slot: {rendered}",
11574        );
11575    }
11576
11577    #[test]
11578    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
11579        // Diagnostic-shape pin on the shape-predicate arm (peer
11580        // with `validate_repositorio_diagnostic_carries_offending_value`):
11581        // the error's Display surfaces the offending value + slot
11582        // name verbatim, so a `feira lint` run can render the
11583        // diagnostic without re-parsing and the author can grep
11584        // their caixa.lisp for the offending `:edicao` value.
11585        let c = caixa_with_edicao(Some("v2026"));
11586        let rendered = c.validate_edicao().unwrap_err().to_string();
11587        assert!(
11588            rendered.contains(":edicao"),
11589            "diagnostic must name the offending slot: {rendered}",
11590        );
11591        assert!(
11592            rendered.contains("v2026"),
11593            "diagnostic must quote the offending value: {rendered}",
11594        );
11595    }
11596
11597    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
11598
11599    #[test]
11600    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
11601        // The canonical per-`Caixa` `:edicao` language-edition scalar
11602        // pin: [`Caixa::edicao`] must return the `:edicao` typed
11603        // byte-string verbatim as an `Option<&str>`, byte-equal to the
11604        // raw `self.edicao.as_deref()` access across every representative
11605        // value in the accept-set — `None` (the "omit the slot to defer
11606        // to the substrate's default edition" arm every existing
11607        // [`caixa-resolver`] fixture without an `:edicao` line carries),
11608        // `Some("")` (a past-the-guard sentinel that pins the accessor
11609        // doesn't perform a silent `Some("") → None` collapse on the
11610        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
11611        // but the accessor must ship the raw slot verbatim so a
11612        // validate-time gate regression surfaces at any future edition-
11613        // aware consumer's boundary rather than being silently absorbed
11614        // into the substrate's default edition), `Some("2026")` (the
11615        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
11616        // template scaffolds via [`Caixa::template`] and every
11617        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
11618        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
11619        // carries by construction), `Some("2018")` / `Some("2021")` /
11620        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
11621        // peer with Cargo's `[package] edition` grammar every future-
11622        // introduced sibling to `"2026"` will follow), and eight
11623        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
11624        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
11625        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
11626        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
11627        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
11628        // length-numeric, `Some("latest")` free-form-non-year — the
11629        // sentinels pin the accessor doesn't silently absorb the
11630        // refusal cases into a substrate-default-edition fallback).
11631        //
11632        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
11633        // return scalar accessor pin on the substrate primitive —
11634        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
11635        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
11636        // (3f16e2f) pins that opened the "outer [`Caixa`]
11637        // `Option<&str>` scalar" projection pin pattern this pin folds
11638        // on. Sibling in shape to the peer per-`:placement`
11639        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11640        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11641        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11642        // axes, extended onto the outer top-level [`Caixa`] universal-
11643        // axis surface's last unlifted `Option<String>` slot. Pins
11644        // against a future silent detour that returned an owned
11645        // `Option<String>` (which would type-check but silently
11646        // allocate on every accessor call, breaking the zero-cost
11647        // projection every peer sibling accessor carries), a
11648        // `Some("") → None` collapse (which would silently absorb the
11649        // `EdicaoEmpty` refusal case at the accessor boundary and any
11650        // future edition-aware consumer would silently fall back to
11651        // the substrate's default edition on a struct-literal
11652        // `Caixa { edicao: Some(""), .. }`), or a
11653        // `None → Some("2026")` collapse (which would silently reify
11654        // the substrate's default edition at the accessor boundary
11655        // and every downstream consumer keying off the
11656        // `Option::is_none()` discriminator would lose the "author
11657        // omitted the slot" signal).
11658        for edicao in [
11659            None,
11660            Some(""),
11661            Some("2026"),
11662            Some("2018"),
11663            Some("2021"),
11664            Some("2024"),
11665            Some("2026 "),
11666            Some(" 2026"),
11667            Some("2026\n"),
11668            Some("2026"),
11669            Some("v2026"),
11670            Some("2026.1"),
11671            Some("26"),
11672            Some("latest"),
11673        ] {
11674            let c = caixa_with_edicao(edicao);
11675            assert_eq!(
11676                c.edicao(),
11677                edicao,
11678                "Caixa::edicao must return :edicao verbatim (got {:?}, \
11679                 expected {edicao:?})",
11680                c.edicao(),
11681            );
11682            assert_eq!(
11683                c.edicao(),
11684                c.edicao.as_deref(),
11685                "Caixa::edicao must byte-equal the raw \
11686                 `self.edicao.as_deref()` field access across every \
11687                 value in the Option<&str> accept-set",
11688            );
11689        }
11690    }
11691
11692    #[test]
11693    fn validate_edicao_empty_arm_routes_through_accessor() {
11694        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
11695        // must key off [`Caixa::edicao`], not the raw
11696        // `self.edicao.as_deref()` field access. Structurally: a
11697        // `Caixa { edicao: Some(""), .. }` must surface the
11698        // `EdicaoEmpty` refusal exactly, and a
11699        // `Caixa { edicao: Some("2026"), .. }` (the canonical
11700        // 4-digit-ASCII-decimal-year form) must pass validate. The
11701        // pair jointly pins the accessor + validate-gate composition:
11702        // any future silent detour that had the accessor return `None`
11703        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
11704        // would silently absorb the `EdicaoEmpty` refusal at the
11705        // accessor boundary and the validate gate would accept a
11706        // struct-literal `Caixa { edicao: Some(""), .. }` — the
11707        // composition pin catches that at caixa-core build time.
11708        //
11709        // Peer of the [`Caixa::licenca`] (6d5bc28)
11710        // `validate_licenca_empty_arm_routes_through_accessor`,
11711        // [`Caixa::repositorio`] (cc7332d)
11712        // `validate_repositorio_empty_arm_routes_through_accessor`,
11713        // and [`Caixa::descricao`] (3f16e2f)
11714        // `validate_descricao_empty_arm_routes_through_accessor`
11715        // composition pins on the sibling outer top-level [`Caixa`]
11716        // `Option<&str>` universal-axis surface — same "the validate /
11717        // shape-gate predicate must route through the substrate-
11718        // primitive typed dispatch" discipline extended onto the
11719        // fourth and final outer top-level [`Caixa`] universal-axis
11720        // `Option<&str>`-composition surface, closing the accessor-
11721        // composition family.
11722        let c = caixa_with_edicao(Some(""));
11723        assert!(
11724            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
11725            "validate_edicao must reject edicao == Some(\"\") with \
11726             EdicaoEmpty — the accessor and the validate gate must \
11727             route through the same substrate-primitive typed dispatch \
11728             on the :edicao empty arm",
11729        );
11730        let c = caixa_with_edicao(Some("2026"));
11731        assert!(
11732            c.validate_edicao().is_ok(),
11733            "validate_edicao must accept edicao == Some(\"2026\") \
11734             (the canonical 4-digit-ASCII-decimal-year shape)",
11735        );
11736    }
11737
11738    #[test]
11739    fn edicao_projects_option_str_by_borrow() {
11740        // The by-borrow pin: [`Caixa::edicao`] returns
11741        // `Option<&str>` by borrow — the `&str` borrows the underlying
11742        // `String` storage of the `Option<String>` slot and the
11743        // accessor must not allocate a fresh `String` on every call.
11744        // Peer of the [`Caixa::licenca`] (6d5bc28),
11745        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
11746        // (3f16e2f) by-borrow pins on the peer outer top-level
11747        // [`Caixa`] `Option<&str>`-return axes, and of the
11748        // per-`:placement`
11749        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11750        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
11751        // return axis, extended onto the fourth and final outer top-
11752        // level [`Caixa`] universal-axis `Option<&str>` shape — the
11753        // accessor's returned `&str` must borrow from `&self` (the
11754        // returned reference's lifetime is tied to `&self`), and
11755        // calling the accessor twice on the same [`Caixa`] must yield
11756        // the same `Option<&str>` verbatim (idempotent, no side
11757        // effects on `&self`).
11758        //
11759        // Pins against a future silent detour that returned an owned
11760        // `Option<String>` (which would type-check but silently
11761        // allocate on every call, breaking the zero-cost projection
11762        // every peer sibling accessor carries), or a one-arm-only
11763        // accessor that returned a saturating value on some sentinel
11764        // input (breaking the pass-through invariant the sibling
11765        // required-scalar accessors carry).
11766        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
11767            let c = caixa_with_edicao(edicao);
11768            let first = c.edicao();
11769            let second = c.edicao();
11770            assert_eq!(
11771                first, second,
11772                "Caixa::edicao must be idempotent — two successive \
11773                 calls on the same &self must return the same \
11774                 Option<&str>",
11775            );
11776            assert_eq!(
11777                first, edicao,
11778                "Caixa::edicao must return :edicao verbatim by \
11779                 borrow — got {first:?}, expected {edicao:?}",
11780            );
11781        }
11782    }
11783
11784    #[test]
11785    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
11786        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
11787        // label caixa-identity scalar pin: [`Caixa::nome`] must return
11788        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
11789        // the raw field access across every representative value in
11790        // the accept-set — the canonical `"demo"` template baseline
11791        // (the same `feira init`-scaffolded default the sibling
11792        // `validate_nome_accepts_canonical_template` positive-control
11793        // gate pins), plus every sibling per-typed-slot atom accessor's
11794        // canonical positive-arm byte-string (`"catalog"` per
11795        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
11796        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
11797        // `caixa-helm`/`caixa-flux` cross-crate integration-test
11798        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
11799        // canonical example), plus every past-the-guard sentinel for
11800        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
11801        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
11802        // the bare DNS-1123 63-byte cap but overflows the joint
11803        // `lareira-<nome>` chart-name budget the sibling
11804        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
11805        //
11806        // The past-the-guard sentinels pin the accessor doesn't
11807        // silently absorb the refusal cases into a template-derived
11808        // fallback (a future `.nome().is_empty().then(|| "demo")`
11809        // collapse would silently absorb the `NomeEmpty` refusal at
11810        // the accessor boundary and the validate gate would accept a
11811        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
11812        // catches that at caixa-core build time).
11813        //
11814        // First outer top-level [`Caixa`] `&str`-return required-
11815        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
11816        // required-scalar" projection pattern the sibling per-`Caixa`
11817        // `:versao` future lift folds on. Sibling in shape to the peer
11818        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
11819        // required-`String`-carry accessor pin on the sibling per-
11820        // sub-struct required-axis, extended onto the outer top-level
11821        // [`Caixa`] universal-axis required-`String`-carry axis.
11822        for nome in [
11823            "demo",
11824            "catalog",
11825            "cart",
11826            "hello-rio",
11827            "checkout",
11828            "",
11829            "Bad_Name",
11830            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
11831        ] {
11832            let c = caixa_with_nome(nome);
11833            assert_eq!(
11834                c.nome(),
11835                nome,
11836                "Caixa::nome must return :nome verbatim (got {}, \
11837                 expected {nome})",
11838                c.nome(),
11839            );
11840            assert_eq!(
11841                c.nome(),
11842                c.nome.as_str(),
11843                "Caixa::nome must byte-equal the raw .nome field \
11844                 access across every value in the String accept-set",
11845            );
11846        }
11847    }
11848
11849    #[test]
11850    fn validate_nome_empty_arm_routes_through_accessor() {
11851        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
11852        // key off [`Caixa::nome`], not the raw `.nome` field access.
11853        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
11854        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
11855        // template baseline (the peer positive-arm the sibling
11856        // `validate_nome_accepts_canonical_template` gate carves out)
11857        // must pass validate. The pair jointly pins the accessor +
11858        // validate-gate composition: any future silent detour that
11859        // had the accessor return a fresh `"demo"` on the empty arm
11860        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
11861        // would silently absorb the `NomeEmpty` refusal at the
11862        // accessor boundary and the validate gate would accept a
11863        // struct-literal `Caixa { nome: "".into(), .. }` — the
11864        // composition pin catches that at caixa-core build time.
11865        //
11866        // Peer of the sibling per-`Caixa`
11867        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
11868        // / `validate_repositorio_empty_arm_routes_through_accessor`
11869        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
11870        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
11871        // (2641cbd) composition pins on the sibling outer top-level
11872        // [`Caixa`] `Option<&str>` axes — same "the validate /
11873        // shape-gate predicate must route through the substrate-
11874        // primitive typed dispatch" discipline extended onto the peer
11875        // outer top-level [`Caixa`] required-`&str` composition axis.
11876        let c = caixa_with_nome("");
11877        assert!(
11878            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
11879            "validate_nome must reject nome == \"\" with NomeEmpty — \
11880             the accessor and the validate gate must route through the \
11881             same substrate-primitive typed dispatch on the :nome \
11882             empty-arm",
11883        );
11884        let c = caixa_with_nome("demo");
11885        assert!(
11886            c.validate_nome().is_ok(),
11887            "validate_nome must accept nome == \"demo\" (the canonical \
11888             DNS-1123-label template baseline)",
11889        );
11890    }
11891
11892    #[test]
11893    fn nome_projects_str_by_borrow() {
11894        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
11895        // — the `&str` borrows the underlying `String` storage of the
11896        // required `nome` slot and the accessor must not allocate a
11897        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
11898        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
11899        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
11900        // by-borrow pins on the peer outer top-level [`Caixa`]
11901        // `Option<&str>`-return axes, extended onto the first outer
11902        // top-level [`Caixa`] required-`&str`-return axis — the
11903        // accessor's returned `&str` must borrow from `&self` (the
11904        // returned reference's lifetime is tied to `&self`), and
11905        // calling the accessor twice on the same [`Caixa`] must yield
11906        // the same `&str` verbatim (idempotent, no side effects on
11907        // `&self`).
11908        //
11909        // Pins against a future silent detour that returned an owned
11910        // `String` (which would type-check but silently allocate on
11911        // every call, breaking the zero-cost projection every peer
11912        // sibling accessor carries), an accidental
11913        // `.nome.to_lowercase()` detour that returned a fresh
11914        // allocation through an already-DNS-1123-lowercase-only
11915        // string (breaking a future `const fn` regression), or a
11916        // one-arm-only accessor that returned a canonicalized value
11917        // on some sentinel input (breaking the pass-through invariant
11918        // the sibling required-scalar accessors carry).
11919        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
11920            let c = caixa_with_nome(nome);
11921            let first = c.nome();
11922            let second = c.nome();
11923            assert_eq!(
11924                first, second,
11925                "Caixa::nome must be idempotent — two successive calls \
11926                 on the same &self must return the same &str",
11927            );
11928            assert_eq!(
11929                first, nome,
11930                "Caixa::nome must return :nome verbatim by borrow — \
11931                 got {first}, expected {nome}",
11932            );
11933        }
11934    }
11935
11936    #[test]
11937    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
11938        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
11939        // pinned-version scalar pin: [`Caixa::versao`] must return the
11940        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
11941        // raw `.versao` field access across every representative value
11942        // in the accept-set — the canonical `"0.1.0"` template baseline
11943        // (the same `feira init`-scaffolded default the sibling
11944        // `validate_versao_accepts_canonical_template` positive-control
11945        // gate pins), plus every canonical SemVer-2 shape the sibling
11946        // `validate_versao_accepts_canonical_forms` positive-arm sweep
11947        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
11948        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
11949        // `"10.20.30"`), plus every past-the-guard sentinel for the
11950        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
11951        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
11952        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
11953        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
11954        // `"latest"` the docker-tag-shape footgun — the sentinels pin
11955        // the accessor doesn't silently absorb the refusal cases into a
11956        // template-derived fallback like `"0.1.0"`).
11957        //
11958        // The past-the-guard sentinels pin the accessor doesn't silently
11959        // absorb the refusal cases into a template-derived fallback (a
11960        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
11961        // silently absorb the `VersaoEmpty` refusal at the accessor
11962        // boundary and the validate gate would accept a struct-literal
11963        // `Caixa { versao: "".into(), .. }` — the pin catches that at
11964        // caixa-core build time).
11965        //
11966        // Second outer top-level [`Caixa`] `&str`-return required-scalar
11967        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
11968        // scalar" projection pattern the sibling per-`Caixa`
11969        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
11970        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
11971        // (4127bb6) / per-`:children`
11972        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
11973        // / per-`:upgrade-from`
11974        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
11975        // struct `:versao`-shaped `&str`-return accessor pins on the
11976        // sibling per-typed-slot version-carrier axes, extended onto the
11977        // second outer top-level [`Caixa`] universal-axis required-
11978        // `String`-carry axis so the two universal-axis identity-
11979        // carrying scalars every `defcaixa` form supplies (`:nome` +
11980        // `:versao`) share the same "one typed dispatch per axis" pin
11981        // discipline.
11982        for versao in [
11983            "0.1.0",
11984            "0.0.0",
11985            "1.0.0",
11986            "0.2.0-rc.1",
11987            "1.0.0-alpha.0",
11988            "1.0.0+build.42",
11989            "1.0.0-rc.1+build.42",
11990            "10.20.30",
11991            "",
11992            "v0.1.0",
11993            "0.1",
11994            "^0.1",
11995            "0.1.0.0",
11996            "latest",
11997        ] {
11998            let c = caixa_with_versao(versao);
11999            assert_eq!(
12000                c.versao(),
12001                versao,
12002                "Caixa::versao must return :versao verbatim (got {}, \
12003                 expected {versao})",
12004                c.versao(),
12005            );
12006            assert_eq!(
12007                c.versao(),
12008                c.versao.as_str(),
12009                "Caixa::versao must byte-equal the raw .versao field \
12010                 access across every value in the String accept-set",
12011            );
12012        }
12013    }
12014
12015    #[test]
12016    fn validate_versao_empty_arm_routes_through_accessor() {
12017        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
12018        // must key off [`Caixa::versao`], not the raw `.versao` field
12019        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
12020        // surface the `VersaoEmpty` refusal exactly, and the canonical
12021        // `"0.1.0"` template baseline (the peer positive-arm the sibling
12022        // `validate_versao_accepts_canonical_template` gate carves out)
12023        // must pass validate. The pair jointly pins the accessor +
12024        // validate-gate composition: any future silent detour that had
12025        // the accessor return a fresh `"0.1.0"` on the empty arm
12026        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
12027        // would silently absorb the `VersaoEmpty` refusal at the
12028        // accessor boundary and the validate gate would accept a
12029        // struct-literal `Caixa { versao: "".into(), .. }` — the
12030        // composition pin catches that at caixa-core build time.
12031        //
12032        // Peer of the sibling per-`Caixa`
12033        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
12034        // composition pin on the sibling outer top-level [`Caixa`]
12035        // required-`&str` universal-axis surface — same "the validate /
12036        // shape-gate predicate must route through the substrate-
12037        // primitive typed dispatch" discipline extended onto the peer
12038        // outer top-level [`Caixa`] required-`&str` universal-axis
12039        // pinned-version composition axis, closing the second
12040        // coordinate of the "one canonical typed dispatch per per-Caixa
12041        // required-`&str` universal-axis" discipline.
12042        let c = caixa_with_versao("");
12043        assert!(
12044            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
12045            "validate_versao must reject versao == \"\" with VersaoEmpty — \
12046             the accessor and the validate gate must route through the \
12047             same substrate-primitive typed dispatch on the :versao \
12048             empty-arm",
12049        );
12050        let c = caixa_with_versao("0.1.0");
12051        assert!(
12052            c.validate_versao().is_ok(),
12053            "validate_versao must accept versao == \"0.1.0\" (the \
12054             canonical SemVer-2 template baseline)",
12055        );
12056    }
12057
12058    #[test]
12059    fn versao_projects_str_by_borrow() {
12060        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
12061        // — the `&str` borrows the underlying `String` storage of the
12062        // required `versao` slot and the accessor must not allocate a
12063        // fresh `String` on every call. Peer of the [`Caixa::nome`]
12064        // (e6b7d97) by-borrow pin on the sibling outer top-level
12065        // [`Caixa`] required-`&str`-return axis, extended onto the
12066        // second outer top-level [`Caixa`] required-`&str`-return
12067        // universal-axis pinned-version surface — the accessor's
12068        // returned `&str` must borrow from `&self` (the returned
12069        // reference's lifetime is tied to `&self`), and calling the
12070        // accessor twice on the same [`Caixa`] must yield the same
12071        // `&str` verbatim (idempotent, no side effects on `&self`).
12072        //
12073        // Pins against a future silent detour that returned an owned
12074        // `String` (which would type-check but silently allocate on
12075        // every call, breaking the zero-cost projection every peer
12076        // sibling accessor carries), an accidental
12077        // `semver::Version::parse(&self.versao).unwrap().to_string()`
12078        // detour that returned a canonicalized fresh allocation through
12079        // an already-canonical byte-string (breaking a future `const fn`
12080        // regression and silently absorbing the `VersaoInvalid` refusal
12081        // at the accessor boundary), or a one-arm-only accessor that
12082        // returned a canonicalized value on some sentinel input
12083        // (breaking the pass-through invariant the sibling required-
12084        // scalar accessors carry).
12085        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
12086            let c = caixa_with_versao(versao);
12087            let first = c.versao();
12088            let second = c.versao();
12089            assert_eq!(
12090                first, second,
12091                "Caixa::versao must be idempotent — two successive \
12092                 calls on the same &self must return the same &str",
12093            );
12094            assert_eq!(
12095                first, versao,
12096                "Caixa::versao must return :versao verbatim by borrow \
12097                 — got {first}, expected {versao}",
12098            );
12099        }
12100    }
12101
12102    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
12103        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12104        c.kind = kind;
12105        c
12106    }
12107
12108    #[test]
12109    fn kind_returns_kind_variant_verbatim_across_permutations() {
12110        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
12111        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
12112        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
12113        // the raw `.kind` field access across every variant in the
12114        // closed accept-set (`Biblioteca` — the library kind that
12115        // exports lisp forms; `Binario` — the nix-built executable kind
12116        // under `exe/`; `Servico` — the wasm-component daemon kind
12117        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
12118        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
12119        // composition kind).
12120        //
12121        // Pins against a future silent detour that re-derived the kind
12122        // from a peer axis (an accidental fallback to
12123        // `if !servicos.is_empty() { Servico } else if
12124        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
12125        // collapse that read the code-surface / mesh-slot columns into
12126        // the kind discriminator), a variant remap the operator
12127        // authors on one consumer without the other, or a stale-derive
12128        // detour that substituted [`CaixaKind::Biblioteca`] as the
12129        // default when the field held any other variant (which would
12130        // silently collapse the distinction between "author explicitly
12131        // declared `:kind Servico`" and "author declared any other
12132        // kind" every downstream renderer-dispatch site depends on).
12133        //
12134        // First outer top-level [`Caixa`] `Copy`-return required-enum-
12135        // discriminant accessor pin — opens the "outer [`Caixa`]
12136        // `Copy`-return required-discriminant" projection pattern.
12137        // Sibling in shape to the peer per-`:supervisor`
12138        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
12139        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
12140        // (921fe1b), and per-`:children`
12141        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
12142        // `Copy`-return closed-set-enum discriminant accessor pins on
12143        // the sibling nested-spec typed-slot discriminator axes,
12144        // extended here to the outer top-level [`Caixa`] universal-
12145        // axis surface.
12146        for kind in [
12147            CaixaKind::Biblioteca,
12148            CaixaKind::Binario,
12149            CaixaKind::Servico,
12150            CaixaKind::Supervisor,
12151            CaixaKind::Aplicacao,
12152        ] {
12153            let c = caixa_with_kind(kind);
12154            assert_eq!(
12155                c.kind(),
12156                kind,
12157                "Caixa::kind must return :kind verbatim (got {:?}, \
12158                 expected {kind:?})",
12159                c.kind(),
12160            );
12161            assert_eq!(
12162                c.kind(),
12163                c.kind,
12164                "Caixa::kind accessor and .kind field access must \
12165                 byte-equal — the accessor is the substrate-primitive \
12166                 typed dispatch every downstream kind-gate consumer \
12167                 must route through",
12168            );
12169        }
12170    }
12171
12172    #[test]
12173    fn require_kind_reads_through_lifted_kind_accessor() {
12174        // Two-consumer coherence pin: the [`crate::render::require_kind`]
12175        // entry-gate predicate (the canonical two-line
12176        // `require_kind(caixa, Servico)?` prelude every per-Servico /
12177        // per-Aplicacao renderer runs at its entry-point) and the
12178        // sibling [`crate::render::KindMismatch`] error carrier's
12179        // `actual:` field (which names the offending caixa's variant
12180        // in the diagnostic) must both key off the lifted accessor, so
12181        // any future rebrand on the typed slot's reader shape lands at
12182        // exactly one place. Pins the two-site coherence by exercising
12183        // every off-diagonal `(actual, expected)` pair across the
12184        // closed accept-set — the `KindMismatch { actual, expected }`
12185        // surfaced on the mismatch arm must byte-equal the pair the
12186        // accessor returns for each side.
12187        //
12188        // Peer of the sibling per-`:placement`
12189        // `validate_placement_reads_through_lifted_estrategia_accessor`
12190        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
12191        // `Copy`-return discriminant axis — same "the entry-gate
12192        // predicate and the error carrier's `actual:` field must route
12193        // through the substrate-primitive typed dispatch" discipline
12194        // extended onto the outer top-level [`Caixa`] universal-axis
12195        // discriminant surface.
12196        for expected in [
12197            CaixaKind::Biblioteca,
12198            CaixaKind::Binario,
12199            CaixaKind::Servico,
12200            CaixaKind::Supervisor,
12201            CaixaKind::Aplicacao,
12202        ] {
12203            for actual in [
12204                CaixaKind::Biblioteca,
12205                CaixaKind::Binario,
12206                CaixaKind::Servico,
12207                CaixaKind::Supervisor,
12208                CaixaKind::Aplicacao,
12209            ] {
12210                let c = caixa_with_kind(actual);
12211                let result = crate::render::require_kind(&c, expected);
12212                if expected == actual {
12213                    assert!(
12214                        result.is_ok(),
12215                        "require_kind must accept when actual == expected \
12216                         (actual={actual:?}, expected={expected:?})",
12217                    );
12218                } else {
12219                    let err = result.expect_err("require_kind must reject when actual != expected");
12220                    assert_eq!(
12221                        err.actual,
12222                        c.kind(),
12223                        "KindMismatch.actual must byte-equal Caixa::kind() \
12224                         — the error carrier's `actual:` field reads \
12225                         through the lifted accessor",
12226                    );
12227                    assert_eq!(
12228                        err.expected, expected,
12229                        "KindMismatch.expected must byte-equal the \
12230                         expected variant passed to require_kind",
12231                    );
12232                }
12233            }
12234        }
12235    }
12236
12237    #[test]
12238    fn aplicacao_view_kind_gate_routes_through_accessor() {
12239        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
12240        // must key off [`Caixa::kind`], not the raw `.kind` field
12241        // access. Structurally: a `Caixa { kind: X, .. }` for any
12242        // non-`Aplicacao` variant must fold to `None` on the
12243        // `aplicacao_view` composer (the "kind mismatch → no typed
12244        // view" contract every downstream Aplicacao consumer keys off
12245        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
12246        // `Some(_)`. The pair jointly pins the accessor + view-gate
12247        // composition: any future silent detour that had the accessor
12248        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
12249        // input would silently absorb the kind-mismatch case at the
12250        // accessor boundary and every per-Aplicacao renderer would
12251        // silently render a non-Aplicacao caixa's mesh slots — the
12252        // composition pin catches that at caixa-core build time.
12253        //
12254        // Peer of the sibling per-`Caixa`
12255        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
12256        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
12257        // composition pins on the sibling outer top-level [`Caixa`]
12258        // required-`&str` universal-axis surfaces — same "the
12259        // composer / validate gate must route through the substrate-
12260        // primitive typed dispatch" discipline extended onto the
12261        // outer top-level [`Caixa`] `Copy`-return required-
12262        // discriminant composition axis.
12263        for kind in [
12264            CaixaKind::Biblioteca,
12265            CaixaKind::Binario,
12266            CaixaKind::Servico,
12267            CaixaKind::Supervisor,
12268        ] {
12269            let c = caixa_with_kind(kind);
12270            assert!(
12271                c.aplicacao_view().is_none(),
12272                "aplicacao_view must return None on non-Aplicacao \
12273                 kind {kind:?} — the composer's kind-gate must route \
12274                 through Caixa::kind()",
12275            );
12276        }
12277        let c = caixa_with_kind(CaixaKind::Aplicacao);
12278        assert!(
12279            c.aplicacao_view().is_some(),
12280            "aplicacao_view must return Some on kind Aplicacao — \
12281             the composer's kind-gate must accept the matching arm \
12282             through Caixa::kind()",
12283        );
12284    }
12285
12286    #[test]
12287    fn supervisor_view_kind_gate_routes_through_accessor() {
12288        // Composition pin (mirror of the sibling
12289        // `aplicacao_view_kind_gate_routes_through_accessor` on the
12290        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
12291        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
12292        // field access. A `Caixa { kind: X, .. }` for any non-
12293        // `Supervisor` variant must fold to `None` on the
12294        // `supervisor_view` composer, and a `Caixa { kind:
12295        // Supervisor, .. }` must fold to `Some(_)`. Same peer
12296        // composition pin discipline on the second `_view` composer
12297        // axis.
12298        for kind in [
12299            CaixaKind::Biblioteca,
12300            CaixaKind::Binario,
12301            CaixaKind::Servico,
12302            CaixaKind::Aplicacao,
12303        ] {
12304            let c = caixa_with_kind(kind);
12305            assert!(
12306                c.supervisor_view().is_none(),
12307                "supervisor_view must return None on non-Supervisor \
12308                 kind {kind:?} — the composer's kind-gate must route \
12309                 through Caixa::kind()",
12310            );
12311        }
12312        let mut c = caixa_with_kind(CaixaKind::Supervisor);
12313        // A Supervisor caixa needs a strategy + at least one child to
12314        // fold to a Some(_) that also validates; the composer itself
12315        // requires only the kind arm, so bare kind flip is enough to
12316        // pin the `Some(_)` return, but we populate the minimum
12317        // supervisor shape so a future strengthening of the composer
12318        // to reject an empty spec doesn't false-positive this pin.
12319        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
12320        c.children = vec![crate::supervisor::ChildSpec {
12321            caixa: "child".into(),
12322            versao: "^0.1".into(),
12323            restart: crate::supervisor::RestartPolicy::Permanent,
12324        }];
12325        assert!(
12326            c.supervisor_view().is_some(),
12327            "supervisor_view must return Some on kind Supervisor — \
12328             the composer's kind-gate must accept the matching arm \
12329             through Caixa::kind()",
12330        );
12331    }
12332
12333    #[test]
12334    fn kind_projects_by_copy() {
12335        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
12336        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
12337        // `&self` (the returned value is owned, `Copy`-projected from
12338        // the underlying [`CaixaKind`] storage; two calls on the same
12339        // [`Caixa`] must yield byte-equal values). Peer of the peer
12340        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
12341        // `SupervisorSpec::estrategia` / per-`:children`
12342        // `ChildSpec::restart` `Copy`-return discriminant accessor
12343        // pins on the sibling nested-spec typed-slot discriminator
12344        // axes, extended onto the first outer top-level [`Caixa`]
12345        // required-`Copy`-return axis — pins against a future silent
12346        // detour that returned `&CaixaKind` (which would type-check
12347        // but silently constrain every consumer's callsite to a
12348        // borrow-shaped dispatch, breaking the zero-cost `Copy`
12349        // projection every peer sibling accessor carries).
12350        for kind in [
12351            CaixaKind::Biblioteca,
12352            CaixaKind::Binario,
12353            CaixaKind::Servico,
12354            CaixaKind::Supervisor,
12355            CaixaKind::Aplicacao,
12356        ] {
12357            let c = caixa_with_kind(kind);
12358            let first: CaixaKind = c.kind();
12359            let second: CaixaKind = c.kind();
12360            assert_eq!(
12361                first, second,
12362                "Caixa::kind must be idempotent — two successive \
12363                 calls on the same &self must return the same \
12364                 CaixaKind variant",
12365            );
12366            assert_eq!(
12367                first, kind,
12368                "Caixa::kind must return :kind verbatim by Copy — \
12369                 got {first:?}, expected {kind:?}",
12370            );
12371        }
12372    }
12373
12374    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
12375
12376    #[test]
12377    fn autores_returns_autores_slice_verbatim_across_permutations() {
12378        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
12379        // name-list slice pin: [`Caixa::autores`] must return the
12380        // `:autores` typed [`Vec<String>`] list verbatim as a
12381        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
12382        // access across every representative value in the accept-set —
12383        // `[]` (the "no maintainers declared" arm every existing
12384        // fixture without an `:autores` line carries), `[""]` (a past-
12385        // the-guard sentinel that pins the accessor doesn't perform a
12386        // silent `[""] → []` collapse on the empty-entry arm — validate
12387        // rejects `[""]` through `AutorEmpty` but the accessor must
12388        // ship the raw slot verbatim so a validate-time gate regression
12389        // surfaces at the caixa-helm emit boundary rather than being
12390        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
12391        // canonical single-maintainer form every `feira init` template
12392        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
12393        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
12394        // (the canonical RFC-5322 `<name> <email>` form the
12395        // `is_chart_maintainer_name_shape` predicate accepts), and
12396        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
12397        // sentinel — validate rejects through `AutorDuplicate` but the
12398        // accessor must ship the raw slot verbatim).
12399        //
12400        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
12401        // pin on the substrate primitive — opens the "outer [`Caixa`]
12402        // `&[T]` slice" projection pattern the sibling per-`Caixa`
12403        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
12404        // / `:servicos` / `:upgrade-from` / `:children` future lifts
12405        // fold on. Sibling in shape to the peer per-`:supervisor`
12406        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
12407        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
12408        // (a6e18d7), per-`:membros`
12409        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
12410        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
12411        // (0dcc926), and per-`:upgrade-from :instructions`
12412        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
12413        // `&[T]`-return slice accessor pins on the sibling per-M2 /
12414        // per-M3 typed-slot list axes, extended onto the outer top-
12415        // level [`Caixa`] universal-axis surface. Pins against a future
12416        // silent detour that returned an owned `Vec<String>` (which
12417        // would type-check but silently clone on every accessor call,
12418        // breaking the zero-cost projection every peer sibling slice
12419        // accessor carries), a `[""] → []` collapse (which would
12420        // silently absorb the `AutorEmpty` refusal case at the accessor
12421        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
12422        // would silently absorb the `AutorDuplicate` refusal case at
12423        // the accessor boundary and the caixa-helm `maintainers:` fold
12424        // would silently render a dedupped list on a struct-literal
12425        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
12426        for autores in [
12427            vec![],
12428            vec![""],
12429            vec!["pleme-io"],
12430            vec!["alice", "bob"],
12431            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
12432            vec!["pleme-io", "pleme-io"],
12433        ] {
12434            let c = caixa_with_autores(autores.clone());
12435            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
12436            assert_eq!(
12437                c.autores(),
12438                expected.as_slice(),
12439                "Caixa::autores must return :autores verbatim (got {:?}, \
12440                 expected {expected:?})",
12441                c.autores(),
12442            );
12443            assert_eq!(
12444                c.autores(),
12445                c.autores.as_slice(),
12446                "Caixa::autores must byte-equal the raw \
12447                 `self.autores.as_slice()` field access across every \
12448                 value in the Vec<String> accept-set",
12449            );
12450        }
12451    }
12452
12453    #[test]
12454    fn validate_autores_empty_entry_arm_routes_through_accessor() {
12455        // Composition pin: [`Caixa::validate_autores`]'s per-entry
12456        // empty-arm gate must key off [`Caixa::autores`], not the raw
12457        // `&self.autores` field-borrow walk. Structurally: a
12458        // `Caixa { autores: vec!["".into()], .. }` must surface the
12459        // `AutorEmpty` refusal exactly, and a
12460        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
12461        // canonical single-maintainer form) must pass validate. The
12462        // pair jointly pins the accessor + validate-gate composition:
12463        // any future silent detour that had the accessor return an
12464        // empty slice on the `[""]` arm (a
12465        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
12466        // would silently absorb the `AutorEmpty` refusal at the
12467        // accessor boundary and the validate gate would accept a
12468        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
12469        // the composition pin catches that at caixa-core build time.
12470        //
12471        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
12472        // accessor-composition pin
12473        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
12474        // sibling `Option<&str>`-composition axis and the
12475        // per-`:politicas :circuit-breaker`
12476        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
12477        // accessor-composition pin
12478        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
12479        // on the sibling required-`u32`-composition axis — same "the
12480        // validate / shape-gate predicate must route through the
12481        // substrate-primitive typed dispatch" discipline extended onto
12482        // the outer top-level [`Caixa`] universal-axis `&[T]`-
12483        // composition surface.
12484        let c = caixa_with_autores(vec![""]);
12485        assert!(
12486            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
12487            "validate_autores must reject autores == vec![\"\"] with \
12488             AutorEmpty — the accessor and the validate gate must \
12489             route through the same substrate-primitive typed dispatch \
12490             on the :autores per-entry empty arm",
12491        );
12492        let c = caixa_with_autores(vec!["pleme-io"]);
12493        assert!(
12494            c.validate_autores().is_ok(),
12495            "validate_autores must accept autores == vec![\"pleme-io\"] \
12496             (the canonical single-maintainer shape every `feira init` \
12497             template scaffolds)",
12498        );
12499    }
12500
12501    #[test]
12502    fn autores_projects_slice_by_borrow() {
12503        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
12504        // borrow — the returned slice borrows the underlying
12505        // `Vec<String>` storage of the `:autores` slot and the
12506        // accessor must not clone the backing `Vec` on every call.
12507        // Peer of the per-`:membros`
12508        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
12509        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
12510        // (0dcc926) / per-`:placement`
12511        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
12512        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
12513        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
12514        // typed-slot `&[T]`-return axes, extended onto the outer top-
12515        // level [`Caixa`] universal-axis `&[String]` shape — the
12516        // accessor's returned slice must borrow from `&self` (the
12517        // returned reference's lifetime is tied to `&self`), and
12518        // calling the accessor twice on the same [`Caixa`] must yield
12519        // slices that are pointer-equal (the underlying byte-buffer is
12520        // the storage `Vec`'s allocation, not a fresh copy) as well as
12521        // value-equal (idempotent, no side effects on `&self`).
12522        //
12523        // Pins against a future silent detour that returned an owned
12524        // `Vec<String>` (which would type-check but silently clone on
12525        // every call, breaking the zero-cost projection every peer
12526        // sibling slice accessor carries), a `&Vec<String>` return
12527        // (which would leak the backing `Vec`'s grow/push/reserve
12528        // surface no downstream consumer reaches for), or a one-arm-
12529        // only accessor that returned a saturating value on some
12530        // sentinel input (breaking the pass-through invariant the
12531        // sibling slice accessors carry).
12532        for autores in [
12533            vec![],
12534            vec!["pleme-io"],
12535            vec!["alice", "bob"],
12536            vec!["pleme-io", "pleme-io"],
12537        ] {
12538            let c = caixa_with_autores(autores.clone());
12539            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
12540            let first = c.autores();
12541            let second = c.autores();
12542            assert_eq!(
12543                first, second,
12544                "Caixa::autores must be idempotent — two successive \
12545                 calls on the same &self must return the same \
12546                 &[String]",
12547            );
12548            assert_eq!(
12549                first.as_ptr(),
12550                second.as_ptr(),
12551                "Caixa::autores must borrow the underlying Vec<String> \
12552                 storage — two successive calls must return slices \
12553                 with the same backing pointer (a fresh Vec<String> \
12554                 clone would change the pointer on every call)",
12555            );
12556            assert_eq!(
12557                first,
12558                expected.as_slice(),
12559                "Caixa::autores must return :autores verbatim by \
12560                 borrow — got {first:?}, expected {expected:?}",
12561            );
12562        }
12563    }
12564
12565    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
12566
12567    #[test]
12568    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
12569        // The canonical per-`Caixa` `:etiquetas` universal-axis
12570        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
12571        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
12572        // as a `&[String]`, byte-equal to the raw
12573        // `self.etiquetas.as_slice()` access across every representative
12574        // value in the accept-set — `[]` (the "no tags declared" arm
12575        // every existing fixture without an `:etiquetas` line carries),
12576        // `[""]` (a past-the-guard sentinel that pins the accessor
12577        // doesn't perform a silent `[""] → []` collapse on the empty-
12578        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
12579        // but the accessor must ship the raw slot verbatim so a
12580        // validate-time gate regression surfaces at the caixa-helm emit
12581        // boundary rather than being silently absorbed into a keyword-
12582        // drop), `["demo"]` (the canonical single-tag form every
12583        // `feira init` template scaffolds), `["example", "aplicacao",
12584        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
12585        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
12586        // (a past-the-guard duplicate sentinel — validate rejects
12587        // through `EtiquetaDuplicate` but the accessor must ship the
12588        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
12589        // at chart-render time isn't silently promoted into the
12590        // accessor boundary and struct-literal
12591        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
12592        // fixtures continue to expose the duplicate at the accessor).
12593        //
12594        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
12595        // pin on the substrate primitive — folds on the "outer
12596        // [`Caixa`] `&[T]` slice" projection pattern
12597        // `autores_returns_autores_slice_verbatim_across_permutations`
12598        // (b5d813f) opened, sibling in shape and idiom. Pins against a
12599        // future silent detour that returned an owned `Vec<String>`
12600        // (which would type-check but silently clone on every accessor
12601        // call, breaking the zero-cost projection every peer sibling
12602        // slice accessor carries), a `[""] → []` collapse (which would
12603        // silently absorb the `EtiquetaEmpty` refusal case at the
12604        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
12605        // (which would silently absorb the `EtiquetaDuplicate` refusal
12606        // case at the accessor boundary — the caixa-helm chart-render
12607        // `BTreeSet::collect` dedup is downstream of the accessor and
12608        // must not be silently promoted into it).
12609        for etiquetas in [
12610            vec![],
12611            vec![""],
12612            vec!["demo"],
12613            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
12614            vec!["demo", "demo"],
12615        ] {
12616            let c = caixa_with_etiquetas(etiquetas.clone());
12617            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
12618            assert_eq!(
12619                c.etiquetas(),
12620                expected.as_slice(),
12621                "Caixa::etiquetas must return :etiquetas verbatim (got \
12622                 {:?}, expected {expected:?})",
12623                c.etiquetas(),
12624            );
12625            assert_eq!(
12626                c.etiquetas(),
12627                c.etiquetas.as_slice(),
12628                "Caixa::etiquetas must byte-equal the raw \
12629                 `self.etiquetas.as_slice()` field access across every \
12630                 value in the Vec<String> accept-set",
12631            );
12632        }
12633    }
12634
12635    #[test]
12636    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
12637        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
12638        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
12639        // `&self.etiquetas` field-borrow walk. Structurally: a
12640        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
12641        // `EtiquetaEmpty` refusal exactly, and a
12642        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
12643        // single-tag form) must pass validate. The pair jointly pins
12644        // the accessor + validate-gate composition: any future silent
12645        // detour that had the accessor return an empty slice on the
12646        // `[""]` arm (a
12647        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
12648        // silently absorb the `EtiquetaEmpty` refusal at the accessor
12649        // boundary and the validate gate would accept a struct-literal
12650        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
12651        // pin catches that at caixa-core build time.
12652        //
12653        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
12654        // through_accessor` (b5d813f) accessor-composition pin on the
12655        // sibling `&[T]`-composition axis — same "the validate / shape-
12656        // gate predicate must route through the substrate-primitive
12657        // typed dispatch" discipline extended onto the sibling outer
12658        // top-level [`Caixa`] `&[T]`-composition surface.
12659        let c = caixa_with_etiquetas(vec![""]);
12660        assert!(
12661            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
12662            "validate_etiquetas must reject etiquetas == vec![\"\"] \
12663             with EtiquetaEmpty — the accessor and the validate gate \
12664             must route through the same substrate-primitive typed \
12665             dispatch on the :etiquetas per-entry empty arm",
12666        );
12667        let c = caixa_with_etiquetas(vec!["demo"]);
12668        assert!(
12669            c.validate_etiquetas().is_ok(),
12670            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
12671             (the canonical single-tag shape every `feira init` \
12672             template scaffolds)",
12673        );
12674    }
12675
12676    #[test]
12677    fn etiquetas_projects_slice_by_borrow() {
12678        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
12679        // by borrow — the returned slice borrows the underlying
12680        // `Vec<String>` storage of the `:etiquetas` slot and the
12681        // accessor must not clone the backing `Vec` on every call.
12682        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12683        // (b5d813f) by-borrow pin on the sibling outer top-level
12684        // [`Caixa`] `&[String]`-return axis — the accessor's returned
12685        // slice must borrow from `&self` (the returned reference's
12686        // lifetime is tied to `&self`), and calling the accessor twice
12687        // on the same [`Caixa`] must yield slices that are pointer-
12688        // equal (the underlying byte-buffer is the storage `Vec`'s
12689        // allocation, not a fresh copy) as well as value-equal
12690        // (idempotent, no side effects on `&self`).
12691        //
12692        // Pins against a future silent detour that returned an owned
12693        // `Vec<String>` (which would type-check but silently clone on
12694        // every call, breaking the zero-cost projection every peer
12695        // sibling slice accessor carries), a `&Vec<String>` return
12696        // (which would leak the backing `Vec`'s grow/push/reserve
12697        // surface no downstream consumer reaches for), or a one-arm-
12698        // only accessor that returned a saturating value on some
12699        // sentinel input (breaking the pass-through invariant the
12700        // sibling slice accessors carry).
12701        for etiquetas in [
12702            vec![],
12703            vec!["demo"],
12704            vec!["example", "aplicacao", "mesh"],
12705            vec!["demo", "demo"],
12706        ] {
12707            let c = caixa_with_etiquetas(etiquetas.clone());
12708            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
12709            let first = c.etiquetas();
12710            let second = c.etiquetas();
12711            assert_eq!(
12712                first, second,
12713                "Caixa::etiquetas must be idempotent — two successive \
12714                 calls on the same &self must return the same \
12715                 &[String]",
12716            );
12717            assert_eq!(
12718                first.as_ptr(),
12719                second.as_ptr(),
12720                "Caixa::etiquetas must borrow the underlying \
12721                 Vec<String> storage — two successive calls must \
12722                 return slices with the same backing pointer (a fresh \
12723                 Vec<String> clone would change the pointer on every \
12724                 call)",
12725            );
12726            assert_eq!(
12727                first,
12728                expected.as_slice(),
12729                "Caixa::etiquetas must return :etiquetas verbatim by \
12730                 borrow — got {first:?}, expected {expected:?}",
12731            );
12732        }
12733    }
12734
12735    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
12736
12737    #[test]
12738    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
12739        // The canonical per-`Caixa` `:bibliotecas` universal-axis
12740        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
12741        // must return the `:bibliotecas` typed [`Vec<String>`] list
12742        // verbatim as a `&[String]`, byte-equal to the raw
12743        // `self.bibliotecas.as_slice()` access across every
12744        // representative value in the accept-set — `[]` (the "no
12745        // libraries declared" arm every `:kind` other than `Biblioteca`
12746        // + every `Biblioteca` relying on the canonical
12747        // `lib/<nome>.lisp` implicit-default path carries; the
12748        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
12749        // fires exactly on this empty-slot + `Biblioteca`-kind
12750        // combination), `[""]` (a past-the-guard sentinel that pins
12751        // the accessor doesn't perform a silent `[""] → []` collapse
12752        // on the empty-entry arm — validate rejects `[""]` through
12753        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
12754        // must ship the raw slot verbatim so a validate-time gate
12755        // regression surfaces at the `feira build` phase-1 parse
12756        // boundary rather than being silently absorbed into a
12757        // library-drop), `["lib/demo.lisp"]` (the canonical single-
12758        // entry form `Caixa::template` scaffolds and every `feira init`
12759        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
12760        // (the canonical multi-library form the
12761        // `validate_code_paths_accepts_explicit_relative_paths_on_
12762        // every_slot` fixture emits), and `["lib/foo.lisp",
12763        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
12764        // validate rejects through `CodePathDuplicate { slot:
12765        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
12766        // but the accessor must ship the raw slot verbatim so the
12767        // `feira build` `for entry in caixa.bibliotecas()` parse walk
12768        // sees the duplicate at the accessor boundary and struct-
12769        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
12770        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
12771        // the duplicate at the accessor).
12772        //
12773        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
12774        // pin on the substrate primitive — folds on the "outer
12775        // [`Caixa`] `&[T]` slice" projection pattern
12776        // `autores_returns_autores_slice_verbatim_across_permutations`
12777        // (b5d813f) opened and
12778        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12779        // (78c7d3c) folded on, sibling in shape and idiom. Pins
12780        // against a future silent detour that returned an owned
12781        // `Vec<String>` (which would type-check but silently clone on
12782        // every accessor call, breaking the zero-cost projection
12783        // every peer sibling slice accessor carries), a `[""] → []`
12784        // collapse (which would silently absorb the `CodePathEmpty`
12785        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
12786        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
12787        // would silently absorb the `CodePathDuplicate` refusal case
12788        // at the accessor boundary — the per-slot set-not-multiset
12789        // gate is downstream of the accessor and must not be silently
12790        // promoted into it).
12791        for bibliotecas in [
12792            vec![],
12793            vec![""],
12794            vec!["lib/demo.lisp"],
12795            vec!["lib/demo.lisp", "lib/helpers.lisp"],
12796            vec!["lib/foo.lisp", "lib/foo.lisp"],
12797        ] {
12798            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
12799            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
12800            assert_eq!(
12801                c.bibliotecas(),
12802                expected.as_slice(),
12803                "Caixa::bibliotecas must return :bibliotecas verbatim \
12804                 (got {:?}, expected {expected:?})",
12805                c.bibliotecas(),
12806            );
12807            assert_eq!(
12808                c.bibliotecas(),
12809                c.bibliotecas.as_slice(),
12810                "Caixa::bibliotecas must byte-equal the raw \
12811                 `self.bibliotecas.as_slice()` field access across \
12812                 every value in the Vec<String> accept-set",
12813            );
12814        }
12815    }
12816
12817    #[test]
12818    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
12819        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12820        // empty-arm gate on the `:bibliotecas` slot must key off
12821        // [`Caixa::bibliotecas`], not a divergent raw
12822        // `&self.bibliotecas` field-borrow walk. Structurally: a
12823        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
12824        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
12825        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
12826        // into()], .. }` (the canonical single-library form
12827        // `Caixa::template` scaffolds) must pass validate. The pair
12828        // jointly pins the accessor + validate-gate composition: any
12829        // future silent detour that had the accessor return an empty
12830        // slice on the `[""]` arm (a `.iter().filter(|s|
12831        // !s.is_empty()).collect()` collapse) would silently absorb
12832        // the `CodePathEmpty` refusal at the accessor boundary and
12833        // the validate gate would accept a struct-literal
12834        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
12835        // composition pin catches that at caixa-core build time.
12836        //
12837        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
12838        // through_accessor` (b5d813f) and
12839        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12840        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12841        // composition axes — same "the validate / shape-gate
12842        // predicate must route through the substrate-primitive typed
12843        // dispatch" discipline extended onto the sibling outer top-
12844        // level [`Caixa`] `&[T]`-composition surface. Nominally the
12845        // in-tree `validate_code_paths` production body still keys
12846        // off the internal `[(":bibliotecas", &self.bibliotecas,
12847        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12848        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12849        // (the tuple's homogeneous slice-typed shape blocks a per-
12850        // element accessor swap in isolation — a future companion
12851        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
12852        // `&[T]` slice-accessor axis closes that tuple onto the
12853        // triple of typed dispatches as a unit); the composition pin
12854        // catches any future accessor-side silent filter drop against
12855        // that eventual tuple-closure regardless of whether the
12856        // `:bibliotecas` slot is threaded through the accessor or the
12857        // raw field access at the tuple's construction site.
12858        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
12859        assert!(
12860            matches!(
12861                c.validate_code_paths(),
12862                Err(ManifestError::CodePathEmpty {
12863                    slot: ":bibliotecas"
12864                })
12865            ),
12866            "validate_code_paths must reject bibliotecas == vec![\"\"] \
12867             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
12868             accessor and the validate gate must route through the \
12869             same substrate-primitive typed dispatch on the \
12870             :bibliotecas per-entry empty arm",
12871        );
12872        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
12873        assert!(
12874            c.validate_code_paths().is_ok(),
12875            "validate_code_paths must accept bibliotecas == \
12876             vec![\"lib/demo.lisp\"] (the canonical single-library \
12877             shape every `feira init` template scaffolds)",
12878        );
12879    }
12880
12881    #[test]
12882    fn bibliotecas_projects_slice_by_borrow() {
12883        // The by-borrow pin: [`Caixa::bibliotecas`] returns
12884        // `&[String]` by borrow — the returned slice borrows the
12885        // underlying `Vec<String>` storage of the `:bibliotecas` slot
12886        // and the accessor must not clone the backing `Vec` on every
12887        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12888        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
12889        // by-borrow pins on the sibling outer top-level [`Caixa`]
12890        // `&[String]`-return axes — the accessor's returned slice
12891        // must borrow from `&self` (the returned reference's lifetime
12892        // is tied to `&self`), and calling the accessor twice on the
12893        // same [`Caixa`] must yield slices that are pointer-equal
12894        // (the underlying byte-buffer is the storage `Vec`'s
12895        // allocation, not a fresh copy) as well as value-equal
12896        // (idempotent, no side effects on `&self`).
12897        //
12898        // Pins against a future silent detour that returned an owned
12899        // `Vec<String>` (which would type-check but silently clone on
12900        // every call, breaking the zero-cost projection every peer
12901        // sibling slice accessor carries), a `&Vec<String>` return
12902        // (which would leak the backing `Vec`'s grow/push/reserve
12903        // surface no downstream consumer reaches for), or a one-arm-
12904        // only accessor that returned a saturating value on some
12905        // sentinel input (breaking the pass-through invariant the
12906        // sibling slice accessors carry).
12907        for bibliotecas in [
12908            vec![],
12909            vec!["lib/demo.lisp"],
12910            vec!["lib/demo.lisp", "lib/helpers.lisp"],
12911            vec!["lib/foo.lisp", "lib/foo.lisp"],
12912        ] {
12913            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
12914            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
12915            let first = c.bibliotecas();
12916            let second = c.bibliotecas();
12917            assert_eq!(
12918                first, second,
12919                "Caixa::bibliotecas must be idempotent — two \
12920                 successive calls on the same &self must return the \
12921                 same &[String]",
12922            );
12923            assert_eq!(
12924                first.as_ptr(),
12925                second.as_ptr(),
12926                "Caixa::bibliotecas must borrow the underlying \
12927                 Vec<String> storage — two successive calls must \
12928                 return slices with the same backing pointer (a \
12929                 fresh Vec<String> clone would change the pointer on \
12930                 every call)",
12931            );
12932            assert_eq!(
12933                first,
12934                expected.as_slice(),
12935                "Caixa::bibliotecas must return :bibliotecas verbatim \
12936                 by borrow — got {first:?}, expected {expected:?}",
12937            );
12938        }
12939    }
12940
12941    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
12942
12943    #[test]
12944    fn exe_returns_exe_slice_verbatim_across_permutations() {
12945        // The canonical per-`Caixa` `:exe` universal-axis
12946        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
12947        // must return the `:exe` typed [`Vec<String>`] list verbatim as
12948        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
12949        // access across every representative value in the accept-set —
12950        // `[]` (the "no executable declared" arm every `:kind` other
12951        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
12952        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
12953        // + `Binario`-kind combination), `[""]` (a past-the-guard
12954        // sentinel that pins the accessor doesn't perform a silent
12955        // `[""] → []` collapse on the empty-entry arm — validate rejects
12956        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
12957        // accessor must ship the raw slot verbatim so a validate-time
12958        // gate regression surfaces at the layout / `feira nix` boundary
12959        // rather than being silently absorbed into an executable-drop),
12960        // `["exe/cli"]` (the canonical single-entry Binario form every
12961        // in-tree `caixa_with_code_paths` positive control uses),
12962        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
12963        // form the `validate_code_paths_accepts_explicit_relative_paths_
12964        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
12965        // (a past-the-guard duplicate sentinel — validate rejects
12966        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
12967        // set-not-multiset gate, but the accessor must ship the raw
12968        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
12969        // into(), "exe/cli".into()], .. }` fixtures continue to expose
12970        // the duplicate at the accessor).
12971        //
12972        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
12973        // pin on the substrate primitive — folds on the "outer
12974        // [`Caixa`] `&[T]` slice" projection pattern
12975        // `autores_returns_autores_slice_verbatim_across_permutations`
12976        // (b5d813f) opened,
12977        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12978        // (78c7d3c) folded on, and
12979        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12980        // (8a36c23) closed the universal-axis text-tag family of.
12981        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
12982        // the sibling `:servicos` future lift closes onto. Pins against
12983        // a future silent detour that returned an owned `Vec<String>`
12984        // (which would type-check but silently clone on every accessor
12985        // call, breaking the zero-cost projection every peer sibling
12986        // slice accessor carries), a `[""] → []` collapse (which would
12987        // silently absorb the `CodePathEmpty` refusal case at the
12988        // accessor boundary), or an `["exe/cli", "exe/cli"] →
12989        // ["exe/cli"]` dedup collapse (which would silently absorb the
12990        // `CodePathDuplicate` refusal case at the accessor boundary —
12991        // the per-slot set-not-multiset gate is downstream of the
12992        // accessor and must not be silently promoted into it).
12993        for exe in [
12994            vec![],
12995            vec![""],
12996            vec!["exe/cli"],
12997            vec!["exe/cli", "exe/serve"],
12998            vec!["exe/cli", "exe/cli"],
12999        ] {
13000            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
13001            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
13002            assert_eq!(
13003                c.exe(),
13004                expected.as_slice(),
13005                "Caixa::exe must return :exe verbatim (got {:?}, \
13006                 expected {expected:?})",
13007                c.exe(),
13008            );
13009            assert_eq!(
13010                c.exe(),
13011                c.exe.as_slice(),
13012                "Caixa::exe must byte-equal the raw \
13013                 `self.exe.as_slice()` field access across every value \
13014                 in the Vec<String> accept-set",
13015            );
13016        }
13017    }
13018
13019    #[test]
13020    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
13021        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
13022        // empty-arm gate on the `:exe` slot must key off
13023        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
13024        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
13025        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
13026        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
13027        // (the canonical single-executable form every in-tree
13028        // `caixa_with_code_paths` positive control uses) must pass
13029        // validate. The pair jointly pins the accessor + validate-gate
13030        // composition: any future silent detour that had the accessor
13031        // return an empty slice on the `[""]` arm (a
13032        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
13033        // silently absorb the `CodePathEmpty` refusal at the accessor
13034        // boundary and the validate gate would accept a struct-literal
13035        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
13036        // catches that at caixa-core build time.
13037        //
13038        // Peer of the per-`Caixa`
13039        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
13040        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
13041        // (b5d813f), and
13042        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13043        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
13044        // composition axes — same "the validate / shape-gate predicate
13045        // must route through the substrate-primitive typed dispatch"
13046        // discipline extended onto the sibling outer top-level [`Caixa`]
13047        // `&[T]`-composition surface. Nominally the in-tree
13048        // `validate_code_paths` production body still keys off the
13049        // internal `[(":bibliotecas", &self.bibliotecas,
13050        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
13051        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
13052        // (the tuple's homogeneous slice-typed shape blocks a per-
13053        // element accessor swap in isolation — a future companion lift
13054        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
13055        // accessor axis closes that tuple onto the triple of typed
13056        // dispatches as a unit); the composition pin catches any future
13057        // accessor-side silent filter drop against that eventual tuple-
13058        // closure regardless of whether the `:exe` slot is threaded
13059        // through the accessor or the raw field access at the tuple's
13060        // construction site.
13061        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
13062        assert!(
13063            matches!(
13064                c.validate_code_paths(),
13065                Err(ManifestError::CodePathEmpty { slot: ":exe" })
13066            ),
13067            "validate_code_paths must reject exe == vec![\"\"] \
13068             with CodePathEmpty {{ slot: \":exe\" }} — the \
13069             accessor and the validate gate must route through the \
13070             same substrate-primitive typed dispatch on the \
13071             :exe per-entry empty arm",
13072        );
13073        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
13074        assert!(
13075            c.validate_code_paths().is_ok(),
13076            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
13077             (the canonical single-executable shape every in-tree \
13078             `caixa_with_code_paths` positive control uses)",
13079        );
13080    }
13081
13082    #[test]
13083    fn exe_projects_slice_by_borrow() {
13084        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
13085        // borrow — the returned slice borrows the underlying
13086        // `Vec<String>` storage of the `:exe` slot and the accessor
13087        // must not clone the backing `Vec` on every call. Peer of the
13088        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
13089        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
13090        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
13091        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
13092        // return axes — the accessor's returned slice must borrow from
13093        // `&self` (the returned reference's lifetime is tied to
13094        // `&self`), and calling the accessor twice on the same
13095        // [`Caixa`] must yield slices that are pointer-equal (the
13096        // underlying byte-buffer is the storage `Vec`'s allocation,
13097        // not a fresh copy) as well as value-equal (idempotent, no
13098        // side effects on `&self`).
13099        //
13100        // Pins against a future silent detour that returned an owned
13101        // `Vec<String>` (which would type-check but silently clone on
13102        // every call, breaking the zero-cost projection every peer
13103        // sibling slice accessor carries), a `&Vec<String>` return
13104        // (which would leak the backing `Vec`'s grow/push/reserve
13105        // surface no downstream consumer reaches for), or a one-arm-
13106        // only accessor that returned a saturating value on some
13107        // sentinel input (breaking the pass-through invariant the
13108        // sibling slice accessors carry).
13109        for exe in [
13110            vec![],
13111            vec!["exe/cli"],
13112            vec!["exe/cli", "exe/serve"],
13113            vec!["exe/cli", "exe/cli"],
13114        ] {
13115            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
13116            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
13117            let first = c.exe();
13118            let second = c.exe();
13119            assert_eq!(
13120                first, second,
13121                "Caixa::exe must be idempotent — two successive calls \
13122                 on the same &self must return the same &[String]",
13123            );
13124            assert_eq!(
13125                first.as_ptr(),
13126                second.as_ptr(),
13127                "Caixa::exe must borrow the underlying Vec<String> \
13128                 storage — two successive calls must return slices \
13129                 with the same backing pointer (a fresh Vec<String> \
13130                 clone would change the pointer on every call)",
13131            );
13132            assert_eq!(
13133                first,
13134                expected.as_slice(),
13135                "Caixa::exe must return :exe verbatim by borrow — \
13136                 got {first:?}, expected {expected:?}",
13137            );
13138        }
13139    }
13140
13141    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
13142
13143    #[test]
13144    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
13145        // The canonical per-`Caixa` `:servicos` universal-axis
13146        // ComputeUnit-CR-YAML-entry-path-list slice pin:
13147        // [`Caixa::servicos`] must return the `:servicos` typed
13148        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
13149        // the raw `self.servicos.as_slice()` access across every
13150        // representative value in the accept-set — `[]` (the "no
13151        // ComputeUnit-CR declared" arm every `:kind` other than
13152        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
13153        // `ServicoWithoutServicos` arm-gate fires exactly on this
13154        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
13155        // guard sentinel that pins the accessor doesn't perform a
13156        // silent `[""] → []` collapse on the empty-entry arm — validate
13157        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
13158        // but the accessor must ship the raw slot verbatim so a
13159        // validate-time gate regression surfaces at the layout /
13160        // per-Servico renderer boundary rather than being silently
13161        // absorbed into a component-drop),
13162        // `["servicos/demo.computeunit.yaml"]` (the canonical
13163        // singleton V0-shape every in-tree `caixa_with_code_paths`
13164        // positive control uses; the same shape
13165        // [`crate::require_single_servico`] admits),
13166        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
13167        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
13168        // singularity gate rejects through `ServicoCountMismatch
13169        // { count: 2 }` but the accessor must ship the raw slot
13170        // verbatim so struct-literal `Caixa { servicos: vec![...,
13171        // ...], .. }` fixtures continue to expose the count at the
13172        // accessor), and `["servicos/a.computeunit.yaml",
13173        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
13174        // sentinel — validate rejects through
13175        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
13176        // set-not-multiset gate, but the accessor must ship the raw
13177        // slot verbatim so struct-literal fixtures continue to expose
13178        // the duplicate at the accessor).
13179        //
13180        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
13181        // slice accessor pin on the substrate primitive — folds on the
13182        // "outer [`Caixa`] `&[T]` slice" projection pattern
13183        // `autores_returns_autores_slice_verbatim_across_permutations`
13184        // (b5d813f) opened,
13185        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13186        // (78c7d3c) folded on,
13187        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
13188        // (8a36c23) closed the universal-axis text-tag family of, and
13189        // `exe_returns_exe_slice_verbatim_across_permutations`
13190        // (65d9527) opened the foreign-code-slot sub-family of. Closes
13191        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
13192        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
13193        // `:servicos`) now each carries a substrate-canonical slice
13194        // accessor. Pins against a future silent detour that returned
13195        // an owned `Vec<String>` (which would type-check but silently
13196        // clone on every accessor call, breaking the zero-cost
13197        // projection every peer sibling slice accessor carries), a
13198        // `[""] → []` collapse (which would silently absorb the
13199        // `CodePathEmpty` refusal case at the accessor boundary), an
13200        // `[a, a] → [a]` dedup collapse (which would silently absorb
13201        // the `CodePathDuplicate` refusal case at the accessor
13202        // boundary — the per-slot set-not-multiset gate is downstream
13203        // of the accessor and must not be silently promoted into it),
13204        // or a `[a, b] → [a]` singleton collapse (which would silently
13205        // absorb the V0 `ServicoCountMismatch` refusal case at the
13206        // accessor boundary — the V0 singularity gate is downstream of
13207        // the accessor and must not be silently promoted into it).
13208        for servicos in [
13209            vec![],
13210            vec![""],
13211            vec!["servicos/demo.computeunit.yaml"],
13212            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
13213            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
13214        ] {
13215            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
13216            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
13217            assert_eq!(
13218                c.servicos(),
13219                expected.as_slice(),
13220                "Caixa::servicos must return :servicos verbatim (got \
13221                 {:?}, expected {expected:?})",
13222                c.servicos(),
13223            );
13224            assert_eq!(
13225                c.servicos(),
13226                c.servicos.as_slice(),
13227                "Caixa::servicos must byte-equal the raw \
13228                 `self.servicos.as_slice()` field access across every \
13229                 value in the Vec<String> accept-set",
13230            );
13231        }
13232    }
13233
13234    #[test]
13235    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
13236        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
13237        // empty-arm gate on the `:servicos` slot must key off
13238        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
13239        // field-borrow walk. Structurally: a `Caixa { servicos:
13240        // vec!["".into()], .. }` must surface the `CodePathEmpty
13241        // { slot: ":servicos" }` refusal exactly, and a `Caixa
13242        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
13243        // .. }` (the canonical singleton V0-shape every in-tree
13244        // `caixa_with_code_paths` positive control uses) must pass
13245        // validate. The pair jointly pins the accessor + validate-gate
13246        // composition: any future silent detour that had the accessor
13247        // return an empty slice on the `[""]` arm (a `.iter().filter
13248        // (|s| !s.is_empty()).collect()` collapse) would silently
13249        // absorb the `CodePathEmpty` refusal at the accessor boundary
13250        // and the validate gate would accept a struct-literal
13251        // `Caixa { servicos: vec!["".into()], .. }` — the composition
13252        // pin catches that at caixa-core build time.
13253        //
13254        // Peer of the per-`Caixa`
13255        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
13256        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
13257        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
13258        // (b5d813f), and
13259        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13260        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
13261        // composition axes — same "the validate / shape-gate predicate
13262        // must route through the substrate-primitive typed dispatch"
13263        // discipline extended onto the sibling outer top-level
13264        // [`Caixa`] `&[T]`-composition surface, closing the trio of
13265        // code-surface accessor-composition pins on the same axis.
13266        // Nominally the in-tree `validate_code_paths` production body
13267        // still keys off the internal
13268        // `[(":bibliotecas", &self.bibliotecas,
13269        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
13270        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
13271        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
13272        // per-element accessor swap in isolation — a future companion
13273        // lift promotes the tuple's element type to `&[String]` and
13274        // threads the triple of typed dispatches through as a unit);
13275        // the composition pin catches any future accessor-side silent
13276        // filter drop against that eventual tuple-closure regardless
13277        // of whether the `:servicos` slot is threaded through the
13278        // accessor or the raw field access at the tuple's construction
13279        // site.
13280        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
13281        assert!(
13282            matches!(
13283                c.validate_code_paths(),
13284                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
13285            ),
13286            "validate_code_paths must reject servicos == vec![\"\"] \
13287             with CodePathEmpty {{ slot: \":servicos\" }} — the \
13288             accessor and the validate gate must route through the \
13289             same substrate-primitive typed dispatch on the \
13290             :servicos per-entry empty arm",
13291        );
13292        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
13293        assert!(
13294            c.validate_code_paths().is_ok(),
13295            "validate_code_paths must accept servicos == \
13296             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
13297             singleton V0-shape every in-tree `caixa_with_code_paths` \
13298             positive control uses)",
13299        );
13300    }
13301
13302    #[test]
13303    fn servicos_projects_slice_by_borrow() {
13304        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
13305        // borrow — the returned slice borrows the underlying
13306        // `Vec<String>` storage of the `:servicos` slot and the
13307        // accessor must not clone the backing `Vec` on every call.
13308        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13309        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
13310        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
13311        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
13312        // the sibling outer top-level [`Caixa`] `&[String]`-return
13313        // axes — the accessor's returned slice must borrow from
13314        // `&self` (the returned reference's lifetime is tied to
13315        // `&self`), and calling the accessor twice on the same
13316        // [`Caixa`] must yield slices that are pointer-equal (the
13317        // underlying byte-buffer is the storage `Vec`'s allocation,
13318        // not a fresh copy) as well as value-equal (idempotent, no
13319        // side effects on `&self`).
13320        //
13321        // Pins against a future silent detour that returned an owned
13322        // `Vec<String>` (which would type-check but silently clone on
13323        // every call, breaking the zero-cost projection every peer
13324        // sibling slice accessor carries), a `&Vec<String>` return
13325        // (which would leak the backing `Vec`'s grow/push/reserve
13326        // surface no downstream consumer reaches for), or a one-arm-
13327        // only accessor that returned a saturating value on some
13328        // sentinel input (breaking the pass-through invariant the
13329        // sibling slice accessors carry).
13330        for servicos in [
13331            vec![],
13332            vec!["servicos/demo.computeunit.yaml"],
13333            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
13334            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
13335        ] {
13336            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
13337            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
13338            let first = c.servicos();
13339            let second = c.servicos();
13340            assert_eq!(
13341                first, second,
13342                "Caixa::servicos must be idempotent — two successive \
13343                 calls on the same &self must return the same &[String]",
13344            );
13345            assert_eq!(
13346                first.as_ptr(),
13347                second.as_ptr(),
13348                "Caixa::servicos must borrow the underlying \
13349                 Vec<String> storage — two successive calls must \
13350                 return slices with the same backing pointer (a fresh \
13351                 Vec<String> clone would change the pointer on every \
13352                 call)",
13353            );
13354            assert_eq!(
13355                first,
13356                expected.as_slice(),
13357                "Caixa::servicos must return :servicos verbatim by \
13358                 borrow — got {first:?}, expected {expected:?}",
13359            );
13360        }
13361    }
13362
13363    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
13364
13365    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
13366        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13367        c.deps = deps;
13368        c
13369    }
13370
13371    #[test]
13372    fn deps_returns_deps_slice_verbatim_across_permutations() {
13373        // The canonical per-`Caixa` `:deps` universal-axis runtime-
13374        // dependency-declaration-list slice pin: [`Caixa::deps`] must
13375        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
13376        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
13377        // access across every representative value in the accept-set —
13378        // `[]` (the "no runtime deps declared" arm every existing
13379        // fixture without a `:deps` line carries; the
13380        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
13381        // single-entry list (the shape most consumer caixas carry), a
13382        // canonical two-entry list (the multi-dep runtime closure), and
13383        // two past-the-guard sentinels — a `[""]`-`:nome` entry
13384        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
13385        // `NomeInvalid` but the accessor must ship the raw slot
13386        // verbatim) and a `[a, a]` duplicate (validate rejects through
13387        // `DuplicateNome { list: ":deps" }` but the accessor must ship
13388        // the raw slot verbatim so struct-literal fixtures continue to
13389        // expose the duplicate at the accessor).
13390        //
13391        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
13392        // pin on the substrate primitive — opens the outer-`Caixa`
13393        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
13394        // future lift closes on. Peer of the closed outer-`Caixa`
13395        // foreign-code-slot `&[String]` sub-family
13396        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
13397        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
13398        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
13399        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
13400        // (`autores_returns_autores_slice_verbatim_across_permutations`
13401        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13402        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
13403        // projection pattern onto a novel element-type axis (`Dep`
13404        // composite vs the prior sibling family's `String` scalar).
13405        // Pins against a future silent detour that returned an owned
13406        // `Vec<Dep>` (which would type-check but silently clone on every
13407        // accessor call, breaking the zero-cost projection every peer
13408        // sibling slice accessor carries), a `[""] → []` collapse (which
13409        // would silently absorb the `NomeEmpty` refusal case at the
13410        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
13411        // would silently absorb the `DuplicateNome` refusal case at the
13412        // accessor boundary).
13413        for deps in [
13414            vec![],
13415            vec![Dep::simple("", "^0.1")],
13416            vec![Dep::simple("caixa-teia", "^0.1")],
13417            vec![
13418                Dep::simple("caixa-teia", "^0.1"),
13419                Dep::simple("caixa-core", "^0.1"),
13420            ],
13421            vec![
13422                Dep::simple("caixa-teia", "^0.1"),
13423                Dep::simple("caixa-teia", "^0.2"),
13424            ],
13425        ] {
13426            let c = caixa_with_deps(deps.clone());
13427            assert_eq!(
13428                c.deps(),
13429                deps.as_slice(),
13430                "Caixa::deps must return :deps verbatim (got {:?}, \
13431                 expected {deps:?})",
13432                c.deps(),
13433            );
13434            assert_eq!(
13435                c.deps(),
13436                c.deps.as_slice(),
13437                "Caixa::deps must element-equal the raw \
13438                 `self.deps.as_slice()` field access across every \
13439                 value in the Vec<Dep> accept-set",
13440            );
13441        }
13442    }
13443
13444    #[test]
13445    fn validate_deps_duplicate_arm_routes_through_accessor() {
13446        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
13447        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
13448        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
13449        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
13450        // "^0.2")], .. }` must surface the `DuplicateNome { list:
13451        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
13452        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
13453        // form) must pass validate. The pair jointly pins the accessor +
13454        // validate-gate composition: any future silent detour that had
13455        // the accessor return a dedupped slice on the `[a, a]` arm (a
13456        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
13457        // would silently absorb the `DuplicateNome` refusal at the
13458        // accessor boundary and the validate gate would accept a
13459        // struct-literal `Caixa` carrying the drift — the composition
13460        // pin catches that at caixa-core build time.
13461        //
13462        // Peer of the per-`Caixa`
13463        // `validate_autores_empty_entry_arm_routes_through_accessor`
13464        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13465        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
13466        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
13467        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
13468        // (611f78b) accessor-composition pins on the sibling `&[T]`-
13469        // composition axes — same "the validate gate must route through
13470        // the substrate-primitive typed dispatch" discipline extended
13471        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
13472        // composition surface, opening the outer-`Caixa` dependency-slot
13473        // arm of the composition-pin family.
13474        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
13475        let err = c.validate_deps().unwrap_err();
13476        assert!(
13477            matches!(
13478                err,
13479                DepError::DuplicateNome { ref nome, list } if nome == "d"
13480                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
13481            ),
13482            "validate_deps must reject deps == \
13483             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
13484             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
13485             accessor and the validate gate must route through the \
13486             same substrate-primitive typed dispatch on the :deps \
13487             within-list duplicate arm (got {err:?})",
13488        );
13489        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
13490        assert!(
13491            c.validate_deps().is_ok(),
13492            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
13493             (the canonical single-entry form)",
13494        );
13495    }
13496
13497    #[test]
13498    fn deps_projects_slice_by_borrow() {
13499        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
13500        // — the returned slice borrows the underlying `Vec<Dep>` storage
13501        // of the `:deps` slot and the accessor must not clone the
13502        // backing `Vec` on every call. Peer of the per-`Caixa`
13503        // `autores_projects_slice_by_borrow` (b5d813f),
13504        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
13505        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
13506        // `exe_projects_slice_by_borrow` (65d9527), and
13507        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
13508        // on the sibling outer top-level [`Caixa`] `&[String]`-return
13509        // axes — the accessor's returned slice must borrow from `&self`
13510        // (the returned reference's lifetime is tied to `&self`), and
13511        // calling the accessor twice on the same [`Caixa`] must yield
13512        // slices that are pointer-equal (the underlying byte-buffer is
13513        // the storage `Vec`'s allocation, not a fresh copy) as well as
13514        // value-equal (idempotent, no side effects on `&self`).
13515        //
13516        // Pins against a future silent detour that returned an owned
13517        // `Vec<Dep>` (which would type-check but silently clone on
13518        // every call), a `&Vec<Dep>` return (which would leak the
13519        // backing `Vec`'s grow/push/reserve surface no downstream
13520        // consumer reaches for), or a one-arm-only accessor that
13521        // returned a saturating value on some sentinel input.
13522        for deps in [
13523            vec![],
13524            vec![Dep::simple("caixa-teia", "^0.1")],
13525            vec![
13526                Dep::simple("caixa-teia", "^0.1"),
13527                Dep::simple("caixa-core", "^0.1"),
13528            ],
13529        ] {
13530            let c = caixa_with_deps(deps.clone());
13531            let first = c.deps();
13532            let second = c.deps();
13533            assert_eq!(
13534                first, second,
13535                "Caixa::deps must be idempotent — two successive calls \
13536                 on the same &self must return the same &[Dep]",
13537            );
13538            assert_eq!(
13539                first.as_ptr(),
13540                second.as_ptr(),
13541                "Caixa::deps must borrow the underlying Vec<Dep> \
13542                 storage — two successive calls must return slices \
13543                 with the same backing pointer (a fresh Vec<Dep> clone \
13544                 would change the pointer on every call)",
13545            );
13546            assert_eq!(
13547                first,
13548                deps.as_slice(),
13549                "Caixa::deps must return :deps verbatim by borrow — \
13550                 got {first:?}, expected {deps:?}",
13551            );
13552        }
13553    }
13554
13555    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
13556
13557    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
13558        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13559        c.deps_dev = deps_dev;
13560        c
13561    }
13562
13563    #[test]
13564    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
13565        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
13566        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
13567        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
13568        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
13569        // access across every representative value in the accept-set —
13570        // `[]` (the "no dev deps declared" arm every existing fixture
13571        // without a `:deps-dev` line carries; the [`Caixa::template`]
13572        // scaffold emits `:deps-dev ()`), a canonical single-entry list
13573        // (the shape most consumer caixas carry — a `tatara-check` dev
13574        // pin), a canonical two-entry list (the multi-dev-dep closure),
13575        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
13576        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
13577        // `NomeInvalid` but the accessor must ship the raw slot
13578        // verbatim) and a `[a, a]` duplicate (validate rejects through
13579        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
13580        // ship the raw slot verbatim so struct-literal fixtures continue
13581        // to expose the duplicate at the accessor).
13582        //
13583        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
13584        // pin on the substrate primitive — closes the outer-`Caixa`
13585        // dependency-slot `&[Dep]` sub-family the sibling
13586        // `deps_returns_deps_slice_verbatim_across_permutations`
13587        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
13588        // slice" projection pattern onto the sibling dev-dep axis —
13589        // pins against a future silent detour that returned an owned
13590        // `Vec<Dep>` (which would type-check but silently clone on every
13591        // accessor call, breaking the zero-cost projection every peer
13592        // sibling slice accessor carries), a `[""] → []` collapse (which
13593        // would silently absorb the `NomeEmpty` refusal case at the
13594        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
13595        // would silently absorb the `DuplicateNome` refusal case at the
13596        // accessor boundary).
13597        for deps_dev in [
13598            vec![],
13599            vec![Dep::simple("", "^0.1")],
13600            vec![Dep::simple("tatara-check", "^0.1")],
13601            vec![
13602                Dep::simple("tatara-check", "^0.1"),
13603                Dep::simple("caixa-lint", "^0.1"),
13604            ],
13605            vec![
13606                Dep::simple("tatara-check", "^0.1"),
13607                Dep::simple("tatara-check", "^0.2"),
13608            ],
13609        ] {
13610            let c = caixa_with_deps_dev(deps_dev.clone());
13611            assert_eq!(
13612                c.deps_dev(),
13613                deps_dev.as_slice(),
13614                "Caixa::deps_dev must return :deps-dev verbatim (got \
13615                 {:?}, expected {deps_dev:?})",
13616                c.deps_dev(),
13617            );
13618            assert_eq!(
13619                c.deps_dev(),
13620                c.deps_dev.as_slice(),
13621                "Caixa::deps_dev must element-equal the raw \
13622                 `self.deps_dev.as_slice()` field access across every \
13623                 value in the Vec<Dep> accept-set",
13624            );
13625        }
13626    }
13627
13628    #[test]
13629    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
13630        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
13631        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
13632        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
13633        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
13634        // Dep::simple("d", "^0.2")], .. }` must surface the
13635        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
13636        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
13637        // canonical single-entry form) must pass validate. The pair
13638        // jointly pins the accessor + validate-gate composition: any
13639        // future silent detour that had the accessor return a dedupped
13640        // slice on the `[a, a]` arm (a
13641        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
13642        // would silently absorb the `DuplicateNome` refusal at the
13643        // accessor boundary and the validate gate would accept a
13644        // struct-literal `Caixa` carrying the drift — the composition
13645        // pin catches that at caixa-core build time.
13646        //
13647        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
13648        // (ad34b4e) on the sibling `:deps` axis — same "the validate
13649        // gate must route through the substrate-primitive typed
13650        // dispatch" discipline folded onto the sibling `:deps-dev`
13651        // axis, closing the two-list dep-graph composition-pin family.
13652        // The `:deps-dev` diagnostic must carry the
13653        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
13654        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
13655        // offending list unambiguously.
13656        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
13657        let err = c.validate_deps().unwrap_err();
13658        assert!(
13659            matches!(
13660                err,
13661                DepError::DuplicateNome { ref nome, list } if nome == "d"
13662                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
13663            ),
13664            "validate_deps must reject deps_dev == \
13665             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
13666             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
13667             accessor and the validate gate must route through the \
13668             same substrate-primitive typed dispatch on the :deps-dev \
13669             within-list duplicate arm (got {err:?})",
13670        );
13671        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
13672        assert!(
13673            c.validate_deps().is_ok(),
13674            "validate_deps must accept deps_dev == \
13675             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
13676        );
13677    }
13678
13679    #[test]
13680    fn deps_dev_projects_slice_by_borrow() {
13681        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
13682        // borrow — the returned slice borrows the underlying `Vec<Dep>`
13683        // storage of the `:deps-dev` slot and the accessor must not
13684        // clone the backing `Vec` on every call. Peer of
13685        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
13686        // `:deps` axis, and of the per-`Caixa`
13687        // `autores_projects_slice_by_borrow` (b5d813f),
13688        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
13689        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
13690        // `exe_projects_slice_by_borrow` (65d9527), and
13691        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
13692        // on the sibling outer top-level [`Caixa`] `&[String]`-return
13693        // axes — the accessor's returned slice must borrow from `&self`
13694        // (the returned reference's lifetime is tied to `&self`), and
13695        // calling the accessor twice on the same [`Caixa`] must yield
13696        // slices that are pointer-equal (the underlying byte-buffer is
13697        // the storage `Vec`'s allocation, not a fresh copy) as well as
13698        // value-equal (idempotent, no side effects on `&self`).
13699        //
13700        // Pins against a future silent detour that returned an owned
13701        // `Vec<Dep>` (which would type-check but silently clone on
13702        // every call), a `&Vec<Dep>` return (which would leak the
13703        // backing `Vec`'s grow/push/reserve surface no downstream
13704        // consumer reaches for), or a one-arm-only accessor that
13705        // returned a saturating value on some sentinel input.
13706        for deps_dev in [
13707            vec![],
13708            vec![Dep::simple("tatara-check", "^0.1")],
13709            vec![
13710                Dep::simple("tatara-check", "^0.1"),
13711                Dep::simple("caixa-lint", "^0.1"),
13712            ],
13713        ] {
13714            let c = caixa_with_deps_dev(deps_dev.clone());
13715            let first = c.deps_dev();
13716            let second = c.deps_dev();
13717            assert_eq!(
13718                first, second,
13719                "Caixa::deps_dev must be idempotent — two successive \
13720                 calls on the same &self must return the same &[Dep]",
13721            );
13722            assert_eq!(
13723                first.as_ptr(),
13724                second.as_ptr(),
13725                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
13726                 storage — two successive calls must return slices \
13727                 with the same backing pointer (a fresh Vec<Dep> clone \
13728                 would change the pointer on every call)",
13729            );
13730            assert_eq!(
13731                first,
13732                deps_dev.as_slice(),
13733                "Caixa::deps_dev must return :deps-dev verbatim by \
13734                 borrow — got {first:?}, expected {deps_dev:?}",
13735            );
13736        }
13737    }
13738
13739    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
13740
13741    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
13742        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13743        c.limits = limits;
13744        c
13745    }
13746
13747    #[test]
13748    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
13749        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
13750        // composite optional-composite-reference-shape pin:
13751        // [`Caixa::limits`] must return the `:limits` typed
13752        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
13753        // reference over the same backing storage the raw
13754        // `self.limits.as_ref()` field access borrows from, byte-equal
13755        // across every representative fixture in the accept-set — the
13756        // author-omitted `None` shape (the "engine-default applies"
13757        // partition every downstream Servico M2 overlay emitter treats
13758        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
13759        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
13760        // per-axis cap is `None`, so the peer M2 overlay emitter's
13761        // `.is_empty()`-gated projection still emits nothing but the
13762        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
13763        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
13764        // fixture (only `:memory` set — the canonical shape most
13765        // memory-heavy Servicos carry), and a fully-populated composite
13766        // (every per-axis cap set — the canonical shape a
13767        // sandboxed-by-default Servico carries).
13768        //
13769        // Pins against a future silent detour that returned a fresh-
13770        // cloned [`LimitsSpec`] copy (which would type-check via the
13771        // `Clone` impl but silently break every downstream caller that
13772        // relied on the reference sharing the composite's backing
13773        // identity), a reference to an operator-resolved overlay (the
13774        // future per-cluster `:limits-overrides` slot — its resolution
13775        // must land at exactly this accessor body, not silently divert
13776        // the raw slot away from a second consumer), a
13777        // `None` → `Some(LimitsSpec::default)` cluster-default
13778        // projection (which would collapse the load-bearing
13779        // "author-omitted `:limits` ⇒ engine-default applies" partition
13780        // the peer [`crate::render::servico_m2_overlay`] emitter and
13781        // the peer [`Caixa::declared_servico_slots`] enumerator both
13782        // read), or an axis-shuffled projection (a future detour that
13783        // swapped `memory` and `fuel` through the accessor would
13784        // silently split the paired [`crate::StandardLayout::verify`]
13785        // per-`:limits` shape gate's traversal input from the peer
13786        // `servico_m2_overlay` emitter's projection input).
13787        //
13788        // First outer top-level [`Caixa`] `Option<&Composite>`-return
13789        // composite-reference accessor pin on the substrate primitive
13790        // — opens the outer-`Caixa` `Option<&Composite>` composite-
13791        // reference projection pattern the sibling `:behavior`
13792        // [`crate::BehaviorSpec`] / `:politicas`
13793        // [`crate::aplicacao::MeshPolicy`] / `:placement`
13794        // [`crate::aplicacao::Placement`] / `:entrada`
13795        // [`crate::aplicacao::Entrada`] future outer-composite lifts
13796        // fold on. Peer of the closed M3 outer-composite family the
13797        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
13798        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
13799        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
13800        // reference accessor pins already carry on the outer
13801        // [`crate::AplicacaoSpec`] altitude — extends the outer-
13802        // accessor byte-equal-projection discipline onto the outer
13803        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
13804        use crate::LimitsSpec;
13805        use std::time::Duration;
13806        let fixtures: Vec<Option<LimitsSpec>> = vec![
13807            None,
13808            Some(LimitsSpec::default()),
13809            Some(LimitsSpec {
13810                memory: Some(64 * 1024 * 1024),
13811                ..Default::default()
13812            }),
13813            Some(LimitsSpec {
13814                memory: Some(64 * 1024 * 1024),
13815                fuel: Some(1_000_000),
13816                wall_clock: Some(Duration::from_secs(30)),
13817                cpu: Some(500),
13818            }),
13819        ];
13820        for limits in fixtures {
13821            let c = caixa_with_limits(limits.clone());
13822            assert_eq!(
13823                c.limits(),
13824                limits.as_ref(),
13825                "Caixa::limits must return :limits verbatim (got {:?}, \
13826                 expected {:?})",
13827                c.limits(),
13828                limits.as_ref(),
13829            );
13830            match (c.limits(), c.limits.as_ref()) {
13831                (Some(a), Some(b)) => assert!(
13832                    std::ptr::eq(a, b),
13833                    "Caixa::limits accessor and self.limits.as_ref() \
13834                     field access must borrow the same backing storage \
13835                     — the accessor is the substrate-primitive typed \
13836                     dispatch every downstream Servico-M2-overlay \
13837                     composite consumer must route through, and a \
13838                     reference-identity split would silently break \
13839                     every consumer that relied on the borrow sharing \
13840                     the composite's storage",
13841                ),
13842                (None, None) => {}
13843                _ => panic!(
13844                    "Caixa::limits presence bit must byte-equal \
13845                     self.limits.is_some() — a presence-bit drift would \
13846                     silently split the paired StandardLayout::verify \
13847                     per-`:limits` shape gate's traversal head from \
13848                     the peer render::servico_m2_overlay M2 overlay \
13849                     emitter's traversal head from the peer \
13850                     Caixa::declared_servico_slots M2 declared-slot \
13851                     enumerator's presence probe",
13852                ),
13853            }
13854            assert_eq!(
13855                c.limits().is_some(),
13856                c.limits.is_some(),
13857                "Caixa::limits().is_some() must byte-equal \
13858                 self.limits.is_some() — a presence-bit drift would \
13859                 silently split every downstream Option<&LimitsSpec> \
13860                 consumer's partition on the engine-default arm",
13861            );
13862        }
13863    }
13864
13865    #[test]
13866    fn declared_servico_slots_limits_arm_routes_through_accessor() {
13867        // Composition pin: [`Caixa::declared_servico_slots`]'s
13868        // `:limits` presence-probe arm must key off [`Caixa::limits`],
13869        // not the raw `self.limits.is_some()` field-probe. Structurally:
13870        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
13871        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
13872        // (the presence bit is `Some`, so the M2 kind-coherence gate
13873        // must surface the slot as "declared" even when every per-axis
13874        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
13875        // push the label (the "author omitted the slot entirely"
13876        // partition). The pair jointly pins the accessor + declared-
13877        // slot enumerator composition: any future silent detour that
13878        // had the accessor collapse `Some(LimitsSpec::default())` to
13879        // `None` (a `.filter(|l| !l.is_empty())` projection) would
13880        // silently absorb the "declared but empty" arm at the
13881        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
13882        // kind-coherence gate would silently accept a
13883        // struct-literal `Caixa` carrying the drift.
13884        //
13885        // Peer of the sibling per-`Caixa`
13886        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
13887        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
13888        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
13889        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
13890        // enumerator gate must route through the substrate-primitive
13891        // typed dispatch" discipline extended onto the outer top-level
13892        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
13893        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
13894        // composition-pin family.
13895        use crate::LimitsSpec;
13896        let c = caixa_with_limits(Some(LimitsSpec::default()));
13897        let slots = c.declared_servico_slots();
13898        assert!(
13899            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
13900            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
13901             when `:limits` is Some (even for LimitsSpec::default()) \
13902             — the accessor and the enumerator gate must route through \
13903             the same substrate-primitive typed dispatch on the outer \
13904             :limits presence bit (got slots={slots:?})",
13905        );
13906        let c = caixa_with_limits(None);
13907        let slots = c.declared_servico_slots();
13908        assert!(
13909            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
13910            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
13911             when `:limits` is None — the author-omitted arm must \
13912             route through the accessor's None-return unchanged (got \
13913             slots={slots:?})",
13914        );
13915    }
13916
13917    #[test]
13918    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
13919        // Composition pin: [`crate::render::servico_m2_overlay`]'s
13920        // per-`:limits` M2 overlay emit arm must key off
13921        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
13922        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
13923        // Some(64 MiB), .. default }), .. }` must surface the
13924        // `M2_KEY_LIMITS` key with the per-axis
13925        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
13926        // limits: Some(LimitsSpec::default()), .. }` must omit the
13927        // key entirely (the `.is_empty()`-gated inner arm elides an
13928        // empty composite even when the outer presence bit is `Some`),
13929        // and a `Caixa { limits: None, .. }` must also omit the key
13930        // (the "author omitted the slot entirely" partition). The
13931        // three-fixture family jointly pins the accessor + M2 overlay
13932        // emitter composition: any future silent detour that had the
13933        // accessor return a fresh-cloned copy on the `Some` arm (a
13934        // `LimitsSpec::clone()` projection) would silently break the
13935        // reference-identity pin the peer per-axis
13936        // `serde_yaml::to_value(limits)` projection reads from.
13937        use crate::LimitsSpec;
13938        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
13939        let c = caixa_with_limits(Some(LimitsSpec {
13940            memory: Some(64 * 1024 * 1024),
13941            ..Default::default()
13942        }));
13943        let overlay = servico_m2_overlay(&c).unwrap();
13944        assert!(
13945            overlay.contains_key(M2_KEY_LIMITS),
13946            "servico_m2_overlay must surface M2_KEY_LIMITS when \
13947             `:limits` carries a non-empty composite — the accessor \
13948             and the M2 overlay emitter must route through the same \
13949             substrate-primitive typed dispatch on the outer :limits \
13950             composite (got overlay={overlay:?})",
13951        );
13952        let c = caixa_with_limits(Some(LimitsSpec::default()));
13953        let overlay = servico_m2_overlay(&c).unwrap();
13954        assert!(
13955            !overlay.contains_key(M2_KEY_LIMITS),
13956            "servico_m2_overlay must omit M2_KEY_LIMITS when \
13957             `:limits` is Some(LimitsSpec::default()) — the empty \
13958             composite's `.is_empty()`-gated inner arm must elide \
13959             the key regardless of the outer presence bit (got \
13960             overlay={overlay:?})",
13961        );
13962        let c = caixa_with_limits(None);
13963        let overlay = servico_m2_overlay(&c).unwrap();
13964        assert!(
13965            !overlay.contains_key(M2_KEY_LIMITS),
13966            "servico_m2_overlay must omit M2_KEY_LIMITS when \
13967             `:limits` is None — the author-omitted arm must route \
13968             through the accessor's None-return unchanged (got \
13969             overlay={overlay:?})",
13970        );
13971    }
13972
13973    #[test]
13974    fn limits_projects_option_ref_by_borrow() {
13975        // The by-borrow pin: [`Caixa::limits`] returns
13976        // `Option<&LimitsSpec>` by borrow — the returned reference
13977        // borrows the underlying `Option<LimitsSpec>` storage of the
13978        // `:limits` slot and the accessor must not clone the backing
13979        // composite on every call. Peer of the sibling
13980        // `deps_projects_slice_by_borrow` (ad34b4e) /
13981        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
13982        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
13983        // extended here to the outer [`Caixa`] `Option<&Composite>`-
13984        // return axis: the accessor's returned reference must borrow
13985        // from `&self` (the returned reference's lifetime is tied to
13986        // `&self`), and calling the accessor twice on the same
13987        // [`Caixa`] must yield references that are pointer-equal (the
13988        // underlying byte-buffer is the storage `LimitsSpec`'s
13989        // allocation, not a fresh copy) as well as value-equal
13990        // (idempotent, no side effects on `&self`).
13991        //
13992        // Pins against a future silent detour that returned an owned
13993        // `LimitsSpec` (which would type-check via the `Clone` impl
13994        // but silently clone on every call), a `&LimitsSpec` panic-
13995        // return on the `None` arm (which would collapse the load-
13996        // bearing `Option` presence-bit into a runtime panic), or a
13997        // one-arm-only accessor that returned a saturating composite
13998        // on some sentinel input.
13999        use crate::LimitsSpec;
14000        use std::time::Duration;
14001        for limits in [
14002            Some(LimitsSpec::default()),
14003            Some(LimitsSpec {
14004                memory: Some(64 * 1024 * 1024),
14005                fuel: Some(1_000_000),
14006                wall_clock: Some(Duration::from_secs(30)),
14007                cpu: Some(500),
14008            }),
14009        ] {
14010            let c = caixa_with_limits(limits.clone());
14011            let first = c.limits().unwrap();
14012            let second = c.limits().unwrap();
14013            assert_eq!(
14014                first, second,
14015                "Caixa::limits must be idempotent — two successive \
14016                 calls on the same &self must return the same \
14017                 &LimitsSpec",
14018            );
14019            assert!(
14020                std::ptr::eq(first, second),
14021                "Caixa::limits must borrow the underlying \
14022                 Option<LimitsSpec> storage — two successive calls \
14023                 must return references with the same backing pointer \
14024                 (a fresh LimitsSpec clone would change the pointer \
14025                 on every call)",
14026            );
14027            assert_eq!(
14028                Some(first),
14029                limits.as_ref(),
14030                "Caixa::limits must return :limits verbatim by borrow \
14031                 — got {first:?}, expected {:?}",
14032                limits.as_ref(),
14033            );
14034        }
14035        let c = caixa_with_limits(None);
14036        assert!(
14037            c.limits().is_none(),
14038            "Caixa::limits must return None when :limits is absent — \
14039             the author-omitted arm must project through the \
14040             accessor's Option::None unchanged",
14041        );
14042    }
14043
14044    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
14045
14046    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
14047        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14048        c.behavior = behavior;
14049        c
14050    }
14051
14052    #[test]
14053    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
14054        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
14055        // composite optional-composite-reference-shape pin:
14056        // [`Caixa::behavior`] must return the `:behavior` typed
14057        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
14058        // reference over the same backing storage the raw
14059        // `self.behavior.as_ref()` field access borrows from, byte-equal
14060        // across every representative fixture in the accept-set — the
14061        // author-omitted `None` shape (the "runtime-default applies"
14062        // partition every downstream Servico M2 overlay emitter treats
14063        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
14064        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
14065        // every per-callback path is `None`, so the peer M2 overlay
14066        // emitter's `.is_empty()`-gated projection still emits nothing
14067        // but the outer presence-bit is `Some`, so
14068        // [`Caixa::declared_servico_slots`] still pushes the
14069        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
14070        // (only `:on-state-change` set — the canonical shape a caixa
14071        // that only wires the hot-upgrade migration path carries), and
14072        // a fully-populated composite (every per-callback path set —
14073        // the canonical shape a fully-instrumented gen_server-shaped
14074        // Servico carries).
14075        //
14076        // Peer of the sibling
14077        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14078        // (b2bd9d7) opening fixture-family + reference-identity +
14079        // presence-bit tetrad pin on the outer top-level [`Caixa`]
14080        // `Option<&Composite>`-return sub-family — extended here to the
14081        // second axis of that sub-family so both of the currently-lifted
14082        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
14083        // `:behavior`) carry the same "byte-equal, borrow-shared,
14084        // presence-bit-preserved" outer-accessor discipline.
14085        //
14086        // Pins against a future silent detour that returned a fresh-
14087        // cloned [`crate::BehaviorSpec`] copy (which would type-check
14088        // via the `Clone` impl but silently break every downstream
14089        // caller that relied on the reference sharing the composite's
14090        // backing identity), a reference to an operator-resolved
14091        // overlay (a future per-cluster `:behavior-overrides` slot —
14092        // its resolution must land at exactly this accessor body, not
14093        // silently divert the raw slot away from a second consumer), a
14094        // `None` → `Some(BehaviorSpec::default)` cluster-default
14095        // projection (which would collapse the load-bearing
14096        // "author-omitted `:behavior` ⇒ runtime-default applies"
14097        // partition the peer [`crate::render::servico_m2_overlay`]
14098        // emitter, the peer [`Caixa::declared_servico_slots`]
14099        // enumerator, and the cross-slot
14100        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
14101        // gate all read), or a callback-shuffled projection (a future
14102        // detour that swapped `on_init` and `on_terminate` through the
14103        // accessor would silently split the paired
14104        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
14105        // traversal input from the peer `servico_m2_overlay` emitter's
14106        // projection input from the cross-slot `:state-change`
14107        // composition gate's traversal input).
14108        use crate::BehaviorSpec;
14109        use std::path::PathBuf;
14110        let fixtures: Vec<Option<BehaviorSpec>> = vec![
14111            None,
14112            Some(BehaviorSpec::default()),
14113            Some(BehaviorSpec {
14114                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14115                ..Default::default()
14116            }),
14117            Some(BehaviorSpec {
14118                on_init: Some(PathBuf::from("lib/init.lisp")),
14119                on_call: Some(PathBuf::from("lib/handlers.lisp")),
14120                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
14121                on_info: Some(PathBuf::from("lib/handlers.lisp")),
14122                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14123                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
14124            }),
14125        ];
14126        for behavior in fixtures {
14127            let c = caixa_with_behavior(behavior.clone());
14128            assert_eq!(
14129                c.behavior(),
14130                behavior.as_ref(),
14131                "Caixa::behavior must return :behavior verbatim (got \
14132                 {:?}, expected {:?})",
14133                c.behavior(),
14134                behavior.as_ref(),
14135            );
14136            match (c.behavior(), c.behavior.as_ref()) {
14137                (Some(a), Some(b)) => assert!(
14138                    std::ptr::eq(a, b),
14139                    "Caixa::behavior accessor and self.behavior.as_ref() \
14140                     field access must borrow the same backing storage \
14141                     — the accessor is the substrate-primitive typed \
14142                     dispatch every downstream Servico-M2-overlay \
14143                     composite consumer must route through, and a \
14144                     reference-identity split would silently break \
14145                     every consumer that relied on the borrow sharing \
14146                     the composite's storage",
14147                ),
14148                (None, None) => {}
14149                _ => panic!(
14150                    "Caixa::behavior presence bit must byte-equal \
14151                     self.behavior.is_some() — a presence-bit drift \
14152                     would silently split the paired \
14153                     StandardLayout::verify per-`:behavior` shape \
14154                     gate's traversal head from the peer \
14155                     render::servico_m2_overlay M2 overlay emitter's \
14156                     traversal head from the cross-slot \
14157                     validate_upgrade_from_against_behavior \
14158                     composition gate's traversal head from the peer \
14159                     Caixa::declared_servico_slots M2 declared-slot \
14160                     enumerator's presence probe",
14161                ),
14162            }
14163            assert_eq!(
14164                c.behavior().is_some(),
14165                c.behavior.is_some(),
14166                "Caixa::behavior().is_some() must byte-equal \
14167                 self.behavior.is_some() — a presence-bit drift would \
14168                 silently split every downstream Option<&BehaviorSpec> \
14169                 consumer's partition on the runtime-default arm",
14170            );
14171        }
14172    }
14173
14174    #[test]
14175    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
14176        // Composition pin: [`Caixa::declared_servico_slots`]'s
14177        // `:behavior` presence-probe arm must key off
14178        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
14179        // field-probe. Structurally: a `Caixa { behavior:
14180        // Some(BehaviorSpec::default()), .. }` must still push
14181        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
14182        // presence bit is `Some`, so the M2 kind-coherence gate must
14183        // surface the slot as "declared" even when every per-callback
14184        // path is unset), and a `Caixa { behavior: None, .. }` must
14185        // NOT push the label (the "author omitted the slot entirely"
14186        // partition). The pair jointly pins the accessor + declared-
14187        // slot enumerator composition: any future silent detour that
14188        // had the accessor collapse `Some(BehaviorSpec::default())`
14189        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
14190        // silently absorb the "declared but empty" arm at the
14191        // accessor boundary and the
14192        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
14193        // kind-coherence gate would silently accept a struct-literal
14194        // `Caixa` carrying the drift.
14195        //
14196        // Peer of the sibling
14197        // `declared_servico_slots_limits_arm_routes_through_accessor`
14198        // (b2bd9d7) composition pin on the sibling `:limits` outer-
14199        // `Option<&LimitsSpec>` arm of the same
14200        // [`Caixa::declared_servico_slots`] M2 declared-slot
14201        // enumerator's traversal — same "the enumerator gate must
14202        // route through the substrate-primitive typed dispatch"
14203        // discipline extended onto the outer top-level [`Caixa`]
14204        // `Option<&BehaviorSpec>`-composition surface.
14205        use crate::BehaviorSpec;
14206        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
14207        let slots = c.declared_servico_slots();
14208        assert!(
14209            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
14210            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
14211             when `:behavior` is Some (even for BehaviorSpec::default()) \
14212             — the accessor and the enumerator gate must route through \
14213             the same substrate-primitive typed dispatch on the outer \
14214             :behavior presence bit (got slots={slots:?})",
14215        );
14216        let c = caixa_with_behavior(None);
14217        let slots = c.declared_servico_slots();
14218        assert!(
14219            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
14220            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
14221             when `:behavior` is None — the author-omitted arm must \
14222             route through the accessor's None-return unchanged (got \
14223             slots={slots:?})",
14224        );
14225    }
14226
14227    #[test]
14228    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
14229        // Composition pin: [`crate::render::servico_m2_overlay`]'s
14230        // per-`:behavior` M2 overlay emit arm must key off
14231        // [`Caixa::behavior`], not the raw `&caixa.behavior`
14232        // field-borrow. Structurally: a `Caixa { behavior:
14233        // Some(BehaviorSpec { on_state_change: Some(...), .. default
14234        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
14235        // per-callback `onStateChange` sub-mapping in the overlay, a
14236        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
14237        // must omit the key entirely (the `.is_empty()`-gated inner
14238        // arm elides an empty composite even when the outer presence
14239        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
14240        // also omit the key (the "author omitted the slot entirely"
14241        // partition). The three-fixture family jointly pins the
14242        // accessor + M2 overlay emitter composition: any future
14243        // silent detour that had the accessor return a fresh-cloned
14244        // copy on the `Some` arm (a `BehaviorSpec::clone()`
14245        // projection) would silently break the reference-identity
14246        // pin the peer per-callback `serde_yaml::to_value(behavior)`
14247        // projection reads from.
14248        //
14249        // Peer of the sibling
14250        // `servico_m2_overlay_limits_arm_routes_through_accessor`
14251        // (b2bd9d7) composition pin on the sibling `:limits` outer-
14252        // `Option<&LimitsSpec>` arm of the same
14253        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
14254        // traversal — same "the emitter must route through the
14255        // substrate-primitive typed dispatch on the outer composite"
14256        // discipline extended onto the outer top-level [`Caixa`]
14257        // `Option<&BehaviorSpec>`-composition surface.
14258        use crate::BehaviorSpec;
14259        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
14260        use std::path::PathBuf;
14261        let c = caixa_with_behavior(Some(BehaviorSpec {
14262            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14263            ..Default::default()
14264        }));
14265        let overlay = servico_m2_overlay(&c).unwrap();
14266        assert!(
14267            overlay.contains_key(M2_KEY_BEHAVIOR),
14268            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
14269             `:behavior` carries a non-empty composite — the accessor \
14270             and the M2 overlay emitter must route through the same \
14271             substrate-primitive typed dispatch on the outer :behavior \
14272             composite (got overlay={overlay:?})",
14273        );
14274        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
14275        let overlay = servico_m2_overlay(&c).unwrap();
14276        assert!(
14277            !overlay.contains_key(M2_KEY_BEHAVIOR),
14278            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
14279             `:behavior` is Some(BehaviorSpec::default()) — the empty \
14280             composite's `.is_empty()`-gated inner arm must elide the \
14281             key regardless of the outer presence bit (got \
14282             overlay={overlay:?})",
14283        );
14284        let c = caixa_with_behavior(None);
14285        let overlay = servico_m2_overlay(&c).unwrap();
14286        assert!(
14287            !overlay.contains_key(M2_KEY_BEHAVIOR),
14288            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
14289             `:behavior` is None — the author-omitted arm must route \
14290             through the accessor's None-return unchanged (got \
14291             overlay={overlay:?})",
14292        );
14293    }
14294
14295    #[test]
14296    fn behavior_projects_option_ref_by_borrow() {
14297        // The by-borrow pin: [`Caixa::behavior`] returns
14298        // `Option<&BehaviorSpec>` by borrow — the returned reference
14299        // borrows the underlying `Option<BehaviorSpec>` storage of the
14300        // `:behavior` slot and the accessor must not clone the backing
14301        // composite on every call. Peer of the sibling
14302        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
14303        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
14304        // return sub-family — extended here to the second axis of the
14305        // same sub-family: the accessor's returned reference must
14306        // borrow from `&self` (the returned reference's lifetime is
14307        // tied to `&self`), and calling the accessor twice on the same
14308        // [`Caixa`] must yield references that are pointer-equal (the
14309        // underlying byte-buffer is the storage `BehaviorSpec`'s
14310        // allocation, not a fresh copy) as well as value-equal
14311        // (idempotent, no side effects on `&self`).
14312        //
14313        // Pins against a future silent detour that returned an owned
14314        // `BehaviorSpec` (which would type-check via the `Clone` impl
14315        // but silently clone on every call), a `&BehaviorSpec` panic-
14316        // return on the `None` arm (which would collapse the load-
14317        // bearing `Option` presence-bit into a runtime panic), or a
14318        // one-arm-only accessor that returned a saturating composite
14319        // on some sentinel input.
14320        use crate::BehaviorSpec;
14321        use std::path::PathBuf;
14322        for behavior in [
14323            Some(BehaviorSpec::default()),
14324            Some(BehaviorSpec {
14325                on_init: Some(PathBuf::from("lib/init.lisp")),
14326                on_call: Some(PathBuf::from("lib/handlers.lisp")),
14327                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
14328                on_info: Some(PathBuf::from("lib/handlers.lisp")),
14329                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14330                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
14331            }),
14332        ] {
14333            let c = caixa_with_behavior(behavior.clone());
14334            let first = c.behavior().unwrap();
14335            let second = c.behavior().unwrap();
14336            assert_eq!(
14337                first, second,
14338                "Caixa::behavior must be idempotent — two successive \
14339                 calls on the same &self must return the same \
14340                 &BehaviorSpec",
14341            );
14342            assert!(
14343                std::ptr::eq(first, second),
14344                "Caixa::behavior must borrow the underlying \
14345                 Option<BehaviorSpec> storage — two successive calls \
14346                 must return references with the same backing pointer \
14347                 (a fresh BehaviorSpec clone would change the pointer \
14348                 on every call)",
14349            );
14350            assert_eq!(
14351                Some(first),
14352                behavior.as_ref(),
14353                "Caixa::behavior must return :behavior verbatim by \
14354                 borrow — got {first:?}, expected {:?}",
14355                behavior.as_ref(),
14356            );
14357        }
14358        let c = caixa_with_behavior(None);
14359        assert!(
14360            c.behavior().is_none(),
14361            "Caixa::behavior must return None when :behavior is absent \
14362             — the author-omitted arm must project through the \
14363             accessor's Option::None unchanged",
14364        );
14365    }
14366
14367    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
14368
14369    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
14370        use crate::aplicacao::{Membro, WitContract};
14371        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14372        c.kind = CaixaKind::Aplicacao;
14373        c.membros = vec![Membro {
14374            caixa: "a".into(),
14375            versao: "^0.1".into(),
14376        }];
14377        c.contratos = vec![WitContract {
14378            de: "a".into(),
14379            para: "a".into(),
14380            wit: "wasi:http/proxy".into(),
14381            endpoint: Some("/x".into()),
14382            subject: None,
14383            slot: None,
14384        }];
14385        c.politicas = politicas;
14386        c
14387    }
14388
14389    #[test]
14390    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
14391        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
14392        // composite optional-composite-reference-shape pin:
14393        // [`Caixa::politicas`] must return the `:politicas` typed
14394        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
14395        // reference over the same backing storage the raw
14396        // `self.politicas.as_ref()` field access borrows from,
14397        // byte-equal across every representative fixture in the
14398        // accept-set — the author-omitted `None` shape (the "cluster-
14399        // default applies" partition every downstream mesh-artifact
14400        // emitter treats as "emit no `:politicas` overlay"), the
14401        // empty-composite `Some(MeshPolicy { .. default })` shape
14402        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
14403        // per-axis mesh-policy scalar is `None`, so the peer inner
14404        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
14405        // caixa-mesh overlay elides every per-axis emit but the outer
14406        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
14407        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
14408        // single-axis fixture (only `:timeout` set — the canonical
14409        // shape a latency-sensitive Aplicacao carries), and a
14410        // fully-populated composite (every per-axis mesh-policy
14411        // scalar set — the canonical shape a fully-governed
14412        // Aplicacao carries).
14413        //
14414        // Pins against a future silent detour that returned a fresh-
14415        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
14416        // type-check via the `Clone` impl but silently break every
14417        // downstream caller that relied on the reference sharing the
14418        // composite's backing identity), a reference to an operator-
14419        // resolved overlay (the future per-cluster
14420        // `:politicas-overrides` slot — its resolution must land at
14421        // exactly this accessor body, not silently divert the raw
14422        // slot away from the peer [`Caixa::declared_mesh_slots`]
14423        // enumerator's presence probe), a
14424        // `None` → `Some(MeshPolicy::default)` cluster-default
14425        // projection (which would collapse the load-bearing
14426        // "author-omitted `:politicas` ⇒ cluster-default applies"
14427        // partition the peer [`Caixa::declared_mesh_slots`]
14428        // enumerator and the peer [`Caixa::aplicacao_view`]
14429        // Aplicacao-composition seed both read), or an axis-shuffled
14430        // projection (a future detour that swapped `timeout` and
14431        // `retries` through the accessor would silently split the
14432        // paired [`Caixa::aplicacao_view`] seed's fold input from the
14433        // sibling M3 mesh-artifact emitter's projection input).
14434        //
14435        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
14436        // composite-reference accessor pin on the substrate primitive
14437        // — peer of the sibling
14438        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14439        // (b2bd9d7) and
14440        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14441        // (35d8b52) opening tetrad pins on the outer top-level
14442        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14443        // here to the first of the three M3 mesh-slot axes so the
14444        // opening third of the outer `Option<&Composite>` sub-family
14445        // carries the same "byte-equal, borrow-shared, presence-bit-
14446        // preserved" outer-accessor discipline.
14447        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
14448        use std::time::Duration;
14449        let fixtures: Vec<Option<MeshPolicy>> = vec![
14450            None,
14451            Some(MeshPolicy::default()),
14452            Some(MeshPolicy {
14453                timeout: Some(Duration::from_secs(30)),
14454                ..Default::default()
14455            }),
14456            Some(MeshPolicy {
14457                timeout: Some(Duration::from_secs(30)),
14458                retries: Some(3),
14459                circuit_breaker: Some(CircuitBreaker {
14460                    max_failures: 5,
14461                    window: Duration::from_secs(60),
14462                }),
14463                mtls_required: Some(true),
14464                rate_limit: Some(RateLimit {
14465                    rate: 100,
14466                    window: Duration::from_secs(1),
14467                }),
14468            }),
14469        ];
14470        for politicas in fixtures {
14471            let c = caixa_aplicacao_with_politicas(politicas.clone());
14472            assert_eq!(
14473                c.politicas(),
14474                politicas.as_ref(),
14475                "Caixa::politicas must return :politicas verbatim (got \
14476                 {:?}, expected {:?})",
14477                c.politicas(),
14478                politicas.as_ref(),
14479            );
14480            match (c.politicas(), c.politicas.as_ref()) {
14481                (Some(a), Some(b)) => assert!(
14482                    std::ptr::eq(a, b),
14483                    "Caixa::politicas accessor and self.politicas.as_ref() \
14484                     field access must borrow the same backing storage \
14485                     — the accessor is the substrate-primitive typed \
14486                     dispatch every downstream Aplicacao-mesh-overlay \
14487                     composite consumer must route through, and a \
14488                     reference-identity split would silently break \
14489                     every consumer that relied on the borrow sharing \
14490                     the composite's storage",
14491                ),
14492                (None, None) => {}
14493                _ => panic!(
14494                    "Caixa::politicas presence bit must byte-equal \
14495                     self.politicas.is_some() — a presence-bit drift \
14496                     would silently split the paired \
14497                     Caixa::aplicacao_view Aplicacao-composition seed's \
14498                     traversal head from the peer \
14499                     Caixa::declared_mesh_slots M3 declared-slot \
14500                     enumerator's presence probe",
14501                ),
14502            }
14503            assert_eq!(
14504                c.politicas().is_some(),
14505                c.politicas.is_some(),
14506                "Caixa::politicas().is_some() must byte-equal \
14507                 self.politicas.is_some() — a presence-bit drift would \
14508                 silently split every downstream Option<&MeshPolicy> \
14509                 consumer's partition on the cluster-default arm",
14510            );
14511        }
14512    }
14513
14514    #[test]
14515    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
14516        // Composition pin: [`Caixa::declared_mesh_slots`]'s
14517        // `:politicas` presence-probe arm must key off
14518        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
14519        // field-probe. Structurally: a `Caixa { politicas:
14520        // Some(MeshPolicy::default()), .. }` must still push
14521        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
14522        // presence bit is `Some`, so the M3 kind-coherence gate must
14523        // surface the slot as "declared" even when every per-axis
14524        // scalar is unset), and a `Caixa { politicas: None, .. }` must
14525        // NOT push the label (the "author omitted the slot entirely"
14526        // partition). The pair jointly pins the accessor + declared-
14527        // slot enumerator composition: any future silent detour that
14528        // had the accessor collapse `Some(MeshPolicy::default())` to
14529        // `None` (a `.filter(|p| !p.is_empty())` projection) would
14530        // silently absorb the "declared but empty" arm at the
14531        // accessor boundary and the
14532        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
14533        // coherence gate would silently accept a struct-literal
14534        // `Caixa` carrying the drift.
14535        //
14536        // Peer of the sibling
14537        // `declared_servico_slots_limits_arm_routes_through_accessor`
14538        // (b2bd9d7) and
14539        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14540        // (35d8b52) composition pins on the sibling `:limits` /
14541        // `:behavior` outer-`Option<&Composite>` arms of the peer
14542        // [`Caixa::declared_servico_slots`] M2 declared-slot
14543        // enumerator's traversal — same "the enumerator gate must
14544        // route through the substrate-primitive typed dispatch"
14545        // discipline extended onto the outer top-level [`Caixa`] M3
14546        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
14547        // enumerator carries the same routing invariant as its M2
14548        // sibling.
14549        use crate::aplicacao::MeshPolicy;
14550        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
14551        let slots = c.declared_mesh_slots();
14552        assert!(
14553            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
14554            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
14555             when `:politicas` is Some (even for MeshPolicy::default()) \
14556             — the accessor and the enumerator gate must route through \
14557             the same substrate-primitive typed dispatch on the outer \
14558             :politicas presence bit (got slots={slots:?})",
14559        );
14560        let c = caixa_aplicacao_with_politicas(None);
14561        let slots = c.declared_mesh_slots();
14562        assert!(
14563            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
14564            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
14565             when `:politicas` is None — the author-omitted arm must \
14566             route through the accessor's None-return unchanged (got \
14567             slots={slots:?})",
14568        );
14569    }
14570
14571    #[test]
14572    fn aplicacao_view_politicas_arm_folds_through_accessor() {
14573        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
14574        // Aplicacao-composition seed must fold through
14575        // [`Caixa::politicas`], not the raw
14576        // `self.politicas.clone().unwrap_or_default()` field-borrow.
14577        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
14578        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
14579        // must surface a projected [`crate::AplicacaoSpec`] whose
14580        // `politicas().timeout()` field byte-equals the outer
14581        // composite's `timeout` scalar (the fold must project the
14582        // authored composite verbatim), a `Caixa { politicas:
14583        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
14584        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
14585        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
14586        // fold's empty-composite arm collapses to the same default the
14587        // author-omitted arm does), and a `Caixa { politicas: None,
14588        // kind: Aplicacao, .. }` must surface an
14589        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
14590        // [`crate::aplicacao::MeshPolicy::default`] (the "author
14591        // omitted the slot entirely" arm folds through the
14592        // `unwrap_or_default` onto the cluster-default). The triad
14593        // jointly pins the accessor + Aplicacao-composition seed
14594        // composition: any future silent detour that had the accessor
14595        // divert the raw slot away from the seed's fold (an operator-
14596        // resolved overlay's default-fold arm silently differing from
14597        // the raw slot's default-fold arm) would silently split the
14598        // build-time mesh-artifact emission gate from the caixa-mesh
14599        // renderer's Aplicacao-view input at the composition boundary.
14600        use crate::aplicacao::MeshPolicy;
14601        use std::time::Duration;
14602        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
14603            timeout: Some(Duration::from_secs(30)),
14604            ..Default::default()
14605        }));
14606        let view = c.aplicacao_view().unwrap();
14607        assert_eq!(
14608            view.politicas().timeout(),
14609            Some(Duration::from_secs(30)),
14610            "Caixa::aplicacao_view must fold the authored :politicas \
14611             :timeout scalar through the accessor verbatim onto the \
14612             projected AplicacaoSpec — a future silent detour at the \
14613             seed's fold arm would surface here as a projected-scalar \
14614             drift (got {:?})",
14615            view.politicas().timeout(),
14616        );
14617        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
14618        let view = c.aplicacao_view().unwrap();
14619        assert_eq!(
14620            view.politicas(),
14621            &MeshPolicy::default(),
14622            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
14623             through the accessor onto MeshPolicy::default — the empty- \
14624             composite arm collapses to the same default the author- \
14625             omitted arm does (got {:?})",
14626            view.politicas(),
14627        );
14628        let c = caixa_aplicacao_with_politicas(None);
14629        let view = c.aplicacao_view().unwrap();
14630        assert_eq!(
14631            view.politicas(),
14632            &MeshPolicy::default(),
14633            "Caixa::aplicacao_view must fold None through the accessor's \
14634             unwrap_or_default onto MeshPolicy::default — the author- \
14635             omitted arm must route through the accessor's None-return \
14636             unchanged (got {:?})",
14637            view.politicas(),
14638        );
14639    }
14640
14641    #[test]
14642    fn politicas_projects_option_ref_by_borrow() {
14643        // The by-borrow pin: [`Caixa::politicas`] returns
14644        // `Option<&MeshPolicy>` by borrow — the returned reference
14645        // borrows the underlying `Option<MeshPolicy>` storage of the
14646        // `:politicas` slot and the accessor must not clone the
14647        // backing composite on every call. Peer of the sibling
14648        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
14649        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
14650        // pins on the outer top-level [`Caixa`]
14651        // `Option<&Composite>`-return sub-family — extended here to
14652        // the third axis of the same sub-family: the accessor's
14653        // returned reference must borrow from `&self` (the returned
14654        // reference's lifetime is tied to `&self`), and calling the
14655        // accessor twice on the same [`Caixa`] must yield references
14656        // that are pointer-equal (the underlying byte-buffer is the
14657        // storage `MeshPolicy`'s allocation, not a fresh copy) as
14658        // well as value-equal (idempotent, no side effects on
14659        // `&self`).
14660        //
14661        // Pins against a future silent detour that returned an owned
14662        // `MeshPolicy` (which would type-check via the `Clone` impl
14663        // but silently clone on every call), a `&MeshPolicy` panic-
14664        // return on the `None` arm (which would collapse the load-
14665        // bearing `Option` presence-bit into a runtime panic), or a
14666        // one-arm-only accessor that returned a saturating composite
14667        // on some sentinel input.
14668        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
14669        use std::time::Duration;
14670        for politicas in [
14671            Some(MeshPolicy::default()),
14672            Some(MeshPolicy {
14673                timeout: Some(Duration::from_secs(30)),
14674                retries: Some(3),
14675                circuit_breaker: Some(CircuitBreaker {
14676                    max_failures: 5,
14677                    window: Duration::from_secs(60),
14678                }),
14679                mtls_required: Some(true),
14680                rate_limit: Some(RateLimit {
14681                    rate: 100,
14682                    window: Duration::from_secs(1),
14683                }),
14684            }),
14685        ] {
14686            let c = caixa_aplicacao_with_politicas(politicas.clone());
14687            let first = c.politicas().unwrap();
14688            let second = c.politicas().unwrap();
14689            assert_eq!(
14690                first, second,
14691                "Caixa::politicas must be idempotent — two successive \
14692                 calls on the same &self must return the same \
14693                 &MeshPolicy",
14694            );
14695            assert!(
14696                std::ptr::eq(first, second),
14697                "Caixa::politicas must borrow the underlying \
14698                 Option<MeshPolicy> storage — two successive calls \
14699                 must return references with the same backing pointer \
14700                 (a fresh MeshPolicy clone would change the pointer on \
14701                 every call)",
14702            );
14703            assert_eq!(
14704                Some(first),
14705                politicas.as_ref(),
14706                "Caixa::politicas must return :politicas verbatim by \
14707                 borrow — got {first:?}, expected {:?}",
14708                politicas.as_ref(),
14709            );
14710        }
14711        let c = caixa_aplicacao_with_politicas(None);
14712        assert!(
14713            c.politicas().is_none(),
14714            "Caixa::politicas must return None when :politicas is \
14715             absent — the author-omitted arm must project through the \
14716             accessor's Option::None unchanged",
14717        );
14718    }
14719
14720    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
14721
14722    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
14723        use crate::aplicacao::{Membro, WitContract};
14724        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14725        c.kind = CaixaKind::Aplicacao;
14726        c.membros = vec![Membro {
14727            caixa: "a".into(),
14728            versao: "^0.1".into(),
14729        }];
14730        c.contratos = vec![WitContract {
14731            de: "a".into(),
14732            para: "a".into(),
14733            wit: "wasi:http/proxy".into(),
14734            endpoint: Some("/x".into()),
14735            subject: None,
14736            slot: None,
14737        }];
14738        c.placement = placement;
14739        c
14740    }
14741
14742    #[test]
14743    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
14744        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
14745        // composite optional-composite-reference-shape pin:
14746        // [`Caixa::placement`] must return the `:placement` typed
14747        // `Option<Placement>` verbatim as an `Option<&Placement>`
14748        // reference over the same backing storage the raw
14749        // `self.placement.as_ref()` field access borrows from,
14750        // byte-equal across every representative fixture in the
14751        // accept-set — the author-omitted `None` shape (the
14752        // "cluster-default applies" partition every downstream mesh-
14753        // artifact emitter treats as "emit no `:placement` overlay"),
14754        // the empty-composite `Some(Placement { .. default })` shape
14755        // (`estrategia: SingleNode`, empty clusters, no shard-key /
14756        // affinity — the outer presence-bit is `Some` so
14757        // [`Caixa::declared_mesh_slots`] still pushes the
14758        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
14759        // `Replicated`-on-two-clusters fixture (the canonical shape a
14760        // stateless HTTP Aplicacao carries), and a fully-populated
14761        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
14762        // shape a stateful Akka-style cluster-sharding Aplicacao
14763        // carries).
14764        //
14765        // Pins against a future silent detour that returned a fresh-
14766        // cloned [`crate::aplicacao::Placement`] copy (which would
14767        // type-check via the `Clone` impl but silently break every
14768        // downstream caller that relied on the reference sharing the
14769        // composite's backing identity), a reference to an operator-
14770        // resolved overlay (the future per-cluster
14771        // `:placement-overrides` slot — its resolution must land at
14772        // exactly this accessor body, not silently divert the raw
14773        // slot away from the peer [`Caixa::declared_mesh_slots`]
14774        // enumerator's presence probe), a `None` →
14775        // `Some(Placement::default)` cluster-default projection (which
14776        // would collapse the load-bearing "author-omitted `:placement`
14777        // ⇒ cluster-default applies" partition the peer
14778        // [`Caixa::declared_mesh_slots`] enumerator and the peer
14779        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
14780        // read), or an axis-shuffled projection (a future detour that
14781        // swapped `clusters` and `affinity` through the accessor would
14782        // silently split the paired [`Caixa::aplicacao_view`] seed's
14783        // fold input from the sibling M3 mesh-artifact emitter's
14784        // projection input).
14785        //
14786        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
14787        // composite-reference accessor pin on the substrate primitive
14788        // — peer of the sibling
14789        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14790        // (b2bd9d7),
14791        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14792        // (35d8b52), and
14793        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
14794        // (5d23d29) opening triad pins on the outer top-level
14795        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14796        // here to the second of the three M3 mesh-slot axes so the
14797        // opening four-fifths of the outer `Option<&Composite>` sub-
14798        // family carries the same "byte-equal, borrow-shared,
14799        // presence-bit-preserved" outer-accessor discipline.
14800        use crate::aplicacao::{Placement, PlacementStrategy};
14801        let fixtures: Vec<Option<Placement>> = vec![
14802            None,
14803            Some(Placement::default()),
14804            Some(Placement {
14805                estrategia: PlacementStrategy::Replicated,
14806                clusters: vec!["rio".into(), "sao-paulo".into()],
14807                affinity: None,
14808                shard_key: None,
14809            }),
14810            Some(Placement {
14811                estrategia: PlacementStrategy::Sharded,
14812                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
14813                affinity: Some("data-locality".into()),
14814                shard_key: Some("$tenantId".into()),
14815            }),
14816        ];
14817        for placement in fixtures {
14818            let c = caixa_aplicacao_with_placement(placement.clone());
14819            assert_eq!(
14820                c.placement(),
14821                placement.as_ref(),
14822                "Caixa::placement must return :placement verbatim (got \
14823                 {:?}, expected {:?})",
14824                c.placement(),
14825                placement.as_ref(),
14826            );
14827            match (c.placement(), c.placement.as_ref()) {
14828                (Some(a), Some(b)) => assert!(
14829                    std::ptr::eq(a, b),
14830                    "Caixa::placement accessor and self.placement.as_ref() \
14831                     field access must borrow the same backing storage \
14832                     — the accessor is the substrate-primitive typed \
14833                     dispatch every downstream Aplicacao-distribution- \
14834                     overlay composite consumer must route through, and \
14835                     a reference-identity split would silently break \
14836                     every consumer that relied on the borrow sharing \
14837                     the composite's storage",
14838                ),
14839                (None, None) => {}
14840                _ => panic!(
14841                    "Caixa::placement presence bit must byte-equal \
14842                     self.placement.is_some() — a presence-bit drift \
14843                     would silently split the paired \
14844                     Caixa::aplicacao_view Aplicacao-composition seed's \
14845                     traversal head from the peer \
14846                     Caixa::declared_mesh_slots M3 declared-slot \
14847                     enumerator's presence probe",
14848                ),
14849            }
14850            assert_eq!(
14851                c.placement().is_some(),
14852                c.placement.is_some(),
14853                "Caixa::placement().is_some() must byte-equal \
14854                 self.placement.is_some() — a presence-bit drift would \
14855                 silently split every downstream Option<&Placement> \
14856                 consumer's partition on the cluster-default arm",
14857            );
14858        }
14859    }
14860
14861    #[test]
14862    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
14863        // Composition pin: [`Caixa::declared_mesh_slots`]'s
14864        // `:placement` presence-probe arm must key off
14865        // [`Caixa::placement`], not the raw `self.placement.is_some()`
14866        // field-probe. Structurally: a `Caixa { placement:
14867        // Some(Placement::default()), .. }` must still push
14868        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
14869        // presence bit is `Some`, so the M3 kind-coherence gate must
14870        // surface the slot as "declared" even when every per-axis
14871        // scalar defers to the cluster-default arm), and a `Caixa {
14872        // placement: None, .. }` must NOT push the label (the "author
14873        // omitted the slot entirely" partition). The pair jointly pins
14874        // the accessor + declared-slot enumerator composition: any
14875        // future silent detour that had the accessor collapse
14876        // `Some(Placement::default())` to `None` (a `.filter(|p|
14877        // p.clusters().is_empty().not())` projection) would silently
14878        // absorb the "declared but empty" arm at the accessor boundary
14879        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
14880        // kind-coherence gate would silently accept a struct-literal
14881        // `Caixa` carrying the drift.
14882        //
14883        // Peer of the sibling
14884        // `declared_servico_slots_limits_arm_routes_through_accessor`
14885        // (b2bd9d7),
14886        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14887        // (35d8b52), and
14888        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
14889        // (5d23d29) composition pins on the sibling `:limits` /
14890        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
14891        // — same "the enumerator gate must route through the
14892        // substrate-primitive typed dispatch" discipline extended onto
14893        // the second of the three M3 mesh-slot axes so the
14894        // [`Caixa::declared_mesh_slots`] enumerator carries the same
14895        // routing invariant on the `:placement` arm as the peer
14896        // `:politicas` arm.
14897        use crate::aplicacao::Placement;
14898        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
14899        let slots = c.declared_mesh_slots();
14900        assert!(
14901            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
14902            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
14903             when `:placement` is Some (even for Placement::default()) \
14904             — the accessor and the enumerator gate must route through \
14905             the same substrate-primitive typed dispatch on the outer \
14906             :placement presence bit (got slots={slots:?})",
14907        );
14908        let c = caixa_aplicacao_with_placement(None);
14909        let slots = c.declared_mesh_slots();
14910        assert!(
14911            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
14912            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
14913             when `:placement` is None — the author-omitted arm must \
14914             route through the accessor's None-return unchanged (got \
14915             slots={slots:?})",
14916        );
14917    }
14918
14919    #[test]
14920    fn aplicacao_view_placement_arm_folds_through_accessor() {
14921        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
14922        // Aplicacao-composition seed must fold through
14923        // [`Caixa::placement`], not the raw
14924        // `self.placement.clone().unwrap_or_default()` field-borrow.
14925        // Structurally: a `Caixa { placement: Some(Placement {
14926        // estrategia: Replicated, clusters: ["rio"], .. default }),
14927        // kind: Aplicacao, .. }` must surface a projected
14928        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
14929        // `placement().clusters()` byte-equal the outer composite's
14930        // authored values (the fold must project the authored
14931        // composite verbatim), a `Caixa { placement:
14932        // Some(Placement::default()), kind: Aplicacao, .. }` must
14933        // surface an [`crate::AplicacaoSpec`] whose `placement()`
14934        // byte-equals [`crate::aplicacao::Placement::default`] (the
14935        // fold's empty-composite arm collapses to the same default
14936        // the author-omitted arm does), and a `Caixa { placement:
14937        // None, kind: Aplicacao, .. }` must surface an
14938        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
14939        // [`crate::aplicacao::Placement::default`] (the "author
14940        // omitted the slot entirely" arm folds through the
14941        // `unwrap_or_default` onto the cluster-default). The triad
14942        // jointly pins the accessor + Aplicacao-composition seed
14943        // composition: any future silent detour that had the accessor
14944        // divert the raw slot away from the seed's fold (an operator-
14945        // resolved overlay's default-fold arm silently differing from
14946        // the raw slot's default-fold arm) would silently split the
14947        // build-time distribution-artifact emission gate from the
14948        // caixa-mesh renderer's Aplicacao-view input at the
14949        // composition boundary.
14950        use crate::aplicacao::{Placement, PlacementStrategy};
14951        let c = caixa_aplicacao_with_placement(Some(Placement {
14952            estrategia: PlacementStrategy::Replicated,
14953            clusters: vec!["rio".into()],
14954            affinity: None,
14955            shard_key: None,
14956        }));
14957        let view = c.aplicacao_view().unwrap();
14958        assert_eq!(
14959            view.placement().estrategia(),
14960            PlacementStrategy::Replicated,
14961            "Caixa::aplicacao_view must fold the authored :placement \
14962             :estrategia scalar through the accessor verbatim onto the \
14963             projected AplicacaoSpec — a future silent detour at the \
14964             seed's fold arm would surface here as a projected-scalar \
14965             drift (got {:?})",
14966            view.placement().estrategia(),
14967        );
14968        assert_eq!(
14969            view.placement().clusters(),
14970            &["rio"],
14971            "Caixa::aplicacao_view must fold the authored :placement \
14972             :clusters list through the accessor verbatim onto the \
14973             projected AplicacaoSpec — a future silent detour at the \
14974             seed's fold arm would surface here as a projected-list \
14975             drift (got {:?})",
14976            view.placement().clusters(),
14977        );
14978        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
14979        let view = c.aplicacao_view().unwrap();
14980        assert_eq!(
14981            view.placement(),
14982            &Placement::default(),
14983            "Caixa::aplicacao_view must fold Some(Placement::default()) \
14984             through the accessor onto Placement::default — the empty- \
14985             composite arm collapses to the same default the author- \
14986             omitted arm does (got {:?})",
14987            view.placement(),
14988        );
14989        let c = caixa_aplicacao_with_placement(None);
14990        let view = c.aplicacao_view().unwrap();
14991        assert_eq!(
14992            view.placement(),
14993            &Placement::default(),
14994            "Caixa::aplicacao_view must fold None through the accessor's \
14995             unwrap_or_default onto Placement::default — the author- \
14996             omitted arm must route through the accessor's None-return \
14997             unchanged (got {:?})",
14998            view.placement(),
14999        );
15000    }
15001
15002    #[test]
15003    fn placement_projects_option_ref_by_borrow() {
15004        // The by-borrow pin: [`Caixa::placement`] returns
15005        // `Option<&Placement>` by borrow — the returned reference
15006        // borrows the underlying `Option<Placement>` storage of the
15007        // `:placement` slot and the accessor must not clone the
15008        // backing composite on every call. Peer of the sibling
15009        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
15010        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
15011        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
15012        // pins on the outer top-level [`Caixa`]
15013        // `Option<&Composite>`-return sub-family — extended here to
15014        // the fourth axis of the same sub-family: the accessor's
15015        // returned reference must borrow from `&self` (the returned
15016        // reference's lifetime is tied to `&self`), and calling the
15017        // accessor twice on the same [`Caixa`] must yield references
15018        // that are pointer-equal (the underlying byte-buffer is the
15019        // storage `Placement`'s allocation, not a fresh copy) as well
15020        // as value-equal (idempotent, no side effects on `&self`).
15021        //
15022        // Pins against a future silent detour that returned an owned
15023        // `Placement` (which would type-check via the `Clone` impl
15024        // but silently clone on every call), a `&Placement` panic-
15025        // return on the `None` arm (which would collapse the load-
15026        // bearing `Option` presence-bit into a runtime panic), or a
15027        // one-arm-only accessor that returned a saturating composite
15028        // on some sentinel input.
15029        use crate::aplicacao::{Placement, PlacementStrategy};
15030        for placement in [
15031            Some(Placement::default()),
15032            Some(Placement {
15033                estrategia: PlacementStrategy::Sharded,
15034                clusters: vec!["rio".into(), "sao-paulo".into()],
15035                affinity: Some("data-locality".into()),
15036                shard_key: Some("$tenantId".into()),
15037            }),
15038        ] {
15039            let c = caixa_aplicacao_with_placement(placement.clone());
15040            let first = c.placement().unwrap();
15041            let second = c.placement().unwrap();
15042            assert_eq!(
15043                first, second,
15044                "Caixa::placement must be idempotent — two successive \
15045                 calls on the same &self must return the same \
15046                 &Placement",
15047            );
15048            assert!(
15049                std::ptr::eq(first, second),
15050                "Caixa::placement must borrow the underlying \
15051                 Option<Placement> storage — two successive calls \
15052                 must return references with the same backing pointer \
15053                 (a fresh Placement clone would change the pointer on \
15054                 every call)",
15055            );
15056            assert_eq!(
15057                Some(first),
15058                placement.as_ref(),
15059                "Caixa::placement must return :placement verbatim by \
15060                 borrow — got {first:?}, expected {:?}",
15061                placement.as_ref(),
15062            );
15063        }
15064        let c = caixa_aplicacao_with_placement(None);
15065        assert!(
15066            c.placement().is_none(),
15067            "Caixa::placement must return None when :placement is \
15068             absent — the author-omitted arm must project through the \
15069             accessor's Option::None unchanged",
15070        );
15071    }
15072
15073    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
15074
15075    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
15076        use crate::aplicacao::{Membro, WitContract};
15077        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15078        c.kind = CaixaKind::Aplicacao;
15079        c.membros = vec![Membro {
15080            caixa: "a".into(),
15081            versao: "^0.1".into(),
15082        }];
15083        c.contratos = vec![WitContract {
15084            de: "a".into(),
15085            para: "a".into(),
15086            wit: "wasi:http/proxy".into(),
15087            endpoint: Some("/x".into()),
15088            subject: None,
15089            slot: None,
15090        }];
15091        c.entrada = entrada;
15092        c
15093    }
15094
15095    #[test]
15096    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
15097        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
15098        // composite optional-composite-reference-shape pin:
15099        // [`Caixa::entrada`] must return the `:entrada` typed
15100        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
15101        // reference over the same backing storage the raw
15102        // `self.entrada.as_ref()` field access borrows from,
15103        // byte-equal across every representative fixture in the
15104        // accept-set — the author-omitted `None` shape (the
15105        // "cluster-internal Aplicacao" partition every downstream
15106        // Gateway-API emitter treats as "emit no listener + no
15107        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
15108        // (empty `paths` — the resolved-paths fallback the peer
15109        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
15110        // onto the substrate catch-all), and a fully-populated
15111        // multi-path-with-non-default-port fixture (the canonical
15112        // shape a public HTTP Aplicacao carries).
15113        //
15114        // Pins against a future silent detour that returned a fresh-
15115        // cloned [`crate::aplicacao::Entrada`] copy (which would
15116        // type-check via the `Clone` impl but silently break every
15117        // downstream caller that relied on the reference sharing the
15118        // composite's backing identity), a reference to an operator-
15119        // resolved overlay (the future per-cluster
15120        // `:entrada-overrides` slot — its resolution must land at
15121        // exactly this accessor body, not silently divert the raw
15122        // slot away from the peer [`Caixa::declared_mesh_slots`]
15123        // enumerator's presence probe), or an axis-shuffled projection
15124        // (a future detour that swapped `host` and `para` through the
15125        // accessor would silently split the paired
15126        // [`Caixa::aplicacao_view`] seed's forward input from the
15127        // sibling M3 gateway-artifact emitter's projection input).
15128        //
15129        // Fifth and final outer top-level [`Caixa`]
15130        // `Option<&Composite>`-return composite-reference accessor pin
15131        // on the substrate primitive — peer of the sibling
15132        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15133        // (b2bd9d7),
15134        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
15135        // (35d8b52),
15136        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
15137        // (5d23d29), and
15138        // `placement_returns_placement_option_ref_verbatim_across_permutations`
15139        // (4fb8074) opening tetrad pins on the outer top-level
15140        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
15141        // here to the third and final M3 mesh-slot axis so the closed
15142        // outer `Option<&Composite>` sub-family carries the same
15143        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
15144        // accessor discipline across all five arms.
15145        use crate::aplicacao::Entrada;
15146        let fixtures: Vec<Option<Entrada>> = vec![
15147            None,
15148            Some(Entrada {
15149                host: "checkout.quero.cloud".into(),
15150                para: "gateway".into(),
15151                paths: Vec::new(),
15152                port: crate::DEFAULT_SERVICO_PORT,
15153            }),
15154            Some(Entrada {
15155                host: "api.pleme.io".into(),
15156                para: "public-api".into(),
15157                paths: vec!["/v1".into(), "/v2".into()],
15158                port: 8080,
15159            }),
15160        ];
15161        for entrada in fixtures {
15162            let c = caixa_aplicacao_with_entrada(entrada.clone());
15163            assert_eq!(
15164                c.entrada(),
15165                entrada.as_ref(),
15166                "Caixa::entrada must return :entrada verbatim (got \
15167                 {:?}, expected {:?})",
15168                c.entrada(),
15169                entrada.as_ref(),
15170            );
15171            match (c.entrada(), c.entrada.as_ref()) {
15172                (Some(a), Some(b)) => assert!(
15173                    std::ptr::eq(a, b),
15174                    "Caixa::entrada accessor and self.entrada.as_ref() \
15175                     field access must borrow the same backing storage \
15176                     — the accessor is the substrate-primitive typed \
15177                     dispatch every downstream Aplicacao-external- \
15178                     gateway composite consumer must route through, and \
15179                     a reference-identity split would silently break \
15180                     every consumer that relied on the borrow sharing \
15181                     the composite's storage",
15182                ),
15183                (None, None) => {}
15184                _ => panic!(
15185                    "Caixa::entrada presence bit must byte-equal \
15186                     self.entrada.is_some() — a presence-bit drift \
15187                     would silently split the paired \
15188                     Caixa::aplicacao_view Aplicacao-composition seed's \
15189                     traversal head from the peer \
15190                     Caixa::declared_mesh_slots M3 declared-slot \
15191                     enumerator's presence probe",
15192                ),
15193            }
15194            assert_eq!(
15195                c.entrada().is_some(),
15196                c.entrada.is_some(),
15197                "Caixa::entrada().is_some() must byte-equal \
15198                 self.entrada.is_some() — a presence-bit drift would \
15199                 silently split every downstream Option<&Entrada> \
15200                 consumer's partition on the cluster-internal arm",
15201            );
15202        }
15203    }
15204
15205    #[test]
15206    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
15207        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
15208        // presence-probe arm must key off [`Caixa::entrada`], not the
15209        // raw `self.entrada.is_some()` field-probe. Structurally: a
15210        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
15211        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
15212        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
15213        // presence bit is `Some`, so the M3 kind-coherence gate must
15214        // surface the slot as "declared" even when every per-axis
15215        // scalar defers to the substrate catch-all / default port),
15216        // and a `Caixa { entrada: None, .. }` must NOT push the label
15217        // (the "author omitted the slot entirely" partition). The pair
15218        // jointly pins the accessor + declared-slot enumerator
15219        // composition: any future silent detour that had the accessor
15220        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
15221        // `.filter(|e| !e.paths.is_empty())` projection) would silently
15222        // absorb the "declared but empty-paths" arm at the accessor
15223        // boundary and the
15224        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
15225        // coherence gate would silently accept a struct-literal
15226        // `Caixa` carrying the drift.
15227        //
15228        // Peer of the sibling
15229        // `declared_servico_slots_limits_arm_routes_through_accessor`
15230        // (b2bd9d7),
15231        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15232        // (35d8b52),
15233        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
15234        // (5d23d29), and
15235        // `declared_mesh_slots_placement_arm_routes_through_accessor`
15236        // (4fb8074) composition pins on the sibling `:limits` /
15237        // `:behavior` / `:politicas` / `:placement` outer-
15238        // `Option<&Composite>` arms — same "the enumerator gate must
15239        // route through the substrate-primitive typed dispatch"
15240        // discipline extended onto the third and final M3 mesh-slot
15241        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
15242        // carries the routing invariant on every M3 mesh-slot arm.
15243        use crate::aplicacao::Entrada;
15244        let c = caixa_aplicacao_with_entrada(Some(Entrada {
15245            host: "checkout.quero.cloud".into(),
15246            para: "gateway".into(),
15247            paths: Vec::new(),
15248            port: crate::DEFAULT_SERVICO_PORT,
15249        }));
15250        let slots = c.declared_mesh_slots();
15251        assert!(
15252            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
15253            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
15254             `:entrada` is Some (even for empty-paths / default-port) \
15255             — the accessor and the enumerator gate must route through \
15256             the same substrate-primitive typed dispatch on the outer \
15257             :entrada presence bit (got slots={slots:?})",
15258        );
15259        let c = caixa_aplicacao_with_entrada(None);
15260        let slots = c.declared_mesh_slots();
15261        assert!(
15262            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
15263            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
15264             when `:entrada` is None — the author-omitted arm must \
15265             route through the accessor's None-return unchanged (got \
15266             slots={slots:?})",
15267        );
15268    }
15269
15270    #[test]
15271    fn aplicacao_view_entrada_arm_folds_through_accessor() {
15272        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
15273        // Aplicacao-composition seed must fold through
15274        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
15275        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
15276        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
15277        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
15278        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
15279        // equals the outer composite's authored value (the fold must
15280        // project the authored composite verbatim), and a `Caixa {
15281        // entrada: None, kind: Aplicacao, .. }` must surface an
15282        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
15283        // "author omitted the slot entirely" arm folds through the
15284        // accessor's `Option::cloned` onto the same `None` presence
15285        // bit — unlike the peer `:politicas` / `:placement` arms
15286        // `:entrada` has no cluster-default fold, the omitted arm
15287        // stays omitted). The pair jointly pins the accessor +
15288        // Aplicacao-composition seed composition: any future silent
15289        // detour that had the accessor divert the raw slot away from
15290        // the seed's fold (an operator-resolved overlay's forward arm
15291        // silently differing from the raw slot's forward arm) would
15292        // silently split the build-time gateway-artifact emission gate
15293        // from the caixa-mesh renderer's Aplicacao-view input at the
15294        // composition boundary.
15295        use crate::aplicacao::Entrada;
15296        let authored = Entrada {
15297            host: "api.pleme.io".into(),
15298            para: "public-api".into(),
15299            paths: vec!["/v1".into()],
15300            port: 8080,
15301        };
15302        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
15303        let view = c.aplicacao_view().unwrap();
15304        assert_eq!(
15305            view.entrada(),
15306            Some(&authored),
15307            "Caixa::aplicacao_view must fold the authored :entrada \
15308             composite through the accessor verbatim onto the \
15309             projected AplicacaoSpec — a future silent detour at the \
15310             seed's fold arm would surface here as a projected- \
15311             composite drift (got {:?})",
15312            view.entrada(),
15313        );
15314        let c = caixa_aplicacao_with_entrada(None);
15315        let view = c.aplicacao_view().unwrap();
15316        assert!(
15317            view.entrada().is_none(),
15318            "Caixa::aplicacao_view must fold None through the \
15319             accessor's Option::cloned onto None — the author- \
15320             omitted arm must route through the accessor's None-return \
15321             unchanged (got {:?})",
15322            view.entrada(),
15323        );
15324    }
15325
15326    #[test]
15327    fn entrada_projects_option_ref_by_borrow() {
15328        // The by-borrow pin: [`Caixa::entrada`] returns
15329        // `Option<&Entrada>` by borrow — the returned reference
15330        // borrows the underlying `Option<Entrada>` storage of the
15331        // `:entrada` slot and the accessor must not clone the backing
15332        // composite on every call. Peer of the sibling
15333        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
15334        // `behavior_projects_option_ref_by_borrow` (35d8b52),
15335        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
15336        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
15337        // borrow pins on the outer top-level [`Caixa`]
15338        // `Option<&Composite>`-return sub-family — extended here to
15339        // the fifth and final axis of the same sub-family, closing
15340        // the discipline: the accessor's returned reference must
15341        // borrow from `&self` (the returned reference's lifetime is
15342        // tied to `&self`), and calling the accessor twice on the
15343        // same [`Caixa`] must yield references that are pointer-equal
15344        // (the underlying byte-buffer is the storage `Entrada`'s
15345        // allocation, not a fresh copy) as well as value-equal
15346        // (idempotent, no side effects on `&self`).
15347        //
15348        // Pins against a future silent detour that returned an owned
15349        // `Entrada` (which would type-check via the `Clone` impl but
15350        // silently clone on every call), a `&Entrada` panic-return on
15351        // the `None` arm (which would collapse the load-bearing
15352        // `Option` presence-bit into a runtime panic), or a one-arm-
15353        // only accessor that returned a saturating composite on some
15354        // sentinel input.
15355        use crate::aplicacao::Entrada;
15356        for entrada in [
15357            Some(Entrada {
15358                host: "checkout.quero.cloud".into(),
15359                para: "gateway".into(),
15360                paths: Vec::new(),
15361                port: crate::DEFAULT_SERVICO_PORT,
15362            }),
15363            Some(Entrada {
15364                host: "api.pleme.io".into(),
15365                para: "public-api".into(),
15366                paths: vec!["/v1".into(), "/v2".into()],
15367                port: 8080,
15368            }),
15369        ] {
15370            let c = caixa_aplicacao_with_entrada(entrada.clone());
15371            let first = c.entrada().unwrap();
15372            let second = c.entrada().unwrap();
15373            assert_eq!(
15374                first, second,
15375                "Caixa::entrada must be idempotent — two successive \
15376                 calls on the same &self must return the same &Entrada",
15377            );
15378            assert!(
15379                std::ptr::eq(first, second),
15380                "Caixa::entrada must borrow the underlying \
15381                 Option<Entrada> storage — two successive calls must \
15382                 return references with the same backing pointer (a \
15383                 fresh Entrada clone would change the pointer on every \
15384                 call)",
15385            );
15386            assert_eq!(
15387                Some(first),
15388                entrada.as_ref(),
15389                "Caixa::entrada must return :entrada verbatim by \
15390                 borrow — got {first:?}, expected {:?}",
15391                entrada.as_ref(),
15392            );
15393        }
15394        let c = caixa_aplicacao_with_entrada(None);
15395        assert!(
15396            c.entrada().is_none(),
15397            "Caixa::entrada must return None when :entrada is absent \
15398             — the author-omitted arm must project through the \
15399             accessor's Option::None unchanged",
15400        );
15401    }
15402
15403    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
15404
15405    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
15406        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15407        c.estrategia = estrategia;
15408        c
15409    }
15410
15411    #[test]
15412    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
15413        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
15414        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
15415        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
15416        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
15417        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
15418        // over the same discriminant the raw `self.estrategia` field
15419        // access carries, byte-equal across every representative fixture
15420        // in the accept-set — the author-omitted `None` shape (the
15421        // "defer to [`RestartStrategy::default`] through the
15422        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
15423        // every non-`Supervisor`-kind `defcaixa` carries by
15424        // `#[serde(default)]`), and each of the four closed-set variants
15425        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
15426        // / [`RestartStrategy::RestForOne`] /
15427        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
15428        // partitions on.
15429        //
15430        // Pins against a future silent detour that re-derived the
15431        // strategy from a peer axis (an accidental fallback to
15432        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
15433        // collapse that read the outer `:children` list-length axis into
15434        // the strategy discriminator at the accessor boundary), a
15435        // stale-derive detour that substituted [`RestartStrategy::default`]
15436        // when the outer `Option` held `None` (which would silently
15437        // collapse the load-bearing "author explicitly declared
15438        // `:estrategia OneForOne`" vs "author omitted the slot and
15439        // inherited the default" partition the [`Self::declared_supervisor_slots`]
15440        // presence-probe reads — the enumerator gate would still push
15441        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
15442        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
15443        // kind-coherence gate's traversal head from the
15444        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
15445        // composition head), a reference to an operator-resolved overlay
15446        // (the future per-cluster `:estrategia-overrides` slot — its
15447        // resolution must land at exactly this accessor body, not
15448        // silently divert the raw slot away from a second consumer), or
15449        // an axis-remap projection (a future detour that mapped
15450        // `OneForAll` through the accessor onto `OneForOne` would
15451        // silently split every downstream sibling-restart-strategy
15452        // consumer's per-arm fan-out).
15453        //
15454        // First outer top-level [`Caixa`] `Option<Copy>`-return
15455        // supervisor-tree-slot flat-spread accessor pin on the substrate
15456        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
15457        // projection pattern the sibling per-`Caixa` `:max-restarts` /
15458        // `:restart-window` future outer-scalar pins fold on. Peer of
15459        // the inner-altitude
15460        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
15461        // (eafb619) pin on the post-composition [`SupervisorSpec`]
15462        // altitude — same "the substrate-primitive accessor must byte-
15463        // equal the raw field access verbatim across every author-
15464        // declared value" discipline extended onto the pre-composition
15465        // outer author-surface [`Caixa`] altitude. Peer of the closed
15466        // outer-`Caixa` `Option<&Composite>` composite-reference family
15467        // the sibling `limits` / `behavior` / `politicas` / `placement` /
15468        // `entrada`
15469        // `..._returns_..._option_ref_verbatim_across_permutations` pins
15470        // already carry on the outer `Option<&Composite>` altitude.
15471        use crate::supervisor::RestartStrategy;
15472        let fixtures: Vec<Option<RestartStrategy>> = vec![
15473            None,
15474            Some(RestartStrategy::OneForOne),
15475            Some(RestartStrategy::OneForAll),
15476            Some(RestartStrategy::RestForOne),
15477            Some(RestartStrategy::SimpleOneForOne),
15478        ];
15479        for estrategia in fixtures {
15480            let c = caixa_with_estrategia(estrategia);
15481            assert_eq!(
15482                c.estrategia(),
15483                estrategia,
15484                "Caixa::estrategia must return :estrategia verbatim (got \
15485                 {:?}, expected {:?})",
15486                c.estrategia(),
15487                estrategia,
15488            );
15489            assert_eq!(
15490                c.estrategia(),
15491                c.estrategia,
15492                "Caixa::estrategia accessor and self.estrategia field \
15493                 access must byte-equal — the accessor is the substrate-\
15494                 primitive typed dispatch every downstream supervisor-\
15495                 tree flat-spread consumer must route through, and a \
15496                 discriminant split would silently break every consumer \
15497                 that relied on the accessor sharing the field's own \
15498                 Option<Copy> shape",
15499            );
15500            assert_eq!(
15501                c.estrategia().is_some(),
15502                c.estrategia.is_some(),
15503                "Caixa::estrategia().is_some() must byte-equal \
15504                 self.estrategia.is_some() — a presence-bit drift would \
15505                 silently split the paired Caixa::declared_supervisor_slots \
15506                 presence-probe arm from the Caixa::supervisor_view \
15507                 unwrap_or_default() fold's composition input",
15508            );
15509        }
15510    }
15511
15512    #[test]
15513    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
15514        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15515        // `:estrategia` presence-probe arm must key off
15516        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
15517        // field-probe. Structurally: every `Caixa { estrategia:
15518        // Some(RestartStrategy::_), .. }` variant must push
15519        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
15520        // (the presence bit is `Some` for every closed-set variant, so
15521        // the M2 supervisor-tree kind-coherence gate must surface the
15522        // slot as "declared" regardless of which variant the author
15523        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
15524        // the label (the "author omitted the slot entirely, deferring
15525        // to [`RestartStrategy::default`] through the supervisor_view
15526        // fold" partition). The pair jointly pins the accessor +
15527        // declared-slot enumerator composition: any future silent detour
15528        // that had the accessor collapse `Some(RestartStrategy::default())`
15529        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
15530        // projection) would silently absorb the "declared but default-
15531        // valued" arm at the accessor boundary and the
15532        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
15533        // coherence gate would silently accept a struct-literal `Caixa`
15534        // carrying the drift.
15535        //
15536        // Peer of the sibling per-`Caixa`
15537        // `declared_servico_slots_limits_arm_routes_through_accessor`
15538        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
15539        // `Option<&LimitsSpec>` composition axis — same "the enumerator
15540        // gate must route through the substrate-primitive typed
15541        // dispatch" discipline extended onto the flat-spread M2
15542        // supervisor-tree `Option<RestartStrategy>`-composition surface,
15543        // opening the outer-`Caixa` supervisor-tree-slot arm of the
15544        // composition-pin family.
15545        use crate::supervisor::RestartStrategy;
15546        for estrategia in [
15547            RestartStrategy::OneForOne,
15548            RestartStrategy::OneForAll,
15549            RestartStrategy::RestForOne,
15550            RestartStrategy::SimpleOneForOne,
15551        ] {
15552            let c = caixa_with_estrategia(Some(estrategia));
15553            let slots = c.declared_supervisor_slots();
15554            assert!(
15555                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
15556                "declared_supervisor_slots must push \
15557                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
15558                 Some({estrategia:?}) — the accessor and the enumerator \
15559                 gate must route through the same substrate-primitive \
15560                 typed dispatch on the outer :estrategia presence bit \
15561                 (got slots={slots:?})",
15562            );
15563        }
15564        let c = caixa_with_estrategia(None);
15565        let slots = c.declared_supervisor_slots();
15566        assert!(
15567            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
15568            "declared_supervisor_slots must NOT push \
15569             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
15570             — the author-omitted arm must route through the accessor's \
15571             None-return unchanged (got slots={slots:?})",
15572        );
15573    }
15574
15575    #[test]
15576    fn supervisor_view_estrategia_arm_routes_through_accessor() {
15577        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
15578        // [`SupervisorSpec`] construction arm must key off
15579        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
15580        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
15581        // for every `:kind Supervisor` `Caixa` carrying an author-
15582        // declared `Some(RestartStrategy::_)` variant, the composed
15583        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
15584        // outer accessor's declared variant unchanged; and for a
15585        // `:kind Supervisor` `Caixa` carrying `None`, the composed
15586        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
15587        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
15588        // arm the flat-spread `unwrap_or_default()` fold projects to on
15589        // the author-omitted arm — this is the *composition* between the
15590        // outer `Option<RestartStrategy>` accessor's presence-bit
15591        // surface and the inner post-composition non-`Option`
15592        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
15593        // pins the accessor + supervisor_view composition: any future
15594        // silent detour that had the accessor promote `None` to
15595        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
15596        // projection) would silently collapse the two arms into one at
15597        // the accessor boundary and the [`Self::declared_supervisor_slots`]
15598        // presence probe would silently drift from the composition site.
15599        //
15600        // Peer of the sibling M2 supervisor-slot post-composition
15601        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
15602        // pin on the [`SupervisorSpec::validate`] altitude — this pin
15603        // extends that inner-altitude accessor-routing discipline onto
15604        // the pre-composition outer author-surface [`Caixa`] altitude,
15605        // pinning the composition edge between the flat-spread outer
15606        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
15607        // `RestartStrategy` axes.
15608        use crate::CaixaKind;
15609        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
15610        for estrategia in [
15611            RestartStrategy::OneForOne,
15612            RestartStrategy::OneForAll,
15613            RestartStrategy::RestForOne,
15614            RestartStrategy::SimpleOneForOne,
15615        ] {
15616            let mut c = caixa_with_estrategia(Some(estrategia));
15617            c.kind = CaixaKind::Supervisor;
15618            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
15619            // shape partition through the [`gen_platform::IsVariant`]
15620            // derive-generated
15621            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
15622            // than the raw `matches!(estrategia, RestartStrategy::
15623            // SimpleOneForOne)` open-coded pattern-match — same closed-
15624            // set-typed-enum arm-discriminator dispatch discipline the
15625            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
15626            // convergence (915a934) extended onto its two paired positive
15627            // / negated `matches!` sites and the peer
15628            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
15629            // predicate convergence (766ec63) extended onto the M3 mesh-
15630            // slot per-`:placement` distribution-strategy discriminator
15631            // axis. See the sibling `supervisor::tests::
15632            // round_trip_all_strategies` and
15633            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
15634            // fixtures — the three sites (all test-only,
15635            // acknowledged in 915a934's Prior-commits footnote as the
15636            // outstanding follow-up) now consult one typed dispatch on
15637            // the substrate primitive.
15638            c.children = if estrategia.is_simple_one_for_one() {
15639                Vec::new()
15640            } else {
15641                vec![ChildSpec {
15642                    caixa: "worker".into(),
15643                    versao: "^0.1".into(),
15644                    restart: RestartPolicy::Permanent,
15645                }]
15646            };
15647            let view = c.supervisor_view().expect(
15648                "supervisor_view must materialize a SupervisorSpec for a \
15649                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
15650            );
15651            assert_eq!(
15652                view.estrategia(),
15653                c.estrategia().unwrap(),
15654                "supervisor_view must carry the outer Caixa::estrategia() \
15655                 declared variant onto the composed SupervisorSpec.estrategia \
15656                 field verbatim on the Some arm (got {:?}, expected {:?})",
15657                view.estrategia(),
15658                c.estrategia().unwrap(),
15659            );
15660        }
15661        // The author-omitted arm: outer `None` → composed
15662        // `RestartStrategy::default()` through the flat-spread
15663        // `unwrap_or_default()` fold.
15664        let mut c = caixa_with_estrategia(None);
15665        c.kind = CaixaKind::Supervisor;
15666        // Populate children so the sibling supervisor slots are coherent
15667        // for the [`Self::supervisor_view`] projection; the `:estrategia`
15668        // arm still defers to [`RestartStrategy::default`] on the
15669        // author-omitted arm even when the sibling slots carry values.
15670        c.children = vec![ChildSpec {
15671            caixa: "worker".into(),
15672            versao: "^0.1".into(),
15673            restart: RestartPolicy::Permanent,
15674        }];
15675        let view = c.supervisor_view().expect(
15676            "supervisor_view must materialize a SupervisorSpec for a \
15677             :kind Supervisor Caixa carrying a None `:estrategia` slot",
15678        );
15679        assert_eq!(
15680            view.estrategia(),
15681            RestartStrategy::default(),
15682            "supervisor_view must project the outer Caixa::estrategia() \
15683             None arm onto RestartStrategy::default() through the flat-\
15684             spread unwrap_or_default() fold (got {:?}, expected {:?})",
15685            view.estrategia(),
15686            RestartStrategy::default(),
15687        );
15688        assert!(
15689            c.estrategia().is_none(),
15690            "Caixa::estrategia() must remain None on the author-omitted \
15691             arm — the supervisor_view fold must not mutate the outer \
15692             flat-spread presence bit",
15693        );
15694    }
15695
15696    #[test]
15697    fn estrategia_projects_option_by_copy() {
15698        // The by-`Copy` pin: [`Caixa::estrategia`] returns
15699        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
15700        // the accessor does not borrow `&self` past the call (no
15701        // lifetime on the return type), and calling the accessor twice
15702        // on the same [`Caixa`] must yield discriminant-equal values
15703        // (idempotent, no side effects on `&self`). Peer of the sibling
15704        // outer-`Caixa` `Option<&Composite>` by-borrow
15705        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
15706        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
15707        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
15708        // `placement_projects_option_ref_by_borrow` (4fb8074) /
15709        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
15710        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
15711        // extended here to the outer-`Caixa` `Option<Copy>`-return
15712        // flat-spread axis. The `Copy` discipline replaces the pointer-
15713        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
15714        // `Copy` discriminant is definitionally the same discriminant, so
15715        // the axis reduces to discriminant equality).
15716        //
15717        // Pins against a future silent detour that returned a fresh
15718        // `Option<&RestartStrategy>` (which would type-check but silently
15719        // introduce a borrow of `&self` past the call, collapsing the
15720        // load-bearing "no lifetime on the return type" `Copy` projection
15721        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
15722        // read side effect that flipped the outer discriminant on
15723        // successive calls, or an axis-remap projection that returned a
15724        // different variant than the field storage.
15725        use crate::supervisor::RestartStrategy;
15726        for estrategia in [
15727            Some(RestartStrategy::OneForOne),
15728            Some(RestartStrategy::OneForAll),
15729            Some(RestartStrategy::RestForOne),
15730            Some(RestartStrategy::SimpleOneForOne),
15731        ] {
15732            let c = caixa_with_estrategia(estrategia);
15733            let first = c.estrategia();
15734            let second = c.estrategia();
15735            assert_eq!(
15736                first, second,
15737                "Caixa::estrategia must be idempotent — two successive \
15738                 calls on the same &self must return the same \
15739                 Option<RestartStrategy>",
15740            );
15741            assert_eq!(
15742                first, estrategia,
15743                "Caixa::estrategia must return :estrategia verbatim by \
15744                 Copy — got {first:?}, expected {estrategia:?}",
15745            );
15746        }
15747        let c = caixa_with_estrategia(None);
15748        assert!(
15749            c.estrategia().is_none(),
15750            "Caixa::estrategia must return None when :estrategia is \
15751             absent — the author-omitted arm must project through the \
15752             accessor's Option::None unchanged",
15753        );
15754    }
15755
15756    // ── Caixa::max_restarts / Caixa::restart_window —
15757    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
15758    //    (Option<u32> / Option<&str>) folding on the ed04d3c
15759    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
15760
15761    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
15762        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15763        c.max_restarts = max_restarts;
15764        c
15765    }
15766
15767    fn caixa_supervisor_with_max_restarts_and_window(
15768        max_restarts: Option<u32>,
15769        restart_window: Option<&str>,
15770    ) -> Caixa {
15771        use crate::CaixaKind;
15772        use crate::supervisor::{ChildSpec, RestartPolicy};
15773        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
15774        c.kind = CaixaKind::Supervisor;
15775        c.max_restarts = max_restarts;
15776        c.restart_window = restart_window.map(str::to_string);
15777        c.children = vec![ChildSpec {
15778            caixa: "worker".into(),
15779            versao: "^0.1".into(),
15780            restart: RestartPolicy::Permanent,
15781        }];
15782        c
15783    }
15784
15785    #[test]
15786    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
15787        // Value-shape pin: [`Caixa::max_restarts`] returns the
15788        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
15789        // from the typed slot's own storage, byte-equal across the
15790        // author-omitted `None` arm (the "defer to the
15791        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
15792        // `{intensity, 5, 60}` default" partition every
15793        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
15794        // and each of the representative fixtures in the accept-set —
15795        // `0` (the zero-floor arm the peer
15796        // [`crate::supervisor::SupervisorSpec::validate`]
15797        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
15798        // the post-composition altitude — the accessor must ship the
15799        // raw slot verbatim so struct-literal fixtures continue to
15800        // expose the zero at the accessor boundary), the OTP-canonical
15801        // `5` default (`{intensity, 5, 60}` worker-supervisor from
15802        // Learn You Some Erlang), `1000` (the
15803        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
15804        // upper-bound gate accepts on the boundary), `u32::MAX` (a
15805        // past-the-cap sentinel that the substrate-primitive accessor
15806        // must still ship verbatim). Second outer top-level
15807        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
15808        // pin — folds on the sibling
15809        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
15810        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
15811        // onto the sibling `Option<u32>` restart-budget-count arm.
15812        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
15813        for max_restarts in fixtures {
15814            let c = caixa_with_max_restarts(max_restarts);
15815            assert_eq!(
15816                c.max_restarts(),
15817                max_restarts,
15818                "Caixa::max_restarts must return :max-restarts verbatim \
15819                 (got {:?}, expected {max_restarts:?})",
15820                c.max_restarts(),
15821            );
15822            assert_eq!(
15823                c.max_restarts(),
15824                c.max_restarts,
15825                "Caixa::max_restarts accessor and self.max_restarts \
15826                 field access must byte-equal — a presence-bit or count \
15827                 drift would silently split the paired \
15828                 Caixa::declared_supervisor_slots presence-probe arm \
15829                 from the Caixa::supervisor_view unwrap_or(5) fold's \
15830                 composition input",
15831            );
15832        }
15833    }
15834
15835    #[test]
15836    fn max_restarts_projects_option_by_copy() {
15837        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
15838        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
15839        // borrow `&self` past the call (no lifetime on the return type),
15840        // and calling the accessor twice on the same [`Caixa`] must
15841        // yield equal values (idempotent, no side effects). Peer of the
15842        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
15843        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
15844        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
15845            let c = caixa_with_max_restarts(max_restarts);
15846            let first = c.max_restarts();
15847            let second = c.max_restarts();
15848            assert_eq!(
15849                first, second,
15850                "Caixa::max_restarts must be idempotent — two successive \
15851                 calls on the same &self must return the same Option<u32>",
15852            );
15853            assert_eq!(
15854                first, max_restarts,
15855                "Caixa::max_restarts must return :max-restarts verbatim \
15856                 by Copy — got {first:?}, expected {max_restarts:?}",
15857            );
15858        }
15859    }
15860
15861    #[test]
15862    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
15863        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15864        // `:max-restarts` presence-probe arm must key off
15865        // [`Caixa::max_restarts`], not the raw
15866        // `self.max_restarts.is_some()` field-probe. Structurally: every
15867        // `Caixa { max_restarts: Some(_), .. }` variant must push
15868        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
15869        // list (the presence bit is `Some` for every representative
15870        // count, so the M2 kind-coherence gate must surface the slot as
15871        // "declared"), and a `Caixa { max_restarts: None, .. }` must
15872        // NOT push the label. Peer of the sibling
15873        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
15874        // (ed04d3c) composition pin — same routing-through-accessor
15875        // discipline extended onto the sibling flat-spread `Option<u32>`
15876        // arm.
15877        for max_restarts in [0u32, 5, 1000, u32::MAX] {
15878            let c = caixa_with_max_restarts(Some(max_restarts));
15879            let slots = c.declared_supervisor_slots();
15880            assert!(
15881                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
15882                "declared_supervisor_slots must push \
15883                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
15884                 is Some({max_restarts}) — the accessor and the \
15885                 enumerator gate must route through the same \
15886                 substrate-primitive typed dispatch on the outer \
15887                 :max-restarts presence bit (got slots={slots:?})",
15888            );
15889        }
15890        let c = caixa_with_max_restarts(None);
15891        let slots = c.declared_supervisor_slots();
15892        assert!(
15893            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
15894            "declared_supervisor_slots must NOT push \
15895             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
15896             None — the author-omitted arm must route through the \
15897             accessor's None-return unchanged (got slots={slots:?})",
15898        );
15899    }
15900
15901    #[test]
15902    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
15903        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
15904        // [`SupervisorSpec`] construction arm must key off
15905        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
15906        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
15907        // every `:kind Supervisor` `Caixa` carrying an author-declared
15908        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
15909        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
15910        // carrying `None`, the composed [`SupervisorSpec`]'s
15911        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
15912        // of the sibling
15913        // `supervisor_view_estrategia_arm_routes_through_accessor`
15914        // (ed04d3c) composition pin.
15915        for max_restarts in [1u32, 5, 1000] {
15916            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
15917            let view = c.supervisor_view().expect(
15918                "supervisor_view must materialize a SupervisorSpec for a \
15919                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
15920            );
15921            assert_eq!(
15922                view.max_restarts(),
15923                max_restarts,
15924                "supervisor_view must carry the outer \
15925                 Caixa::max_restarts() Some arm onto the composed \
15926                 SupervisorSpec.max_restarts field verbatim (got {}, \
15927                 expected {max_restarts})",
15928                view.max_restarts(),
15929            );
15930        }
15931        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
15932        let view = c.supervisor_view().expect(
15933            "supervisor_view must materialize a SupervisorSpec for a \
15934             :kind Supervisor Caixa carrying a None :max-restarts",
15935        );
15936        assert_eq!(
15937            view.max_restarts(),
15938            5,
15939            "supervisor_view must project the outer \
15940             Caixa::max_restarts() None arm onto the OTP-canonical \
15941             {{intensity, 5, 60}} default (5) through the flat-spread \
15942             unwrap_or(5) fold (got {})",
15943            view.max_restarts(),
15944        );
15945        assert!(
15946            c.max_restarts().is_none(),
15947            "Caixa::max_restarts() must remain None on the author-\
15948             omitted arm — the supervisor_view fold must not mutate \
15949             the outer flat-spread presence bit",
15950        );
15951    }
15952
15953    #[test]
15954    fn supervisor_view_estrategia_fallback_routes_through_lifted_default() {
15955        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
15956        // `:estrategia` arm must degrade onto the substrate-canonical
15957        // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
15958        // `pub const` — the Erlang/OTP-canonical `one_for_one` strategy
15959        // half of Learn You Some Erlang's `{one_for_one, intensity, 5, 60}`
15960        // worker-supervisor default — rather than the transitively-
15961        // derived [`crate::supervisor::RestartStrategy::default`] route
15962        // the prior `.unwrap_or_default()` fold reached for. Prior to the
15963        // lift the composition site carried `.unwrap_or_default()` with
15964        // no compile-time link back to the shared OTP-canonical strategy
15965        // default that the paired [`crate::supervisor::Default for
15966        // RestartStrategy`] impl and the [`crate::supervisor::Default for
15967        // SupervisorSpec`] impl's struct-literal `estrategia` field both
15968        // (now) route through the same lifted constant — so a future
15969        // rebrand of the OTP-canonical strategy default (an OTP
15970        // `rest_for_one` widening once the substrate discovers startup-
15971        // order-coupled child cohorts as the more common worker-
15972        // supervisor shape, a per-cluster overlay the operator pins
15973        // through the MESH-COMPOSITION §III.2 supervision-canary
15974        // `:estrategia-overrides` roadmap slot) would have had to migrate
15975        // the paired `MaxIntensity` + `Period` halves through the lifted
15976        // constants and the `one_for_one` half through a
15977        // `RestartStrategy::default()` route in lockstep or a
15978        // `:kind Supervisor` caixa carrying an author-omitted
15979        // `:estrategia` slot would silently resolve to a `SupervisorSpec`
15980        // whose `estrategia` disagreed with the paired
15981        // `SupervisorSpec::default()` view. Byte-parity against the
15982        // lifted constant closes the split. Peer of the sibling
15983        // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
15984        // composition pin on the paired `MaxIntensity` half + the
15985        // [`crate::supervisor::restart_strategy_default_routes_through_lifted_default`]
15986        // + [`crate::supervisor::supervisor_spec_default_estrategia_routes_through_lifted_default`]
15987        // pins on the sibling entry points onto the shared substrate
15988        // constant.
15989        use crate::CaixaKind;
15990        use crate::supervisor::{ChildSpec, RestartPolicy};
15991        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
15992        c.kind = CaixaKind::Supervisor;
15993        c.estrategia = None;
15994        c.children = vec![ChildSpec {
15995            caixa: "worker".into(),
15996            versao: "^0.1".into(),
15997            restart: RestartPolicy::Permanent,
15998        }];
15999        let view = c.supervisor_view().expect(
16000            "supervisor_view must materialize a SupervisorSpec for a \
16001             :kind Supervisor Caixa carrying a None :estrategia",
16002        );
16003        assert_eq!(
16004            view.estrategia(),
16005            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
16006            "supervisor_view must degrade the outer \
16007             Caixa::estrategia() None arm onto the lifted \
16008             SUPERVISOR_ESTRATEGIA_DEFAULT typed pub const (got {:?}, \
16009             expected {:?})",
16010            view.estrategia(),
16011            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
16012        );
16013    }
16014
16015    #[test]
16016    fn supervisor_view_max_restarts_fallback_routes_through_lifted_default() {
16017        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
16018        // `:max-restarts` arm must degrade onto the substrate-canonical
16019        // [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`] typed
16020        // `pub const` — the Erlang/OTP-canonical `{intensity, 5, 60}`
16021        // `MaxIntensity` default — rather than a raw `5` literal. Prior
16022        // to the lift the composition site carried an inline
16023        // `.unwrap_or(5)` with no compile-time link back to the shared
16024        // OTP-canonical default that the serde-side
16025        // `#[serde(default = "default_max_restarts")]` wire-format arm
16026        // and the [`Default for crate::supervisor::SupervisorSpec`]
16027        // struct-literal default arm both key off — so a future rebrand
16028        // of the OTP-canonical default (Elixir's `Supervisor` `3`
16029        // default, a per-cluster overlay the operator pins through the
16030        // MESH-COMPOSITION §III.2 supervision-canary
16031        // `:supervisor :max-restarts-overrides` roadmap slot) would
16032        // have had to be threaded through both the serde-side helper
16033        // and this view-construction arm in lockstep or a `:kind
16034        // Supervisor` caixa carrying `:max-restarts ()` would silently
16035        // resolve to a `SupervisorSpec` whose `max_restarts` disagreed
16036        // with the same fixture's serde-side `SupervisorSpec` view (an
16037        // author-omitted slot round-tripping through
16038        // `SupervisorSpec::default()` to the lifted constant, then
16039        // splitting to a stale literal past `supervisor_view`).
16040        // Byte-parity against the lifted constant closes the split.
16041        // Peer of the sibling
16042        // [`crate::supervisor::default_max_restarts_helper_routes_through_lifted_default`]
16043        // + [`crate::supervisor::supervisor_spec_default_max_restarts_routes_through_lifted_default`]
16044        // composition pins that close the same routing on the two
16045        // sibling entry points onto the shared substrate constant.
16046        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
16047        let view = c.supervisor_view().expect(
16048            "supervisor_view must materialize a SupervisorSpec for a \
16049             :kind Supervisor Caixa carrying a None :max-restarts",
16050        );
16051        assert_eq!(
16052            view.max_restarts(),
16053            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
16054            "supervisor_view must degrade the outer \
16055             Caixa::max_restarts() None arm onto the lifted \
16056             SUPERVISOR_MAX_RESTARTS_DEFAULT typed pub const (got {}, \
16057             expected {})",
16058            view.max_restarts(),
16059            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
16060        );
16061    }
16062
16063    #[test]
16064    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
16065        // Value-shape pin: [`Caixa::restart_window`] returns the
16066        // `:restart-window` typed `Option<String>` verbatim as an
16067        // `Option<&str>`, borrowed from the typed slot's own storage,
16068        // byte-equal across the author-omitted `None` arm and each of
16069        // the representative fixtures in the accept-set — the canonical
16070        // `"60s"` from `{intensity, 5, 60}`, the sibling
16071        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
16072        // / `"0s"`) the shared codec's positive-set sweep pin covers,
16073        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
16074        // seconds drift the sibling [`Self::validate_restart_window`]
16075        // gate refuses; the accessor must ship the raw slot verbatim
16076        // so struct-literal fixtures continue to expose the drift at
16077        // the accessor boundary). Third outer top-level [`Caixa`]
16078        // supervisor-tree flat-spread pin — extends the sub-family onto
16079        // the sibling `Option<&str>` raw-duration-string arm.
16080        for window in [
16081            None,
16082            Some("60s"),
16083            Some("5m"),
16084            Some("1h"),
16085            Some("500ms"),
16086            Some("1.5s"),
16087            Some(""),
16088        ] {
16089            let c = caixa_with_restart_window(window);
16090            assert_eq!(
16091                c.restart_window(),
16092                window,
16093                "Caixa::restart_window must return :restart-window \
16094                 verbatim as Option<&str> (got {:?}, expected {window:?})",
16095                c.restart_window(),
16096            );
16097            assert_eq!(
16098                c.restart_window(),
16099                c.restart_window.as_deref(),
16100                "Caixa::restart_window accessor and \
16101                 self.restart_window.as_deref() field access must \
16102                 byte-equal — a byte-level drift would silently split \
16103                 the paired Caixa::declared_supervisor_slots \
16104                 presence-probe arm from the \
16105                 Caixa::validate_restart_window shared-codec gate and \
16106                 the Caixa::supervisor_view soft-swallowing fold",
16107            );
16108        }
16109    }
16110
16111    #[test]
16112    fn restart_window_projects_slice_by_borrow() {
16113        // The by-borrow pin: [`Caixa::restart_window`] returns
16114        // `Option<&str>` by borrow — the returned string slice borrows
16115        // the underlying `Option<String>` storage of the `:restart-window`
16116        // slot and the accessor must not clone on every call. Peer of
16117        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
16118        // by-borrow pins on the universal-axis scalar family
16119        // (`licenca_projects_option_ref_by_borrow` /
16120        // `descricao_projects_option_ref_by_borrow` and siblings) —
16121        // extended onto the M2 supervisor-tree flat-spread
16122        // `Option<&str>` raw-duration-string axis.
16123        for window in [None, Some("60s"), Some("5m"), Some("")] {
16124            let c = caixa_with_restart_window(window);
16125            let first = c.restart_window();
16126            let second = c.restart_window();
16127            assert_eq!(
16128                first, second,
16129                "Caixa::restart_window must be idempotent — two \
16130                 successive calls on the same &self must return the \
16131                 same Option<&str>",
16132            );
16133            if let (Some(a), Some(b)) = (first, second) {
16134                assert_eq!(
16135                    a.as_ptr(),
16136                    b.as_ptr(),
16137                    "Caixa::restart_window must borrow the underlying \
16138                     String storage — two successive Some-arm calls must \
16139                     return slices with the same backing pointer (a fresh \
16140                     String clone would change the pointer on every call)",
16141                );
16142            }
16143            assert_eq!(
16144                first, window,
16145                "Caixa::restart_window must return :restart-window \
16146                 verbatim by borrow — got {first:?}, expected {window:?}",
16147            );
16148        }
16149    }
16150
16151    #[test]
16152    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
16153        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16154        // `:restart-window` presence-probe arm must key off
16155        // [`Caixa::restart_window`], not the raw
16156        // `self.restart_window.is_some()` field-probe. Structurally:
16157        // every `Caixa { restart_window: Some(_), .. }` must push
16158        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
16159        // list, and a `Caixa { restart_window: None, .. }` must NOT
16160        // push the label. Peer of the sibling
16161        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
16162        // routing pin.
16163        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
16164            let c = caixa_with_restart_window(Some(window));
16165            let slots = c.declared_supervisor_slots();
16166            assert!(
16167                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
16168                "declared_supervisor_slots must push \
16169                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
16170                 `:restart-window` is Some({window:?}) — the accessor \
16171                 and the enumerator gate must route through the same \
16172                 substrate-primitive typed dispatch on the outer \
16173                 :restart-window presence bit (got slots={slots:?})",
16174            );
16175        }
16176        let c = caixa_with_restart_window(None);
16177        let slots = c.declared_supervisor_slots();
16178        assert!(
16179            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
16180            "declared_supervisor_slots must NOT push \
16181             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
16182             is None — the author-omitted arm must route through the \
16183             accessor's None-return unchanged (got slots={slots:?})",
16184        );
16185    }
16186
16187    #[test]
16188    fn validate_restart_window_arm_routes_through_accessor() {
16189        // Composition pin: [`Caixa::validate_restart_window`]'s
16190        // shared-codec fold arm must key off [`Caixa::restart_window`],
16191        // not the raw `self.restart_window.as_deref()` field-projection.
16192        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
16193        // express no reset" canonical shape); (2) a canonical `Some`
16194        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
16195        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
16196        // .. })` carrying the offending raw string verbatim. The three
16197        // arms jointly pin that the validator's raw-string binding is
16198        // the accessor's return, not a peer projection — any future
16199        // silent detour that had the accessor collapse `Some("")` to
16200        // `None` would silently absorb the empty-after-trim refusal
16201        // case at the accessor boundary.
16202        caixa_with_restart_window(None)
16203            .validate_restart_window()
16204            .expect("None :restart-window must validate through the accessor");
16205        caixa_with_restart_window(Some("60s"))
16206            .validate_restart_window()
16207            .expect("canonical :restart-window \"60s\" must validate through the accessor");
16208        let err = caixa_with_restart_window(Some("1.5s"))
16209            .validate_restart_window()
16210            .expect_err("fractional-seconds :restart-window must fail through the accessor");
16211        assert!(
16212            matches!(
16213                err,
16214                ManifestError::RestartWindowMalformed { ref restart_window, .. }
16215                    if restart_window == "1.5s"
16216            ),
16217            "validator must carry the offending raw string verbatim \
16218             from the accessor's borrowed &str (got {err:?})",
16219        );
16220    }
16221
16222    #[test]
16223    fn supervisor_view_restart_window_arm_routes_through_accessor() {
16224        // Composition pin: [`Caixa::supervisor_view`]'s
16225        // per-`:restart-window` [`SupervisorSpec`] construction arm
16226        // must key off [`Caixa::restart_window`]'s soft-swallowing
16227        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
16228        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
16229        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
16230        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
16231        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
16232        // (the shared codec's canonical parse); (3) codec-rejected
16233        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
16234        // (the soft-swallow preserving the view's best-effort shape).
16235        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
16236        let view = c.supervisor_view().expect("Supervisor kind has a view");
16237        assert_eq!(
16238            view.restart_window(),
16239            None,
16240            "supervisor_view must project outer None :restart-window \
16241             onto None on the composed SupervisorSpec (never-reset \
16242             sentinel) through the accessor's None-return unchanged",
16243        );
16244
16245        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
16246        let view = c.supervisor_view().expect("Supervisor kind has a view");
16247        assert_eq!(
16248            view.restart_window(),
16249            Some(std::time::Duration::from_secs(60)),
16250            "supervisor_view must fold outer Some(\"60s\") through the \
16251             shared duration_codec into Duration::from_secs(60) on the \
16252             composed SupervisorSpec (accessor's Some(&str) → codec \
16253             parse → Some(Duration))",
16254        );
16255
16256        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
16257        let view = c.supervisor_view().expect("Supervisor kind has a view");
16258        assert_eq!(
16259            view.restart_window(),
16260            None,
16261            "supervisor_view must soft-swallow the shared-codec parse \
16262             failure to None (the view's best-effort shape the sibling \
16263             manifest-level validate_restart_window surfaces as \
16264             RestartWindowMalformed); the accessor's raw-string return \
16265             is the single input every downstream consumer keys off",
16266        );
16267    }
16268
16269    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
16270
16271    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
16272        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16273        c.upgrade_from = upgrade_from;
16274        c
16275    }
16276
16277    #[test]
16278    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
16279        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
16280        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
16281        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
16282        // typed `Vec<UpgradeFromEntry>` verbatim as a
16283        // `&[UpgradeFromEntry]` slice-view over the same backing
16284        // buffer the raw `self.upgrade_from.as_slice()` field access
16285        // borrows from, element-equal across every representative
16286        // fixture in the accept-set — `[]` (the "no hot-upgrade path
16287        // declared" arm every `defcaixa` without an `:upgrade-from`
16288        // block carries; `#[serde(default)]` folds an omitted slot
16289        // onto `Vec::new()`), a canonical single-entry `Restart`
16290        // fixture (the shape most Servicos carry — a single prior
16291        // version with the fallback strategy), a canonical multi-
16292        // entry list carrying every typed instruction variant
16293        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
16294        // `Restart`), and a past-the-guard sentinel — a duplicate-
16295        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
16296        // ([`crate::upgrade::validate_upgrade_from`] rejects through
16297        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
16298        // ship the raw slot verbatim so struct-literal fixtures
16299        // continue to expose the duplicate at the accessor boundary).
16300        //
16301        // Pins against a future silent detour that returned an owned
16302        // `Vec<UpgradeFromEntry>` (which would type-check but silently
16303        // clone on every accessor call, breaking the zero-cost
16304        // projection every peer sibling slice accessor carries), a
16305        // `[dup, dup] → [dup]` dedup collapse (which would silently
16306        // absorb the `DuplicateFrom` refusal case at the accessor
16307        // boundary and the [`crate::StandardLayout::verify`] cross-
16308        // entry gate would silently accept a struct-literal `Caixa`
16309        // carrying the drift), a reference to an operator-resolved
16310        // overlay (the future per-cluster `:upgrade-overrides` slot
16311        // — its resolution must land at exactly this accessor body,
16312        // not silently divert the raw slot away from a second
16313        // consumer), or an axis-shuffled projection (a future detour
16314        // that reordered entries through the accessor would silently
16315        // split the paired [`crate::StandardLayout::verify`] per-
16316        // `:upgrade-from` shape gate's traversal input from the peer
16317        // [`crate::render::servico_m2_overlay`] emitter's projection
16318        // input, since the operator's hot-upgrade dispatch matches
16319        // per-`:from` and axis reordering would silently split the
16320        // per-entry script-path existence probe's iteration order
16321        // from the M2 overlay emitter's serialized-entry order).
16322        //
16323        // First outer top-level [`Caixa`] `&[Composite]`-return
16324        // slice accessor pin on the substrate primitive for M2 / M3
16325        // typed-slot vec-carry axes — opens the outer-`Caixa`
16326        // `&[Composite]` composite-slice projection pattern the
16327        // sibling `:children` [`crate::supervisor::ChildSpec`] /
16328        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
16329        // [`crate::aplicacao::WitContract`] future outer-composite-
16330        // slice pins fold on. Peer of the closed outer-`Caixa`
16331        // scalar `Option<&Composite>` composite-reference family the
16332        // sibling `limits` / `behavior` / `politicas` / `placement`
16333        // / `entrada` `..._returns_..._option_ref_verbatim_across_
16334        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
16335        // the "byte-equal, borrow-shared" outer-accessor discipline
16336        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
16337        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16338        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
16339            vec![],
16340            vec![UpgradeFromEntry {
16341                from: "0.0.1".into(),
16342                instructions: vec![UpgradeInstruction::Restart],
16343            }],
16344            vec![
16345                UpgradeFromEntry {
16346                    from: "0.0.1".into(),
16347                    instructions: vec![
16348                        UpgradeInstruction::LoadModule {
16349                            module: "demo".into(),
16350                        },
16351                        UpgradeInstruction::SoftPurge {
16352                            module: "demo".into(),
16353                        },
16354                    ],
16355                },
16356                UpgradeFromEntry {
16357                    from: "0.0.2".into(),
16358                    instructions: vec![
16359                        UpgradeInstruction::StateChange {
16360                            script: "servicos/upgrade.lisp".into(),
16361                        },
16362                        UpgradeInstruction::Purge {
16363                            module: "demo".into(),
16364                        },
16365                        UpgradeInstruction::Restart,
16366                    ],
16367                },
16368            ],
16369            vec![
16370                UpgradeFromEntry {
16371                    from: "0.1.0".into(),
16372                    instructions: vec![UpgradeInstruction::Restart],
16373                },
16374                UpgradeFromEntry {
16375                    from: "0.1.0".into(),
16376                    instructions: vec![UpgradeInstruction::Restart],
16377                },
16378            ],
16379        ];
16380        for upgrade_from in fixtures {
16381            let c = caixa_with_upgrade_from(upgrade_from.clone());
16382            assert_eq!(
16383                c.upgrade_from(),
16384                upgrade_from.as_slice(),
16385                "Caixa::upgrade_from must return :upgrade-from \
16386                 verbatim (got {:?}, expected {upgrade_from:?})",
16387                c.upgrade_from(),
16388            );
16389            assert_eq!(
16390                c.upgrade_from(),
16391                c.upgrade_from.as_slice(),
16392                "Caixa::upgrade_from must element-equal the raw \
16393                 `self.upgrade_from.as_slice()` field access across \
16394                 every value in the Vec<UpgradeFromEntry> accept-set",
16395            );
16396            assert_eq!(
16397                c.upgrade_from().is_empty(),
16398                c.upgrade_from.is_empty(),
16399                "Caixa::upgrade_from().is_empty() must byte-equal \
16400                 self.upgrade_from.is_empty() — a presence-bit drift \
16401                 would silently split the paired \
16402                 Caixa::declared_servico_slots M2 declared-slot \
16403                 enumerator's presence probe from the peer \
16404                 crate::render::servico_m2_overlay M2 overlay \
16405                 emitter's presence gate",
16406            );
16407        }
16408    }
16409
16410    #[test]
16411    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
16412        // Composition pin: [`Caixa::declared_servico_slots`]'s
16413        // `:upgrade-from` presence-probe arm must key off
16414        // [`Caixa::upgrade_from`], not the raw
16415        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
16416        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
16417        // instructions: vec![Restart] }], .. }` must push
16418        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
16419        // (the presence bit is non-empty, so the M2 kind-coherence
16420        // gate must surface the slot as "declared"), and a `Caixa {
16421        // upgrade_from: vec![], .. }` must NOT push the label (the
16422        // "author omitted the slot entirely" arm — the empty-slice
16423        // partition the serde-default folds onto). The pair jointly
16424        // pins the accessor + declared-slot enumerator composition:
16425        // any future silent detour that had the accessor collapse
16426        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
16427        // is_empty())` projection) would silently absorb the
16428        // "declared but degenerate" arm at the accessor boundary and
16429        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
16430        // coherence gate would silently accept a struct-literal
16431        // `Caixa` carrying the drift.
16432        //
16433        // Peer of the sibling
16434        // `declared_servico_slots_limits_arm_routes_through_accessor`
16435        // (b2bd9d7) and
16436        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16437        // (35d8b52) composition pins on the sibling `:limits` /
16438        // `:behavior` outer-`Option<&Composite>` arms — same "the
16439        // enumerator gate must route through the substrate-primitive
16440        // typed dispatch" discipline extended onto the third M2
16441        // Servico-runtime slot axis, closing the enumerator's routing
16442        // invariant on every M2 arm.
16443        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16444        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
16445            from: "0.0.1".into(),
16446            instructions: vec![UpgradeInstruction::Restart],
16447        }]);
16448        let slots = c.declared_servico_slots();
16449        assert!(
16450            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
16451            "declared_servico_slots must push \
16452             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
16453             non-empty — the accessor and the enumerator gate must \
16454             route through the same substrate-primitive typed \
16455             dispatch on the outer :upgrade-from presence bit (got \
16456             slots={slots:?})",
16457        );
16458        let c = caixa_with_upgrade_from(vec![]);
16459        let slots = c.declared_servico_slots();
16460        assert!(
16461            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
16462            "declared_servico_slots must NOT push \
16463             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
16464             empty — the author-omitted arm must route through the \
16465             accessor's empty-slice return unchanged (got \
16466             slots={slots:?})",
16467        );
16468    }
16469
16470    #[test]
16471    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
16472        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16473        // per-`:upgrade-from` M2 overlay emit arm must key off
16474        // [`Caixa::upgrade_from`], not the raw
16475        // `!caixa.upgrade_from.is_empty()` presence gate + the
16476        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
16477        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
16478        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
16479        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
16480        // sequence in the overlay (the emitter fans onto the serde
16481        // slice-serialization), and a `Caixa { upgrade_from: vec![],
16482        // .. }` must omit the key entirely (the empty-slice
16483        // partition — the `!.is_empty()` outer gate elides the key
16484        // when the author omitted the slot). The pair jointly pins
16485        // the accessor + M2 overlay emitter composition: any future
16486        // silent detour that had the accessor return a fresh-cloned
16487        // `Vec<UpgradeFromEntry>` copy would silently break the
16488        // reference-identity pin the peer per-entry
16489        // `serde_yaml::to_value(caixa.upgrade_from())` projection
16490        // reads from — the projection would clone once per accessor
16491        // call instead of borrowing the storage buffer verbatim.
16492        //
16493        // Peer of the sibling
16494        // `servico_m2_overlay_limits_arm_routes_through_accessor`
16495        // (b2bd9d7) and
16496        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
16497        // (35d8b52) composition pins on the sibling `:limits` /
16498        // `:behavior` outer-`Option<&Composite>` arms — same "the
16499        // M2 overlay emitter must route through the substrate-
16500        // primitive typed dispatch" discipline extended onto the
16501        // third M2 Servico-runtime slot axis, closing the overlay
16502        // emitter's routing invariant on every M2 arm.
16503        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
16504        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16505        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
16506            from: "0.0.1".into(),
16507            instructions: vec![UpgradeInstruction::Restart],
16508        }]);
16509        let overlay = servico_m2_overlay(&c).unwrap();
16510        assert!(
16511            overlay.contains_key(M2_KEY_UPGRADE_FROM),
16512            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
16513             `:upgrade-from` is non-empty — the accessor and the M2 \
16514             overlay emitter must route through the same substrate- \
16515             primitive typed dispatch on the outer :upgrade-from \
16516             slice (got overlay={overlay:?})",
16517        );
16518        let c = caixa_with_upgrade_from(vec![]);
16519        let overlay = servico_m2_overlay(&c).unwrap();
16520        assert!(
16521            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
16522            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
16523             `:upgrade-from` is empty — the empty-slice partition \
16524             must route through the accessor's empty-slice return \
16525             unchanged (got overlay={overlay:?})",
16526        );
16527    }
16528
16529    #[test]
16530    fn upgrade_from_projects_slice_by_borrow() {
16531        // The by-borrow pin: [`Caixa::upgrade_from`] returns
16532        // `&[UpgradeFromEntry]` by borrow — the returned slice
16533        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
16534        // the `:upgrade-from` slot and the accessor must not clone
16535        // the backing `Vec` on every call. Peer of the sibling
16536        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
16537        // (`autores_projects_slice_by_borrow` b5d813f,
16538        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16539        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16540        // `exe_projects_slice_by_borrow` 65d9527,
16541        // `servicos_projects_slice_by_borrow` 611f78b,
16542        // `deps_projects_slice_by_borrow` ad34b4e,
16543        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
16544        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
16545        // axes — extended here to the first outer-`Caixa`
16546        // composite-element `&[Composite]` axis: the accessor's
16547        // returned slice must borrow from `&self` (the returned
16548        // reference's lifetime is tied to `&self`), and calling the
16549        // accessor twice on the same [`Caixa`] must yield slices
16550        // that are pointer-equal (the underlying byte-buffer is the
16551        // storage `Vec`'s allocation, not a fresh copy) as well as
16552        // value-equal (idempotent, no side effects on `&self`).
16553        //
16554        // Pins against a future silent detour that returned an owned
16555        // `Vec<UpgradeFromEntry>` (which would type-check but
16556        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
16557        // return (which would leak the backing `Vec`'s
16558        // grow/push/reserve surface no downstream consumer reaches
16559        // for), or a one-arm-only accessor that returned a
16560        // saturating value on some sentinel input.
16561        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16562        for upgrade_from in [
16563            vec![],
16564            vec![UpgradeFromEntry {
16565                from: "0.0.1".into(),
16566                instructions: vec![UpgradeInstruction::Restart],
16567            }],
16568            vec![
16569                UpgradeFromEntry {
16570                    from: "0.0.1".into(),
16571                    instructions: vec![UpgradeInstruction::Restart],
16572                },
16573                UpgradeFromEntry {
16574                    from: "0.0.2".into(),
16575                    instructions: vec![UpgradeInstruction::SoftPurge {
16576                        module: "demo".into(),
16577                    }],
16578                },
16579            ],
16580        ] {
16581            let c = caixa_with_upgrade_from(upgrade_from.clone());
16582            let first = c.upgrade_from();
16583            let second = c.upgrade_from();
16584            assert_eq!(
16585                first, second,
16586                "Caixa::upgrade_from must be idempotent — two \
16587                 successive calls on the same &self must return the \
16588                 same &[UpgradeFromEntry]",
16589            );
16590            assert_eq!(
16591                first.as_ptr(),
16592                second.as_ptr(),
16593                "Caixa::upgrade_from must borrow the underlying \
16594                 Vec<UpgradeFromEntry> storage — two successive calls \
16595                 must return slices with the same backing pointer (a \
16596                 fresh Vec<UpgradeFromEntry> clone would change the \
16597                 pointer on every call)",
16598            );
16599            assert_eq!(
16600                first,
16601                upgrade_from.as_slice(),
16602                "Caixa::upgrade_from must return :upgrade-from \
16603                 verbatim by borrow — got {first:?}, expected \
16604                 {upgrade_from:?}",
16605            );
16606        }
16607    }
16608
16609    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
16610
16611    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
16612        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16613        c.children = children;
16614        c
16615    }
16616
16617    #[test]
16618    fn children_returns_children_slice_verbatim_across_permutations() {
16619        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
16620        // outer-composite `&[ChildSpec]`-return slice-shape pin:
16621        // [`Caixa::children`] must return the `:children` typed
16622        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
16623        // the same backing buffer the raw `self.children.as_slice()`
16624        // field access borrows from, element-equal across every
16625        // representative fixture in the accept-set — `[]` (the "no
16626        // static children declared" arm every non-`Supervisor`-kind
16627        // `defcaixa` carries by `#[serde(default)]` and every
16628        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
16629        // a canonical single-child `Permanent` fixture (the shape
16630        // most `OneForOne` supervisors carry — a single long-running
16631        // worker child), a canonical multi-child list carrying every
16632        // typed restart-policy variant (`Permanent` / `Transient` /
16633        // `Temporary`), and a past-the-guard sentinel — a duplicate
16634        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
16635        // ([`crate::SupervisorSpec::validate`] rejects through
16636        // `DuplicateChildNome { nome: "w" }` but the accessor must
16637        // ship the raw slot verbatim so struct-literal fixtures
16638        // continue to expose the duplicate at the accessor boundary).
16639        //
16640        // Pins against a future silent detour that returned an owned
16641        // `Vec<ChildSpec>` (which would type-check but silently clone
16642        // on every accessor call, breaking the zero-cost projection
16643        // every peer sibling slice accessor carries), a `[dup, dup] →
16644        // [dup]` dedup collapse (which would silently absorb the
16645        // `DuplicateChildNome` refusal case at the accessor boundary
16646        // and the [`crate::StandardLayout::verify`] cross-child gate
16647        // would silently accept a struct-literal `Caixa` carrying the
16648        // drift), a reference to an operator-resolved overlay (the
16649        // future per-cluster `:children-overrides` slot — its
16650        // resolution must land at exactly this accessor body, not
16651        // silently divert the raw slot away from a second consumer),
16652        // or an axis-shuffled projection (a future detour that
16653        // reordered children through the accessor would silently
16654        // split the paired [`crate::StandardLayout::verify`] per-
16655        // supervisor gate's traversal input from the peer
16656        // [`Self::supervisor_view`] fold-in path's clone-order input,
16657        // since the OTP `RestForOne` restart strategy dispatches on
16658        // declared child order and axis reordering would silently
16659        // split the operator's per-cluster restart-fan-out order
16660        // from the caixa.lisp source-order).
16661        //
16662        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
16663        // accessor pin on the substrate primitive for M2 / M3 typed-
16664        // slot vec-carry axes — folds on the outer-`Caixa`
16665        // `&[Composite]` composite-slice sub-family the sibling
16666        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16667        // (2a1f907) pin opened, peer at the outer altitude of the
16668        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
16669        // (bc92bce) accessor on the same OTP-supervisor static-child-
16670        // list axis.
16671        use crate::supervisor::{ChildSpec, RestartPolicy};
16672        let fixtures: Vec<Vec<ChildSpec>> = vec![
16673            vec![],
16674            vec![ChildSpec {
16675                caixa: "worker".into(),
16676                versao: "^0.1".into(),
16677                restart: RestartPolicy::Permanent,
16678            }],
16679            vec![
16680                ChildSpec {
16681                    caixa: "worker-a".into(),
16682                    versao: "^0.1".into(),
16683                    restart: RestartPolicy::Permanent,
16684                },
16685                ChildSpec {
16686                    caixa: "worker-b".into(),
16687                    versao: "^0.1".into(),
16688                    restart: RestartPolicy::Transient,
16689                },
16690                ChildSpec {
16691                    caixa: "worker-c".into(),
16692                    versao: "^0.1".into(),
16693                    restart: RestartPolicy::Temporary,
16694                },
16695            ],
16696            vec![
16697                ChildSpec {
16698                    caixa: "w".into(),
16699                    versao: "^0.1".into(),
16700                    restart: RestartPolicy::Permanent,
16701                },
16702                ChildSpec {
16703                    caixa: "w".into(),
16704                    versao: "^0.1".into(),
16705                    restart: RestartPolicy::Permanent,
16706                },
16707            ],
16708        ];
16709        for children in fixtures {
16710            let c = caixa_with_children(children.clone());
16711            assert_eq!(
16712                c.children(),
16713                children.as_slice(),
16714                "Caixa::children must return :children verbatim \
16715                 (got {:?}, expected {children:?})",
16716                c.children(),
16717            );
16718            assert_eq!(
16719                c.children(),
16720                c.children.as_slice(),
16721                "Caixa::children must element-equal the raw \
16722                 `self.children.as_slice()` field access across \
16723                 every value in the Vec<ChildSpec> accept-set",
16724            );
16725            assert_eq!(
16726                c.children().is_empty(),
16727                c.children.is_empty(),
16728                "Caixa::children().is_empty() must byte-equal \
16729                 self.children.is_empty() — a presence-bit drift \
16730                 would silently split the paired \
16731                 Caixa::declared_supervisor_slots supervisor-tree \
16732                 declared-slot enumerator's presence probe from the \
16733                 peer Caixa::supervisor_view typed-view composer's \
16734                 fold-in path",
16735            );
16736        }
16737    }
16738
16739    #[test]
16740    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
16741        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16742        // `:children` presence-probe arm must key off
16743        // [`Caixa::children`], not the raw
16744        // `!self.children.is_empty()` field-probe. Structurally: a
16745        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
16746        // "^0.1", restart: Permanent }], .. }` must push
16747        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
16748        // (the presence bit is non-empty, so the supervisor-tree
16749        // kind-coherence gate must surface the slot as "declared"),
16750        // and a `Caixa { children: vec![], .. }` must NOT push the
16751        // label (the "author omitted the slot entirely" arm — the
16752        // empty-slice partition the serde-default folds onto). The
16753        // pair jointly pins the accessor + declared-slot enumerator
16754        // composition: any future silent detour that had the accessor
16755        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
16756        // "__reserved__")` projection) would silently absorb the
16757        // "declared but degenerate" arm at the accessor boundary and
16758        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
16759        // kind-coherence gate would silently accept a struct-literal
16760        // `Caixa` carrying the drift.
16761        //
16762        // Peer of the sibling
16763        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16764        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
16765        // same "the enumerator gate must route through the substrate-
16766        // primitive typed dispatch" discipline extended onto the
16767        // supervisor-tree `:children` composite-slice arm.
16768        use crate::supervisor::{ChildSpec, RestartPolicy};
16769        let c = caixa_with_children(vec![ChildSpec {
16770            caixa: "w".into(),
16771            versao: "^0.1".into(),
16772            restart: RestartPolicy::Permanent,
16773        }]);
16774        let slots = c.declared_supervisor_slots();
16775        assert!(
16776            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
16777            "declared_supervisor_slots must push \
16778             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
16779             non-empty — the accessor and the enumerator gate must \
16780             route through the same substrate-primitive typed \
16781             dispatch on the outer :children presence bit (got \
16782             slots={slots:?})",
16783        );
16784        let c = caixa_with_children(vec![]);
16785        let slots = c.declared_supervisor_slots();
16786        assert!(
16787            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
16788            "declared_supervisor_slots must NOT push \
16789             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
16790             empty — the author-omitted arm must route through the \
16791             accessor's empty-slice return unchanged (got \
16792             slots={slots:?})",
16793        );
16794    }
16795
16796    #[test]
16797    fn supervisor_view_children_arm_routes_through_accessor() {
16798        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
16799        // fold-in arm must key off [`Caixa::children`], not the raw
16800        // `self.children.clone()` field-clone. Structurally: a `Caixa {
16801        // kind: Supervisor, estrategia: Some(OneForOne), children:
16802        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
16803        // per-child list through the accessor into the typed
16804        // [`SupervisorSpec`] view's `children` field verbatim — every
16805        // entry the accessor surfaces must land in the view's
16806        // `children` slot in the same order. The pair jointly pins the
16807        // accessor + view-composer composition: any future silent
16808        // detour that had the accessor return a fresh-cloned
16809        // `Vec<ChildSpec>` copy would silently break the reference-
16810        // identity pin the peer `supervisor_view` fold-in path reads
16811        // from — the fold would clone once more per accessor call
16812        // instead of borrowing the storage buffer verbatim once.
16813        //
16814        // Peer of the sibling
16815        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
16816        // family) composition pin on the peer kind-gate arm — same
16817        // "the view composer must route through the substrate-
16818        // primitive typed dispatch" discipline extended onto the
16819        // per-`:children` fold-in arm, closing the supervisor-view
16820        // composer's routing invariant on the composite-slice input.
16821        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16822        let mut c = caixa_with_children(vec![
16823            ChildSpec {
16824                caixa: "worker-a".into(),
16825                versao: "^0.1".into(),
16826                restart: RestartPolicy::Permanent,
16827            },
16828            ChildSpec {
16829                caixa: "worker-b".into(),
16830                versao: "^0.1".into(),
16831                restart: RestartPolicy::Transient,
16832            },
16833        ]);
16834        c.kind = crate::CaixaKind::Supervisor;
16835        c.estrategia = Some(RestartStrategy::OneForOne);
16836        let view = c
16837            .supervisor_view()
16838            .expect("Supervisor kind must produce a supervisor_view");
16839        assert_eq!(
16840            view.children(),
16841            c.children(),
16842            "supervisor_view must fold Caixa::children verbatim into \
16843             SupervisorSpec::children — the accessor and the view \
16844             composer must route through the same substrate-primitive \
16845             typed dispatch on the outer :children slice (got view \
16846             children={:?}, expected {:?})",
16847            view.children(),
16848            c.children(),
16849        );
16850    }
16851
16852    #[test]
16853    fn children_projects_slice_by_borrow() {
16854        // The by-borrow pin: [`Caixa::children`] returns
16855        // `&[ChildSpec]` by borrow — the returned slice borrows the
16856        // underlying `Vec<ChildSpec>` storage of the `:children` slot
16857        // and the accessor must not clone the backing `Vec` on every
16858        // call. Peer of the sibling outer top-level [`Caixa`]
16859        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
16860        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
16861        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16862        // `exe_projects_slice_by_borrow` 65d9527,
16863        // `servicos_projects_slice_by_borrow` 611f78b,
16864        // `deps_projects_slice_by_borrow` ad34b4e,
16865        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16866        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
16867        // sibling outer top-level [`Caixa`] scalar-element and
16868        // composite-element `&[T]` axes — folds on the outer-`Caixa`
16869        // composite-element `&[Composite]` axis: the accessor's
16870        // returned slice must borrow from `&self` (the returned
16871        // reference's lifetime is tied to `&self`), and calling the
16872        // accessor twice on the same [`Caixa`] must yield slices
16873        // that are pointer-equal (the underlying byte-buffer is the
16874        // storage `Vec`'s allocation, not a fresh copy) as well as
16875        // value-equal (idempotent, no side effects on `&self`).
16876        //
16877        // Pins against a future silent detour that returned an owned
16878        // `Vec<ChildSpec>` (which would type-check but silently clone
16879        // on every call), a `&Vec<ChildSpec>` return (which would leak
16880        // the backing `Vec`'s grow/push/reserve surface no downstream
16881        // consumer reaches for), or a one-arm-only accessor that
16882        // returned a saturating value on some sentinel input.
16883        use crate::supervisor::{ChildSpec, RestartPolicy};
16884        for children in [
16885            vec![],
16886            vec![ChildSpec {
16887                caixa: "w".into(),
16888                versao: "^0.1".into(),
16889                restart: RestartPolicy::Permanent,
16890            }],
16891            vec![
16892                ChildSpec {
16893                    caixa: "worker-a".into(),
16894                    versao: "^0.1".into(),
16895                    restart: RestartPolicy::Permanent,
16896                },
16897                ChildSpec {
16898                    caixa: "worker-b".into(),
16899                    versao: "^0.1".into(),
16900                    restart: RestartPolicy::Transient,
16901                },
16902            ],
16903        ] {
16904            let c = caixa_with_children(children.clone());
16905            let first = c.children();
16906            let second = c.children();
16907            assert_eq!(
16908                first, second,
16909                "Caixa::children must be idempotent — two successive \
16910                 calls on the same &self must return the same \
16911                 &[ChildSpec]",
16912            );
16913            assert_eq!(
16914                first.as_ptr(),
16915                second.as_ptr(),
16916                "Caixa::children must borrow the underlying \
16917                 Vec<ChildSpec> storage — two successive calls must \
16918                 return slices with the same backing pointer (a fresh \
16919                 Vec<ChildSpec> clone would change the pointer on \
16920                 every call)",
16921            );
16922            assert_eq!(
16923                first,
16924                children.as_slice(),
16925                "Caixa::children must return :children verbatim by \
16926                 borrow — got {first:?}, expected {children:?}",
16927            );
16928        }
16929    }
16930
16931    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
16932
16933    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
16934        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16935        c.kind = CaixaKind::Aplicacao;
16936        c.membros = membros;
16937        c
16938    }
16939
16940    #[test]
16941    fn membros_returns_membros_slice_verbatim_across_permutations() {
16942        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
16943        // composite `&[Membro]`-return slice-shape pin:
16944        // [`Caixa::membros`] must return the `:membros` typed
16945        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
16946        // same backing buffer the raw `self.membros.as_slice()` field
16947        // access borrows from, element-equal across every
16948        // representative fixture in the accept-set — `[]` (the "no
16949        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
16950        // carries by `#[serde(default)]` and every partially-authored
16951        // Aplicacao carries before the
16952        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
16953        // canonical single-member fixture (the shape a minimal
16954        // Aplicacao carries — one Servico wrapping one contained
16955        // computation), a canonical multi-member list carrying three
16956        // distinct entries (the canonical checkout-shape Aplicacao —
16957        // cart / pricing / auth — every canonical example carries), and
16958        // a past-the-guard sentinel — a duplicate `:caixa`
16959        // `[("cart", ...), ("cart", ...)]` entry pair
16960        // ([`crate::AplicacaoSpec::validate`] rejects through
16961        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
16962        // the raw slot verbatim so struct-literal fixtures continue to
16963        // expose the duplicate at the accessor boundary).
16964        //
16965        // Pins against a future silent detour that returned an owned
16966        // `Vec<Membro>` (which would type-check but silently clone on
16967        // every accessor call, breaking the zero-cost projection every
16968        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
16969        // dedup collapse (which would silently absorb the
16970        // `DuplicateMembro` refusal case at the accessor boundary and
16971        // the [`crate::StandardLayout::verify`] cross-member gate would
16972        // silently accept a struct-literal `Caixa` carrying the drift),
16973        // a reference to an operator-resolved overlay (the future per-
16974        // cluster `:membros-overrides` slot — its resolution must land
16975        // at exactly this accessor body, not silently divert the raw
16976        // slot away from a second consumer), or an axis-shuffled
16977        // projection (a future detour that reordered members through
16978        // the accessor would silently split the paired
16979        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
16980        // traversal input from the peer [`Self::aplicacao_view`] fold-
16981        // in path's clone-order input, since the canonical `:contratos`
16982        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
16983        // read the member set through the same slice).
16984        //
16985        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
16986        // accessor pin on the substrate primitive for M2 / M3 typed-
16987        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
16988        // arm of the `&[Composite]` composite-slice sub-family the
16989        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16990        // (2a1f907) and
16991        // `children_returns_children_slice_verbatim_across_permutations`
16992        // (c17b51e) pins opened, peer at the outer altitude of the
16993        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
16994        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
16995        // list axis.
16996        use crate::aplicacao::Membro;
16997        let fixtures: Vec<Vec<Membro>> = vec![
16998            vec![],
16999            vec![Membro {
17000                caixa: "cart".into(),
17001                versao: "^0.1".into(),
17002            }],
17003            vec![
17004                Membro {
17005                    caixa: "cart".into(),
17006                    versao: "^0.1".into(),
17007                },
17008                Membro {
17009                    caixa: "pricing".into(),
17010                    versao: "^0.2".into(),
17011                },
17012                Membro {
17013                    caixa: "auth".into(),
17014                    versao: "^1.0".into(),
17015                },
17016            ],
17017            vec![
17018                Membro {
17019                    caixa: "cart".into(),
17020                    versao: "^0.1".into(),
17021                },
17022                Membro {
17023                    caixa: "cart".into(),
17024                    versao: "^0.1".into(),
17025                },
17026            ],
17027        ];
17028        for membros in fixtures {
17029            let c = caixa_aplicacao_with_membros(membros.clone());
17030            assert_eq!(
17031                c.membros(),
17032                membros.as_slice(),
17033                "Caixa::membros must return :membros verbatim \
17034                 (got {:?}, expected {membros:?})",
17035                c.membros(),
17036            );
17037            assert_eq!(
17038                c.membros(),
17039                c.membros.as_slice(),
17040                "Caixa::membros must element-equal the raw \
17041                 `self.membros.as_slice()` field access across every \
17042                 value in the Vec<Membro> accept-set",
17043            );
17044            assert_eq!(
17045                c.membros().is_empty(),
17046                c.membros.is_empty(),
17047                "Caixa::membros().is_empty() must byte-equal \
17048                 self.membros.is_empty() — a presence-bit drift would \
17049                 silently split the paired Caixa::declared_mesh_slots \
17050                 mesh declared-slot enumerator's presence probe from \
17051                 the peer Caixa::aplicacao_view typed-view composer's \
17052                 fold-in path",
17053            );
17054        }
17055    }
17056
17057    #[test]
17058    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
17059        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
17060        // presence-probe arm must key off [`Caixa::membros`], not the
17061        // raw `!self.membros.is_empty()` field-probe. Structurally: a
17062        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
17063        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
17064        // declared-slot list (the presence bit is non-empty, so the
17065        // mesh kind-coherence gate must surface the slot as
17066        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
17067        // push the label (the "author omitted the slot entirely" arm
17068        // — the empty-slice partition the serde-default folds onto).
17069        // The pair jointly pins the accessor + declared-slot
17070        // enumerator composition: any future silent detour that had
17071        // the accessor collapse `[Membro { .. }]` to `[]` (a
17072        // `.filter(|m| m.nome() != "__reserved__")` projection) would
17073        // silently absorb the "declared but degenerate" arm at the
17074        // accessor boundary and the
17075        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17076        // coherence gate would silently accept a struct-literal
17077        // `Caixa` carrying the drift.
17078        //
17079        // Peer of the sibling
17080        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
17081        // (2a1f907) and
17082        // `declared_supervisor_slots_children_arm_routes_through_accessor`
17083        // (c17b51e) composition pins on the M2 `:upgrade-from` /
17084        // `:children` composite-slice arms — same "the enumerator gate
17085        // must route through the substrate-primitive typed dispatch"
17086        // discipline extended onto the M3 `:membros` composite-slice
17087        // arm, opening the M3 arm of the declared-slot enumerator's
17088        // routing invariant.
17089        use crate::aplicacao::Membro;
17090        let c = caixa_aplicacao_with_membros(vec![Membro {
17091            caixa: "cart".into(),
17092            versao: "^0.1".into(),
17093        }]);
17094        let slots = c.declared_mesh_slots();
17095        assert!(
17096            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
17097            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
17098             `:membros` is non-empty — the accessor and the enumerator \
17099             gate must route through the same substrate-primitive \
17100             typed dispatch on the outer :membros presence bit (got \
17101             slots={slots:?})",
17102        );
17103        let c = caixa_aplicacao_with_membros(vec![]);
17104        let slots = c.declared_mesh_slots();
17105        assert!(
17106            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
17107            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
17108             when `:membros` is empty — the author-omitted arm must \
17109             route through the accessor's empty-slice return unchanged \
17110             (got slots={slots:?})",
17111        );
17112    }
17113
17114    #[test]
17115    fn aplicacao_view_membros_arm_routes_through_accessor() {
17116        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
17117        // fold-in arm must key off [`Caixa::membros`], not the raw
17118        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
17119        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
17120        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
17121        // member list through the accessor into the typed
17122        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
17123        // every entry the accessor surfaces must land in the view's
17124        // `membros` slot in the same order. The pair jointly pins the
17125        // accessor + view-composer composition: any future silent
17126        // detour that had the accessor return a fresh-cloned
17127        // `Vec<Membro>` copy would silently break the reference-
17128        // identity pin the peer `aplicacao_view` fold-in path reads
17129        // from — the fold would clone once more per accessor call
17130        // instead of borrowing the storage buffer verbatim once.
17131        //
17132        // Peer of the sibling
17133        // `aplicacao_view_politicas_arm_folds_through_accessor`
17134        // (5d23d29) /
17135        // `aplicacao_view_placement_arm_folds_through_accessor`
17136        // (4fb8074) /
17137        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
17138        // composition pins on the M3 `:politicas` / `:placement` /
17139        // `:entrada` outer-`Option<&Composite>` arms — extended here to
17140        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
17141        // closing the aplicacao-view composer's routing invariant on
17142        // the composite-slice input.
17143        use crate::aplicacao::Membro;
17144        let c = caixa_aplicacao_with_membros(vec![
17145            Membro {
17146                caixa: "cart".into(),
17147                versao: "^0.1".into(),
17148            },
17149            Membro {
17150                caixa: "pricing".into(),
17151                versao: "^0.2".into(),
17152            },
17153        ]);
17154        let view = c
17155            .aplicacao_view()
17156            .expect("Aplicacao kind must produce an aplicacao_view");
17157        assert_eq!(
17158            view.membros(),
17159            c.membros(),
17160            "aplicacao_view must fold Caixa::membros verbatim into \
17161             AplicacaoSpec::membros — the accessor and the view \
17162             composer must route through the same substrate-primitive \
17163             typed dispatch on the outer :membros slice (got view \
17164             membros={:?}, expected {:?})",
17165            view.membros(),
17166            c.membros(),
17167        );
17168    }
17169
17170    #[test]
17171    fn membros_projects_slice_by_borrow() {
17172        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
17173        // borrow — the returned slice borrows the underlying
17174        // `Vec<Membro>` storage of the `:membros` slot and the
17175        // accessor must not clone the backing `Vec` on every call.
17176        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
17177        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
17178        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
17179        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17180        // `exe_projects_slice_by_borrow` 65d9527,
17181        // `servicos_projects_slice_by_borrow` 611f78b,
17182        // `deps_projects_slice_by_borrow` ad34b4e,
17183        // `deps_dev_projects_slice_by_borrow` f7fd81e,
17184        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
17185        // `children_projects_slice_by_borrow` c17b51e) on the sibling
17186        // outer top-level [`Caixa`] scalar-element and composite-
17187        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
17188        // slot composite-element `&[Composite]` axis: the accessor's
17189        // returned slice must borrow from `&self` (the returned
17190        // reference's lifetime is tied to `&self`), and calling the
17191        // accessor twice on the same [`Caixa`] must yield slices that
17192        // are pointer-equal (the underlying byte-buffer is the storage
17193        // `Vec`'s allocation, not a fresh copy) as well as value-equal
17194        // (idempotent, no side effects on `&self`).
17195        //
17196        // Pins against a future silent detour that returned an owned
17197        // `Vec<Membro>` (which would type-check but silently clone on
17198        // every call), a `&Vec<Membro>` return (which would leak the
17199        // backing `Vec`'s grow/push/reserve surface no downstream
17200        // consumer reaches for), or a one-arm-only accessor that
17201        // returned a saturating value on some sentinel input.
17202        use crate::aplicacao::Membro;
17203        for membros in [
17204            vec![],
17205            vec![Membro {
17206                caixa: "cart".into(),
17207                versao: "^0.1".into(),
17208            }],
17209            vec![
17210                Membro {
17211                    caixa: "cart".into(),
17212                    versao: "^0.1".into(),
17213                },
17214                Membro {
17215                    caixa: "pricing".into(),
17216                    versao: "^0.2".into(),
17217                },
17218            ],
17219        ] {
17220            let c = caixa_aplicacao_with_membros(membros.clone());
17221            let first = c.membros();
17222            let second = c.membros();
17223            assert_eq!(
17224                first, second,
17225                "Caixa::membros must be idempotent — two successive \
17226                 calls on the same &self must return the same &[Membro]",
17227            );
17228            assert_eq!(
17229                first.as_ptr(),
17230                second.as_ptr(),
17231                "Caixa::membros must borrow the underlying Vec<Membro> \
17232                 storage — two successive calls must return slices with \
17233                 the same backing pointer (a fresh Vec<Membro> clone \
17234                 would change the pointer on every call)",
17235            );
17236            assert_eq!(
17237                first,
17238                membros.as_slice(),
17239                "Caixa::membros must return :membros verbatim by borrow \
17240                 — got {first:?}, expected {membros:?}",
17241            );
17242        }
17243    }
17244
17245    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
17246
17247    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
17248        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17249        c.kind = CaixaKind::Aplicacao;
17250        c.contratos = contratos;
17251        c
17252    }
17253
17254    fn contrato_http_for_test(
17255        de: &str,
17256        para: &str,
17257        endpoint: &str,
17258    ) -> crate::aplicacao::WitContract {
17259        crate::aplicacao::WitContract {
17260            de: de.into(),
17261            para: para.into(),
17262            wit: "wasi:http/proxy".into(),
17263            endpoint: Some(endpoint.into()),
17264            subject: None,
17265            slot: None,
17266        }
17267    }
17268
17269    #[test]
17270    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
17271        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
17272        // composite `&[WitContract]`-return slice-shape pin:
17273        // [`Caixa::contratos`] must return the `:contratos` typed
17274        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
17275        // over the same backing buffer the raw
17276        // `self.contratos.as_slice()` field access borrows from,
17277        // element-equal across every representative fixture in the
17278        // accept-set — `[]` (the "no contracts declared" arm every
17279        // non-`Aplicacao`-kind `defcaixa` carries by
17280        // `#[serde(default)]` and every leaf-Aplicacao with a single
17281        // member carries), a canonical single-edge fixture (the
17282        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
17283        // edge), and a canonical multi-edge fixture with three distinct
17284        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
17285        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
17286        //
17287        // Pins against a future silent detour that returned an owned
17288        // `Vec<WitContract>` (which would type-check but silently clone
17289        // on every accessor call, breaking the zero-cost projection
17290        // every peer sibling slice accessor carries), an axis-shuffled
17291        // projection (a future detour that reordered edges through the
17292        // accessor would silently split the paired
17293        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
17294        // traversal input from the peer [`Self::aplicacao_view`] fold-
17295        // in path's clone-order input, since every canonical
17296        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
17297        // seed dispatch reads the edge set through the same slice),
17298        // or a reference to an operator-resolved overlay (the future
17299        // per-cluster `:contratos-overrides` slot — its resolution
17300        // must land at exactly this accessor body, not silently divert
17301        // the raw slot away from a second consumer).
17302        //
17303        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
17304        // accessor pin on the substrate primitive for M2 / M3 typed-
17305        // slot vec-carry axes — closes the outer-`Caixa`
17306        // `&[Composite]` composite-slice sub-family the sibling M2
17307        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
17308        // (2a1f907) and
17309        // `children_returns_children_slice_verbatim_across_permutations`
17310        // (c17b51e) pins opened and the M3
17311        // `membros_returns_membros_slice_verbatim_across_permutations`
17312        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
17313        // slot arm of the composite-slice sub-family. Peer at the outer
17314        // altitude of the closed inner-
17315        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
17316        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
17317        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
17318            vec![],
17319            vec![contrato_http_for_test("cart", "catalog", "/items")],
17320            vec![
17321                contrato_http_for_test("cart", "catalog", "/items"),
17322                contrato_http_for_test("cart", "pricing", "/price"),
17323                contrato_http_for_test("cart", "auth", "/whoami"),
17324            ],
17325        ];
17326        for contratos in fixtures {
17327            let c = caixa_aplicacao_with_contratos(contratos.clone());
17328            assert_eq!(
17329                c.contratos(),
17330                contratos.as_slice(),
17331                "Caixa::contratos must return :contratos verbatim \
17332                 (got {:?}, expected {contratos:?})",
17333                c.contratos(),
17334            );
17335            assert_eq!(
17336                c.contratos(),
17337                c.contratos.as_slice(),
17338                "Caixa::contratos must element-equal the raw \
17339                 `self.contratos.as_slice()` field access across every \
17340                 value in the Vec<WitContract> accept-set",
17341            );
17342            assert_eq!(
17343                c.contratos().is_empty(),
17344                c.contratos.is_empty(),
17345                "Caixa::contratos().is_empty() must byte-equal \
17346                 self.contratos.is_empty() — a presence-bit drift would \
17347                 silently split the paired Caixa::declared_mesh_slots \
17348                 mesh declared-slot enumerator's presence probe from \
17349                 the peer Caixa::aplicacao_view typed-view composer's \
17350                 fold-in path",
17351            );
17352        }
17353    }
17354
17355    #[test]
17356    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
17357        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
17358        // presence-probe arm must key off [`Caixa::contratos`], not the
17359        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
17360        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
17361        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
17362        // presence bit is non-empty, so the mesh kind-coherence gate
17363        // must surface the slot as "declared"), and a `Caixa {
17364        // contratos: vec![], .. }` must NOT push the label (the "author
17365        // omitted the slot entirely" arm — the empty-slice partition
17366        // the serde-default folds onto). The pair jointly pins the
17367        // accessor + declared-slot enumerator composition: any future
17368        // silent detour that had the accessor collapse
17369        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
17370        // "__reserved__")` projection) would silently absorb the
17371        // "declared but degenerate" arm at the accessor boundary and
17372        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17373        // coherence gate would silently accept a struct-literal
17374        // `Caixa` carrying the drift.
17375        //
17376        // Peer of the sibling
17377        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
17378        // (2a1f907),
17379        // `declared_supervisor_slots_children_arm_routes_through_accessor`
17380        // (c17b51e), and
17381        // `declared_mesh_slots_membros_arm_routes_through_accessor`
17382        // (0f26987) composition pins on the M2 `:upgrade-from` /
17383        // `:children` / M3 `:membros` composite-slice arms — same "the
17384        // enumerator gate must route through the substrate-primitive
17385        // typed dispatch" discipline extended onto the M3 `:contratos`
17386        // composite-slice arm, closing the M3 mesh-slot arm of the
17387        // declared-slot enumerator's routing invariant on the
17388        // composite-slice inputs.
17389        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
17390            "cart", "catalog", "/items",
17391        )]);
17392        let slots = c.declared_mesh_slots();
17393        assert!(
17394            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
17395            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
17396             `:contratos` is non-empty — the accessor and the enumerator \
17397             gate must route through the same substrate-primitive \
17398             typed dispatch on the outer :contratos presence bit (got \
17399             slots={slots:?})",
17400        );
17401        let c = caixa_aplicacao_with_contratos(vec![]);
17402        let slots = c.declared_mesh_slots();
17403        assert!(
17404            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
17405            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
17406             when `:contratos` is empty — the author-omitted arm must \
17407             route through the accessor's empty-slice return unchanged \
17408             (got slots={slots:?})",
17409        );
17410    }
17411
17412    #[test]
17413    fn aplicacao_view_contratos_arm_routes_through_accessor() {
17414        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
17415        // fold-in arm must key off [`Caixa::contratos`], not the raw
17416        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
17417        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
17418        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
17419        // per-edge list through the accessor into the typed
17420        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
17421        // every entry the accessor surfaces must land in the view's
17422        // `contratos` slot in the same order. The pair jointly pins
17423        // the accessor + view-composer composition: a future silent
17424        // detour that had the accessor shuffle or drop an edge would
17425        // silently split the paired declared-slot enumerator's
17426        // presence bit from the typed-view composer's edge-list, a
17427        // two-consumer split at the enumerator and the view composer
17428        // far from the source `caixa.lisp`.
17429        //
17430        // Peer of the sibling
17431        // `aplicacao_view_membros_arm_routes_through_accessor`
17432        // (0f26987) composition pin on the M3 `:membros` outer-
17433        // `&[Composite]` composite-slice arm, closing the aplicacao-
17434        // view composer's routing invariant on the composite-slice
17435        // inputs at the outer altitude.
17436        let c = caixa_aplicacao_with_contratos(vec![
17437            contrato_http_for_test("cart", "catalog", "/items"),
17438            contrato_http_for_test("cart", "pricing", "/price"),
17439        ]);
17440        let view = c
17441            .aplicacao_view()
17442            .expect("Aplicacao kind must produce an aplicacao_view");
17443        assert_eq!(
17444            view.contratos(),
17445            c.contratos(),
17446            "aplicacao_view must fold Caixa::contratos verbatim into \
17447             AplicacaoSpec::contratos — the accessor and the view \
17448             composer must route through the same substrate-primitive \
17449             typed dispatch on the outer :contratos slice (got view \
17450             contratos={:?}, expected {:?})",
17451            view.contratos(),
17452            c.contratos(),
17453        );
17454    }
17455
17456    #[test]
17457    fn contratos_projects_slice_by_borrow() {
17458        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
17459        // by borrow — the returned slice borrows the underlying
17460        // `Vec<WitContract>` storage of the `:contratos` slot and the
17461        // accessor must not clone the backing `Vec` on every call.
17462        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
17463        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
17464        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
17465        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17466        // `exe_projects_slice_by_borrow` 65d9527,
17467        // `servicos_projects_slice_by_borrow` 611f78b,
17468        // `deps_projects_slice_by_borrow` ad34b4e,
17469        // `deps_dev_projects_slice_by_borrow` f7fd81e,
17470        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
17471        // `children_projects_slice_by_borrow` c17b51e,
17472        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
17473        // outer top-level [`Caixa`] scalar-element and composite-
17474        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
17475        // composite-element `&[Composite]` axis on the by-borrow pin:
17476        // the accessor's returned slice must borrow from `&self` (the
17477        // returned reference's lifetime is tied to `&self`), and
17478        // calling the accessor twice on the same [`Caixa`] must yield
17479        // slices that are pointer-equal (the underlying byte-buffer is
17480        // the storage `Vec`'s allocation, not a fresh copy) as well as
17481        // value-equal (idempotent, no side effects on `&self`).
17482        //
17483        // Pins against a future silent detour that returned an owned
17484        // `Vec<WitContract>` (which would type-check but silently clone
17485        // on every call), a `&Vec<WitContract>` return (which would
17486        // leak the backing `Vec`'s grow/push/reserve surface no
17487        // downstream consumer reaches for), or a one-arm-only accessor
17488        // that returned a saturating value on some sentinel input.
17489        for contratos in [
17490            vec![],
17491            vec![contrato_http_for_test("cart", "catalog", "/items")],
17492            vec![
17493                contrato_http_for_test("cart", "catalog", "/items"),
17494                contrato_http_for_test("cart", "pricing", "/price"),
17495            ],
17496        ] {
17497            let c = caixa_aplicacao_with_contratos(contratos.clone());
17498            let first = c.contratos();
17499            let second = c.contratos();
17500            assert_eq!(
17501                first, second,
17502                "Caixa::contratos must be idempotent — two successive \
17503                 calls on the same &self must return the same \
17504                 &[WitContract]",
17505            );
17506            assert_eq!(
17507                first.as_ptr(),
17508                second.as_ptr(),
17509                "Caixa::contratos must borrow the underlying \
17510                 Vec<WitContract> storage — two successive calls must \
17511                 return slices with the same backing pointer (a fresh \
17512                 Vec<WitContract> clone would change the pointer on \
17513                 every call)",
17514            );
17515            assert_eq!(
17516                first,
17517                contratos.as_slice(),
17518                "Caixa::contratos must return :contratos verbatim by \
17519                 borrow — got {first:?}, expected {contratos:?}",
17520            );
17521        }
17522    }
17523
17524    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
17525
17526    #[test]
17527    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
17528        // Load-bearing invariant: every multi-word top-level [`Caixa`]
17529        // serde-derived JSON key routes through a lifted `&'static str`
17530        // const. The Rust field names are `snake_case`
17531        // (`deps_dev` / `upgrade_from` / `max_restarts` /
17532        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
17533        // "camelCase")]` derive attribute maps each to the camelCase
17534        // byte-string the [`Caixa::to_lisp`] round-trip's
17535        // `serde_json::to_value(self)` step lands under before
17536        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
17537        // to the kebab-case `:deps-dev` / `:upgrade-from` /
17538        // `:max-restarts` / `:restart-window` author surface. Serialize
17539        // a fully-populated [`Caixa`] and pin that each canonical
17540        // byte-sequence appears verbatim in the JSON — a future
17541        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
17542        // verbatim-field-name flip at the derive attribute (any of
17543        // which would silently break every [`Caixa::to_lisp`]
17544        // round-trip and the future M4 operator-side manifest ingest's
17545        // `Value::get(<key>)` navigation) surfaces here as a build-time
17546        // test failure at `manifest.rs`, not as an apply-time
17547        // `.get(<stale-canonical-const>)` returning `None` far from the
17548        // derive-attr drift's commit. Same discipline the sibling
17549        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
17550        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
17551        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
17552        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
17553        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
17554        // [`UpgradeFromEntry`] per-entry axes — extended here to the
17555        // enclosing M0 [`Caixa`] top-level axis so the last of the four
17556        // multi-word top-level [`Caixa`] serde-derived JSON keys
17557        // (`depsDev`) joins the substrate's "one canonical byte-string
17558        // per typed serialized-key axis" discipline.
17559        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17560        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17561        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17562        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
17563        c.upgrade_from = vec![UpgradeFromEntry {
17564            from: "0.0.1".into(),
17565            instructions: vec![UpgradeInstruction::Restart],
17566        }];
17567        c.estrategia = Some(RestartStrategy::OneForOne);
17568        c.max_restarts = Some(3);
17569        c.restart_window = Some("60s".into());
17570        c.children = vec![ChildSpec {
17571            caixa: "child".into(),
17572            versao: "^0.1".into(),
17573            restart: RestartPolicy::Permanent,
17574        }];
17575        let json = serde_json::to_string(&c).unwrap();
17576        for key in [
17577            crate::render::CAIXA_KEY_DEPS_DEV,
17578            crate::render::M2_KEY_UPGRADE_FROM,
17579            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
17580            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
17581        ] {
17582            let quoted = format!("\"{key}\"");
17583            assert!(
17584                json.contains(&quoted),
17585                "serialized Caixa must carry the lifted top-level \
17586                 multi-word byte-sequence {quoted} verbatim in the JSON \
17587                 emission (got: {json})",
17588            );
17589        }
17590    }
17591
17592    #[test]
17593    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
17594        // Cross-axis drift-detection pin: a future collapse of the four
17595        // canonical [`Caixa`] top-level multi-word byte-strings onto the
17596        // same value (e.g. an accidental copy-paste flip of
17597        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
17598        // `"upgradeFrom"`) would silently reroute every downstream
17599        // `Value::get(<key>)` probe on one axis onto the sibling axis's
17600        // top-level entry and pass every propagation-probe test that
17601        // expected only the stale axis's value. Peer of the sibling
17602        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
17603        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
17604        let all = [
17605            crate::render::CAIXA_KEY_DEPS_DEV,
17606            crate::render::M2_KEY_UPGRADE_FROM,
17607            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
17608            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
17609        ];
17610        for (i, a) in all.iter().enumerate() {
17611            for b in all.iter().skip(i + 1) {
17612                assert_ne!(
17613                    a, b,
17614                    "Caixa top-level multi-word key consts must be \
17615                     pairwise-distinct canonical byte-sequences — got \
17616                     `{a}` == `{b}`",
17617                );
17618            }
17619        }
17620    }
17621
17622    #[test]
17623    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
17624        // Shape-pin: every [`Caixa`] top-level multi-word key const must
17625        // be a lowerCamelCase byte-sequence (no `snake_case`
17626        // underscores, no `kebab-case` hyphens, no leading colon, no
17627        // `PascalCase` leading capital, no whitespace / dots) — the
17628        // canonical shape the `#[serde(rename_all = "camelCase")]`
17629        // derive produces on [`Caixa`]. A future flip to a
17630        // non-camelCase attribute at the derive surfaces both here
17631        // (this test fails on the stale-constant shape) and at
17632        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
17633        // (that test fails on the mismatch between const and derive).
17634        // Peer with `membro_key_consts_are_lower_camel_case_shape`
17635        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
17636        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
17637        for key in [
17638            crate::render::CAIXA_KEY_DEPS_DEV,
17639            crate::render::M2_KEY_UPGRADE_FROM,
17640            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
17641            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
17642        ] {
17643            assert!(
17644                !key.is_empty(),
17645                "Caixa top-level multi-word key const must be non-empty \
17646                 (got {key:?})"
17647            );
17648            let first = key.chars().next().unwrap();
17649            assert!(
17650                first.is_ascii_lowercase(),
17651                "Caixa top-level multi-word key const must lead with an \
17652                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
17653            );
17654            assert!(
17655                key.chars().all(|c| c.is_ascii_alphanumeric()),
17656                "Caixa top-level multi-word key const must be \
17657                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
17658                 whitespace (got {key:?})",
17659            );
17660        }
17661    }
17662
17663    #[test]
17664    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
17665        // Scalar-value pin: the byte-string the
17666        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
17667        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
17668        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
17669        // → `depsTest` matching a hypothetical per-test-target
17670        // vocabulary flip) lands as an edit to exactly one const AND
17671        // one derive attribute — the sibling
17672        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
17673        // pin already ties the const to the derive attribute, so a
17674        // rebrand that touches only one side of the pair fails at
17675        // caixa-core build time. Same "scalar-value pin per const"
17676        // discipline the sibling
17677        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
17678        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
17679        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
17680        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
17681    }
17682
17683    #[test]
17684    fn caixa_key_deps_pins_canonical_byte_string() {
17685        // Scalar-value pin: the byte-string the
17686        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
17687        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
17688        // on the two-list dep-graph serialized-key axis — the sibling
17689        // pin covers the multi-word `deps_dev → depsDev` camelCase
17690        // arm, this pin covers the single-word `deps → deps` no-op arm
17691        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
17692        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
17693        // axis and the emitted JSON key equals the source-side field
17694        // name byte-for-byte). A future [`crate::Caixa::deps`] field
17695        // rename (`deps` → `dependencies` matching Cargo's verbatim
17696        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
17697        // hypothetical per-runtime-target vocabulary flip) OR an added
17698        // `#[serde(rename = "…")]` explicit override lands as an edit
17699        // to exactly one const AND one derive-attr / field name — the
17700        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
17701        // pin ties the const to the emitted JSON key, so a rebrand
17702        // that touches only one side of the pair fails at caixa-core
17703        // build time.
17704        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
17705    }
17706
17707    #[test]
17708    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
17709        // Load-bearing invariant on the single-word `deps` top-level
17710        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
17711        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
17712        // `serde_json::to_value(self)` step emits. Serialize a
17713        // populated [`Caixa`] whose `:deps` slot carries at least one
17714        // entry (the `#[serde(default)]` attribute on the field emits
17715        // an empty `[]` even without members, but a non-empty vec
17716        // additionally covers the codec's per-`Dep`-entry emission
17717        // path) and pin that `"deps"` appears verbatim in the JSON
17718        // emission — a future accidental `rename_all = "snake_case"` /
17719        // `"kebab-case"` flip at the derive attribute (or an added
17720        // `#[serde(rename = "…")]` explicit override on the field, or
17721        // a Rust field rename) would break every [`Caixa::to_lisp`]
17722        // round-trip and the future M4 operator-side manifest ingest's
17723        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
17724        // build-time test failure at `manifest.rs`, not as an
17725        // apply-time `.get(<stale-canonical-const>)` returning `None`
17726        // far from the drift's commit. Peer of the sibling
17727        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
17728        // multi-word pin on the same M0 [`Caixa`] top-level
17729        // serialized-key axis, extended here to the single-word arm
17730        // the multi-word test's `rename_all = "camelCase"` sweep can't
17731        // reach (single-word `deps → deps` is a no-op the multi-word
17732        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
17733        // `\"restartWindow\"` byte-scan can never observe).
17734        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17735        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
17736        let json = serde_json::to_string(&c).unwrap();
17737        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
17738        assert!(
17739            json.contains(&quoted),
17740            "serialized Caixa must carry the lifted top-level `deps` \
17741             byte-sequence {quoted} verbatim in the JSON emission (got: \
17742             {json})",
17743        );
17744    }
17745
17746    #[test]
17747    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
17748        // Cross-axis drift-detection pin on the two-list dep-graph
17749        // renderer-side wire-key axis: a future collapse of the
17750        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
17751        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
17752        // same value (e.g. an accidental copy-paste flip of
17753        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
17754        // reroute every downstream `Value::get(<key>)` probe on one
17755        // axis onto the sibling axis's dep-list and pass every
17756        // propagation-probe test that expected only the stale axis's
17757        // value — a dev-only dep would land in the runtime closure at
17758        // publish time, or a runtime dep would be excluded from the
17759        // published lacre. Peer of the sibling four-way distinct pin
17760        // on the top-level multi-word tetrad
17761        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
17762        // and the two-way pin on the sibling
17763        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
17764        // author-facing arm (4da6fba's test), extended here to the
17765        // renderer-side wire-key arm of the same two-list dep-graph
17766        // axis so both halves of the "one canonical byte-string per
17767        // typed axis per (author, wire)" grid carry the same
17768        // distinct-ness discipline.
17769        assert_ne!(
17770            crate::render::CAIXA_KEY_DEPS,
17771            crate::render::CAIXA_KEY_DEPS_DEV,
17772            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
17773             canonical byte-sequences on the two-list dep-graph \
17774             renderer-side wire-key axis"
17775        );
17776    }
17777
17778    // ── DepList / Caixa::push_dep pin ────────────────────────────────
17779    //
17780    // The compounding pin: the two-arm closed-set typed enum
17781    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
17782    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
17783    // consumer of the top-level manifest's dep-mutation surface reads
17784    // through, and the typed dispatch [`Caixa::push_dep`] on the
17785    // substrate primitive folds the "select list → check within-list
17786    // dup → push" cascade onto one method call. Prior to this landing
17787    // the two axes lived across two `&'static str` constants
17788    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
17789    // set type carrying the pair; the `feira add` mutation site's
17790    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
17791    // caixa.deps }` dispatch expressed no compile-time link back to
17792    // the substrate primitive, and a future third dep-list axis would
17793    // have silently split at every open-coded mutation site.
17794
17795    #[test]
17796    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
17797        // Every arm returns the same `&'static str` the substrate's
17798        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
17799        // constants carry. A future rebrand on either constant reaches
17800        // the enum through one edit; a regression to inline literals
17801        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
17802        // quotes from the wire-format constants every consumer routes
17803        // through and this pin flags it at build time.
17804        assert_eq!(
17805            crate::dep::DepList::Prod.as_str(),
17806            crate::render::DEP_AUTHOR_KEY_DEPS
17807        );
17808        assert_eq!(
17809            crate::dep::DepList::Dev.as_str(),
17810            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
17811        );
17812    }
17813
17814    #[test]
17815    fn dep_list_display_routes_through_as_str() {
17816        // Same as-str-through-Display convergence discipline the
17817        // sibling closed-set typed enums carry — a `format!("{list}")`
17818        // call must land byte-for-byte on the accessor's return so a
17819        // future consumer that formats the enum for a diagnostic line
17820        // reaches the same wire-format constant the wire-format
17821        // producers do.
17822        assert_eq!(
17823            format!("{}", crate::dep::DepList::Prod),
17824            crate::dep::DepList::Prod.as_str()
17825        );
17826        assert_eq!(
17827            format!("{}", crate::dep::DepList::Dev),
17828            crate::dep::DepList::Dev.as_str()
17829        );
17830    }
17831
17832    #[test]
17833    fn dep_list_all_enumerates_every_variant_once() {
17834        // Exhaustive-iteration pin — every arm appears exactly once in
17835        // `ALL`, matching the closed set the compiler enforces on the
17836        // sibling `match self` arms. A future variant addition that
17837        // extends only one method's match without extending `ALL`
17838        // would silently drop the new arm from every consumer that
17839        // iterates the slice.
17840        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
17841        assert!(variants.contains(&crate::dep::DepList::Prod));
17842        assert!(variants.contains(&crate::dep::DepList::Dev));
17843        assert_eq!(variants.len(), 2);
17844    }
17845
17846    #[test]
17847    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
17848        // Reverse projection on the two-list dep-graph axis: the
17849        // author-surface wire tag the sibling `as_str` emitter walks
17850        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
17851        // `Some(DepList::Prod)`. A regression that hand-rolled the
17852        // per-arm match without routing through the lifted
17853        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
17854        // future wire-tag rebrand and this pin flags it at build time.
17855        assert_eq!(
17856            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
17857            Some(crate::dep::DepList::Prod)
17858        );
17859    }
17860
17861    #[test]
17862    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
17863        // Peer of the `Prod`-arm pin on the dev-only axis: the
17864        // author-surface wire tag the sibling `as_str` emitter walks
17865        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
17866        // back to `Some(DepList::Dev)`. Same drift-detection posture
17867        // as the peer arm — the sibling method `match` arms are
17868        // compiler-checked exhaustive so a future variant addition
17869        // trips at build time.
17870        assert_eq!(
17871            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
17872            Some(crate::dep::DepList::Dev)
17873        );
17874    }
17875
17876    #[test]
17877    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
17878        // Every input outside the closed-set arm-string set the
17879        // sibling `as_str` emitter walks lands on the terminal `None`
17880        // fallback — no silent-accept surface. Sweeps a set of
17881        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
17882        // rebrand candidates, foreign wire tags, empty string) so a
17883        // future variant addition that widened one wire form without
17884        // extending the emitter's arm-set would trip the sibling
17885        // round-trip pin below rather than silently accepting the new
17886        // form here.
17887        for candidate in [
17888            "",
17889            "deps",
17890            "deps-dev",
17891            ":deps ",
17892            ":Deps",
17893            ":DEPS",
17894            ":build-dep",
17895            ":tool-dep",
17896            "prod",
17897            "dev",
17898        ] {
17899            assert_eq!(
17900                crate::dep::DepList::from_wire(candidate),
17901                None,
17902                "from_wire({candidate:?}) must return None; every input outside \
17903                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
17904                 the sibling as_str emitter walks lands on the terminal fallback",
17905            );
17906        }
17907    }
17908
17909    #[test]
17910    fn dep_list_round_trips_through_as_str_and_from_wire() {
17911        // Load-bearing round-trip pin: every arm the `ALL` iteration
17912        // exposes survives the `as_str` → `from_wire` composition
17913        // byte-for-byte. Same discipline the sibling closed-set enums
17914        // carry — `CaixaKind` /
17915        // `RestartStrategy` / `RestartPolicy` /
17916        // `PlacementStrategy` — extended onto the two-list dep-graph
17917        // axis. A future variant addition that extends `ALL` +
17918        // `as_str` without extending `from_wire` (or vice versa)
17919        // trips at build time on this iteration because the compiler
17920        // enforces exhaustiveness on the sibling `match self` arms.
17921        for &list in crate::dep::DepList::ALL {
17922            assert_eq!(
17923                crate::dep::DepList::from_wire(list.as_str()),
17924                Some(list),
17925                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
17926                 a silent split between the forward emitter and the reverse parser \
17927                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
17928            );
17929        }
17930    }
17931
17932    #[test]
17933    fn push_dep_routes_to_deps_slot_on_prod_arm() {
17934        // The `Prod` arm dispatches to the runtime-closure `:deps`
17935        // slot every downstream lacre-pipeline consumer resolves at
17936        // build time. A future arm that regressed to inline `&mut
17937        // self.deps_dev` on the `Prod` path would silently reroute
17938        // every runtime dep into the dev-only closure at publish time
17939        // — this pin refuses that regression.
17940        let src = Caixa::template("host");
17941        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17942        let before_deps = caixa.deps().len();
17943        let before_deps_dev = caixa.deps_dev().len();
17944        let dep = Dep {
17945            nome: "caixa-teia".to_string(),
17946            versao: "^0.1".to_string(),
17947            fonte: None,
17948            opcional: false,
17949            caracteristicas: Vec::new(),
17950        };
17951        caixa
17952            .push_dep(crate::dep::DepList::Prod, dep)
17953            .expect("first push into :deps succeeds");
17954        assert_eq!(caixa.deps().len(), before_deps + 1);
17955        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
17956        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
17957    }
17958
17959    #[test]
17960    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
17961        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
17962        // must dispatch to the dev-only-closure `:deps-dev` slot every
17963        // downstream test-facing artifact resolver reads. A future
17964        // regression that inverted the two arms would silently route
17965        // every dev-only dep into the runtime closure at publish time
17966        // and this pin catches it before the drift ships.
17967        let src = Caixa::template("host");
17968        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17969        let dep = Dep {
17970            nome: "tatara-check".to_string(),
17971            versao: "*".to_string(),
17972            fonte: None,
17973            opcional: false,
17974            caracteristicas: Vec::new(),
17975        };
17976        caixa
17977            .push_dep(crate::dep::DepList::Dev, dep)
17978            .expect("first push into :deps-dev succeeds");
17979        assert!(caixa.deps().is_empty());
17980        assert_eq!(caixa.deps_dev().len(), 1);
17981        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
17982    }
17983
17984    #[test]
17985    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
17986        // Within-list dup check routes through the canonical
17987        // [`DepError::DuplicateNome`] carrier — the substrate's typed
17988        // diagnostic for the same axis [`Caixa::validate_deps`]'s
17989        // parse-time [`crate::render::insert_first_seen`] walk raises
17990        // on. Prior to the lift the mutation site's inline
17991        // `bail!("dep '{}' already declared", …)` string-diagnostic
17992        // path expressed no through-line back to the typed error;
17993        // routing every dep-list refusal through one carrier means an
17994        // author reading a `feira add` refusal and a `feira build`
17995        // refusal reaches for the same corrective surface without
17996        // switching diagnostic idioms.
17997        let src = Caixa::template("host");
17998        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17999        let dep = Dep {
18000            nome: "caixa-teia".to_string(),
18001            versao: "^0.1".to_string(),
18002            fonte: None,
18003            opcional: false,
18004            caracteristicas: Vec::new(),
18005        };
18006        caixa
18007            .push_dep(crate::dep::DepList::Prod, dep.clone())
18008            .expect("first push succeeds");
18009        let dup = Dep {
18010            nome: "caixa-teia".to_string(),
18011            versao: "^0.2".to_string(),
18012            fonte: None,
18013            opcional: false,
18014            caracteristicas: Vec::new(),
18015        };
18016        let err = caixa
18017            .push_dep(crate::dep::DepList::Prod, dup)
18018            .expect_err("second push with same :nome refuses");
18019        assert_eq!(
18020            err,
18021            DepError::DuplicateNome {
18022                nome: "caixa-teia".to_string(),
18023                list: crate::render::DEP_AUTHOR_KEY_DEPS,
18024            }
18025        );
18026        // The refused mutation must not corrupt the target list —
18027        // exactly one entry lives past the refusal, matching the
18028        // canonical single-source-of-truth invariant `Caixa::deps()`
18029        // carries.
18030        assert_eq!(caixa.deps().len(), 1);
18031    }
18032
18033    #[test]
18034    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
18035        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
18036        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
18037        // `list` payload so a future author reading the refusal grep's
18038        // for the correct `:deps-dev` block in their `caixa.lisp`,
18039        // not the sibling `:deps` block the runtime closure resolves.
18040        let src = Caixa::template("host");
18041        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18042        let dep = Dep {
18043            nome: "tatara-check".to_string(),
18044            versao: "*".to_string(),
18045            fonte: None,
18046            opcional: false,
18047            caracteristicas: Vec::new(),
18048        };
18049        caixa
18050            .push_dep(crate::dep::DepList::Dev, dep.clone())
18051            .expect("first push succeeds");
18052        let err = caixa
18053            .push_dep(crate::dep::DepList::Dev, dep)
18054            .expect_err("second push with same :nome refuses");
18055        assert!(matches!(
18056            err,
18057            DepError::DuplicateNome {
18058                ref nome,
18059                list,
18060            } if nome == "tatara-check"
18061                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
18062        ));
18063    }
18064
18065    #[test]
18066    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
18067        // The within-list dup check is scoped to the target arm — a
18068        // caixa may legitimately carry the same `:nome` under both
18069        // `:deps` and `:deps-dev` (though the substrate's peer
18070        // [`crate::Caixa::validate_deps`] walk still refuses the
18071        // shape at parse time; the mutation-site refusal is scoped to
18072        // the mutation-site's list to match the peer parse-time
18073        // per-list [`crate::render::insert_first_seen`] discipline).
18074        // The two arms hold independent seen-sets.
18075        let src = Caixa::template("host");
18076        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18077        let dep_prod = Dep {
18078            nome: "shared".to_string(),
18079            versao: "^0.1".to_string(),
18080            fonte: None,
18081            opcional: false,
18082            caracteristicas: Vec::new(),
18083        };
18084        let dep_dev = Dep {
18085            nome: "shared".to_string(),
18086            versao: "*".to_string(),
18087            fonte: None,
18088            opcional: false,
18089            caracteristicas: Vec::new(),
18090        };
18091        caixa
18092            .push_dep(crate::dep::DepList::Prod, dep_prod)
18093            .expect("push into :deps succeeds");
18094        caixa
18095            .push_dep(crate::dep::DepList::Dev, dep_dev)
18096            .expect("push same :nome into :deps-dev succeeds");
18097        assert_eq!(caixa.deps().len(), 1);
18098        assert_eq!(caixa.deps_dev().len(), 1);
18099    }
18100
18101    #[test]
18102    fn deps_of_prod_returns_the_deps_slot_verbatim() {
18103        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
18104        // accessor must project onto the runtime-closure `:deps` slot —
18105        // element-equal and length-equal to the sibling per-slot
18106        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
18107        // A future arm that regressed to `self.deps_dev()` on the `Prod`
18108        // path would silently reroute every downstream typed-dispatch
18109        // walker (the [`Caixa::validate_deps`] per-list
18110        // [`crate::render::insert_first_seen`] dedup walk, any future
18111        // per-axis-parametrised consumer) into the sibling dev-only
18112        // closure and this pin refuses that regression.
18113        let src = Caixa::template("host");
18114        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18115        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
18116        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
18117        let dep = Dep {
18118            nome: "caixa-teia".to_string(),
18119            versao: "^0.1".to_string(),
18120            fonte: None,
18121            opcional: false,
18122            caracteristicas: Vec::new(),
18123        };
18124        caixa
18125            .push_dep(crate::dep::DepList::Prod, dep.clone())
18126            .expect("push into :deps succeeds");
18127        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
18128        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
18129        assert_eq!(
18130            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
18131            "caixa-teia"
18132        );
18133    }
18134
18135    #[test]
18136    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
18137        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
18138        // [`Caixa::deps_of`] must project onto the dev-only-closure
18139        // `:deps-dev` slot, element-equal and length-equal to the
18140        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
18141        // future regression that inverted the two arms would silently
18142        // route every dev-list walker onto the runtime closure and this
18143        // pin catches it before the drift ships.
18144        let src = Caixa::template("host");
18145        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18146        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
18147        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
18148        let dep = Dep {
18149            nome: "tatara-check".to_string(),
18150            versao: "*".to_string(),
18151            fonte: None,
18152            opcional: false,
18153            caracteristicas: Vec::new(),
18154        };
18155        caixa
18156            .push_dep(crate::dep::DepList::Dev, dep)
18157            .expect("push into :deps-dev succeeds");
18158        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
18159        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
18160        assert_eq!(
18161            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
18162            "tatara-check"
18163        );
18164    }
18165
18166    #[test]
18167    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
18168        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
18169        // [`Caixa::deps_of`] must land on the same two-slot partition the
18170        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
18171        // expose — the canonical dispatch a future per-axis-parametrised
18172        // walker (a future `feira app graph` per-list dep summary, a
18173        // future M4 per-cluster dev-closure-audit overlay the CR
18174        // materializer resolves per-CR) reads through. Prior to the
18175        // lift the two-block iteration lived open-coded at every walker,
18176        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
18177        // §I) would have had to grow a third block at every consumer.
18178        // A regression that dropped the `Dev` arm from `ALL` would flip
18179        // the collected pairs to `[(":deps", &[])]` alone and this pin
18180        // refuses that shape.
18181        let src = Caixa::template("host");
18182        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18183        let prod_dep = Dep {
18184            nome: "caixa-teia".to_string(),
18185            versao: "^0.1".to_string(),
18186            fonte: None,
18187            opcional: false,
18188            caracteristicas: Vec::new(),
18189        };
18190        let dev_dep = Dep {
18191            nome: "tatara-check".to_string(),
18192            versao: "*".to_string(),
18193            fonte: None,
18194            opcional: false,
18195            caracteristicas: Vec::new(),
18196        };
18197        caixa
18198            .push_dep(crate::dep::DepList::Prod, prod_dep)
18199            .expect("push into :deps succeeds");
18200        caixa
18201            .push_dep(crate::dep::DepList::Dev, dev_dep)
18202            .expect("push into :deps-dev succeeds");
18203        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
18204            .iter()
18205            .map(|&list| {
18206                let slice = caixa.deps_of(list);
18207                (list.as_str(), slice.len(), slice[0].nome())
18208            })
18209            .collect();
18210        assert_eq!(
18211            collected,
18212            vec![
18213                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
18214                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
18215            ]
18216        );
18217    }
18218
18219    #[test]
18220    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
18221        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
18222        // must route its per-list [`crate::render::insert_first_seen`]
18223        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
18224        // rather than the pre-lift open-coded two-block iteration over
18225        // `self.deps()` + `self.deps_dev()`. A regression that dropped
18226        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
18227        // stop refusing within-list dups on the sibling arm; a
18228        // regression that flipped the arm-to-list-key mapping
18229        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
18230        // diagnostic surface. Both drifts surface here through a paired
18231        // duplicate-name refusal per arm plus an offending-list-key
18232        // check on the emitted [`DepError::DuplicateNome`] carrier.
18233        for &list in crate::dep::DepList::ALL {
18234            let src = Caixa::template("host");
18235            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18236            let dup = Dep {
18237                nome: "twin".to_string(),
18238                versao: "^0.1".to_string(),
18239                fonte: None,
18240                opcional: false,
18241                caracteristicas: Vec::new(),
18242            };
18243            match list {
18244                crate::dep::DepList::Prod => {
18245                    caixa.deps.push(dup.clone());
18246                    caixa.deps.push(dup);
18247                }
18248                crate::dep::DepList::Dev => {
18249                    caixa.deps_dev.push(dup.clone());
18250                    caixa.deps_dev.push(dup);
18251                }
18252            }
18253            let err = caixa
18254                .validate_deps()
18255                .expect_err("within-list duplicate :nome must refuse");
18256            assert_eq!(
18257                err,
18258                DepError::DuplicateNome {
18259                    nome: "twin".to_string(),
18260                    list: list.as_str(),
18261                },
18262                "validate_deps on {list} arm must emit \
18263                 DepError::DuplicateNome carrying the arm's own \
18264                 as_str() diagnostic — the arm-to-list-key mapping \
18265                 flowed through DepList::ALL + Caixa::deps_of"
18266            );
18267        }
18268    }
18269}